在C++中,有一种神奇的变量,它不能够表示一个值,可是能够表示某个元素的地址,经过地址来访问这个元素。数组
打个比方:你有一张地图和一个坐标,你就能够经过访问这个坐标来达到你访问坐标所表示的元素的目的。指针变量就是这个“坐标”。函数
下面咱们来具体看看指针变量的应用。大数据
正如上面所说,指针变量不能够表示一个值,可是能够指向别的元素的地址,经过这个地址来间接访问这个元素的值。优化
因为它的性质,指针变量不能够直接=一个元素,赋值时要注意。spa
具体操做下面会讲到。指针
如何声明一个指针变量? 有以下表达式:code
数据类型+“*”+指针名blog
一般咱们这样赋值:内存
int main() { int *p=NULL; return 0; }
这样咱们就定义了一个指针类型的变量p,NULL是空内存,这个内存里什么元素都没有,你能够以后再给p赋一个元素的地址。(能够不用=NULL,可是这是我的习惯,相似于return 0好习惯这种……)io
这个语句的意义是:定义一个int类型的指针p,指向空地址。
那么怎么把一个元素的地址赋给一个指针变量呢?
有以下语句:
#include<cstdio> using namespace std; int main() { int a; int *p=NULL; p=&a; return 0; } /*int main() { int a; int *p=&a; return 0; }*/
上面两个主函数的效果是同样的。
咱们说说这两段代码的意义:
相信你们都用过scanf( ),在咱们输入变量名前要加一个符号“&”,这就是地址符,表示变量名的地址。
咱们的指针要指向一个地址,固然就是:指针名=&变量名啦!
既然咱们会赋值了,下一步就是调用元素值,可是指针指向的是一个地址,不能直接参与运算,这时候咱们要用到间接运算符“*”。(就是定义的时候那个星号)
若是咱们有一个元素a,须要用指针来输出它,怎么操做?
对于这个问题,有以下程序:
#include<cstdio> using namespace std; int main() { int a; scanf("%d",&a); int *p=&a;//定义一个指针变量p指向元素a printf("%d",*p);//间接运算符+指针名表示指针所指元素 return 0; }
代码注释已经很详尽了,咱们的指针指向一个元素,用“*”+指针名便可访问指针所指元素
注意:经过指针操做元素值和直接操做元素值是同样的,好比下面这段代码:
#include<cstdio> using namespace std; int main() { int a,b,s,t,*pa=NULL,*pb=NULL; pa=&a,pb=&b; a=10,b=20; s=*pa+*pb; t=*pa**pb; printf("%d %d",s,t); return 0; }
程序给出的结果是30 200。
首先咱们给出一个基本的定义:
当咱们定义数组的时候,系统会给出连续的地址,好比a[5],假设a[0]的地址是0,那么a[1]的地址就是1……以此类推。
此时,咱们直接把地址+1(指针+1),就能够访问数组的下一个元素。
#include<cstdio> using namespace std; int main() { int a[5],*p=&a[0]; for(int i=0;i<5;i++) scanf("%d",&a[i]); for(int i=0;i<5;i++) { printf("%d ",*p); p++; } return 0; }
对于p--,同理。这个语句输出了a[ ]中的全部变量。
指向数组的指针叫数组指针,众所周知,一个数组的地址是连续的,首地址就是他所占有的几个单元的首地址,咱们能够把数组名赋给指针,也能够把数组中某个元素的地址赋给它。
有如下语句:
int a[5],*p=a;
则如下三个语句
&a[0],a,*p,均指向同一个单元——数组的首地址。
那么能够推导:&a[i]、a+i、p+i,均指向数组a中a[i]的地址。
有以下代码:
#include<cstdio> using namespace std; int main() { int a[5],*pa=a; for(int i=0;i<5;i++) scanf("%d",&a[i]); for(int i=0;i<5;i++) printf("%d %d %d %d\n",*(pa+i),pa[i],a[i],*(a+i)); return 0; }
咱们输如5个数:1 2 3 4 5
系统给出了5行:
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
这说明上面4个语句:*(pa+i),pa[i],a[i],*(a+i)是等价的。
代码说明和注意事项:
一、a(数组名)能够加“*”变为常量指针,a是开始元素,根据指针的加减原理,a+i是第i个元素的地址。
二、a是常量名,不能直接进行指针的+、-操做(这里指的是p++、p--这种赋值操做非法,可是a+i这种是合法的),可是pa是指针变量,能够进行加减操做。
咱们用以下语句能够申请一个系统空间给指针p:
int *p=new(int);
此时*p的内容不肯定。
这个语句是动态数组的基础。
一、原理:以前说过,若是咱们一次申请多个空间,系统会发给咱们连续的新地址,能够当作数组用。
二、具体操做
有以下代码:
#include<cstdio> using namespace std; int main() { int n,*p; scanf("%d",&n); p=new int[n+1];//申请连续的n+1个空间给指针p for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=n;i++) printf("%d ",p[i]); return 0; }
若是咱们输入:
5
1 2 3 4 5
系统给出
1 2 3 4 5
上面的代码你能够理解有一个数组,数组名就是指针名,其他操做和第5个板块中提到的同样。(经过数组名+下标访问)
咱们还能够改为这个样子:
#include<cstdio> using namespace std; int main() { int n,*p; scanf("%d",&n); p=new int[n+1];//申请连续的n+1个空间给指针a for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=n;i++) { p++;//因为p默认指向第0个元素,因此先++ printf("%d ",*p); } return 0; }
这里使用指针访问而不使用数组名访问,和上面的代码是等价的。固然你也能够写成这样:printf("%d ",*(p+i));在上面提到过,这几种写法是等价的。
前面扯了那么多指针的基本定义和写法,终于到了今天的正题了——利用指针创建动态数组。
咱们给出一个情景:如今有一个巨大(行列<=10000000)可是稀疏(大部分元素是0)的矩阵,咱们要对这个矩阵进行操做,怎么办呢?
显然,这样的代码是绝对行不通的。
#include<cstdio> #define N 10000100 using namespace std; int n[N][N];
若是这么写,你的空间复杂度是绝对过不了的。
咱们要进行优化才行。
记得指针能够申请空间吗?咱们能够利用这个特性,避免存储无效数据(0),咱们为每一次输入的有效数据开一个新的内存单元,这样就不会爆内存啦!
咱们看下面这个例题:
【问题描述】
矩阵能够认为是N*M的二维数组。如今有一个巨大但稀疏的矩阵。
N,M的范围是1<=N,M<=100000,有K个位置有数据,1<=K<=100000。
矩阵输入的方式是从上到下(第1行到第N行),从左到右(从第1列到第M列)扫描,记录有数据的坐标位置(x,y)和值(v)。这是按照行优先的方式保存数据的。
如今要求按照列优先的数据,即从左到右,从上到下扫描,输出有数据的坐标和位置。
【输入格式】
第1行:3个整数N,M,K,其中1<=N,M,K<=100000;下面有K行,每行三个整数:a,b,c,表示第a行第b列有数据c。数据在int范围内,保证是行优先的次序。
【输出格式】
1行,K个整数,是按照列优先次序输出的数
【样例输入】
4 5 9
1 2 12
1 4 23
2 2 56
2 5 78
3 2 100
3 4 56
4 1 73
4 3 34
4 5 55
【样例输出】
73 12 56 100 34 23 56 78 55
【样例解释】
0 | 12 | 0 | 23 | 0 |
0 | 56 | 0 | 0 | 78 |
0 | 100 | 0 | 56 | 0 |
73 | 0 | 34 | 0 | 55 |
对于这个矩阵,咱们能够这样存:
73 | 12 | 34 | 23 | 78 |
—— | 56 | —— | 56 | 55 |
—— | 100 | —— | —— | —— |
—— | —— | —— | —— | —— |
注:标记“------”的都是没有使用的内存,这样咱们就节省了11个内存单元,对于大数据的时候,咱们还能节省更多的内存,保证不会超出空间限制。
这个思路的大致意思就是:忽略x的值,把第y列第一次输入的数据当作第y列的第一个数据,而后是第二个……
下面来看代码实现:
#include<cstdio> using namespace std; const int LP=100001; int n,m,k; int x[LP],y[LP],d[LP],c[LP];//记录数据,记录第n个数据在x行,y列,c是第y列的数据总数。 int *a[LP];//最多有LP列,因此咱们开LP长度的指针数组 int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;i++) { scanf("%d%d%d",&x[i],&y[i],&d[i]);//输入x,y,d c[y[i]]++;//第y[i]列的数据个数++ } for(int i=1;i<=m;i++) a[i]=new int[c[i]];//为每一列申请空间来存数据 for(int i=1;i<=k;i++) { *a[y[i]]=d[i];//收集数据到第y列中 a[y[i]]++;//第y列的指针指向下一个地址,准备下一次收集 } for(int i=1;i<=m;i++)//列优先 { a[i]-=c[i];//由于前面收集数据的时候每一列的指针都指向了该列的最后一个元素,因此要先减去该列的元素数,让它指向第一个元素 for(int j=1;j<=c[i];j++,a[i]++)//从第1列开始输出,j用来统计输出到第i列第几个元素,若是输出到最后一个元素,跳出循环 printf("%d ",*a[i]);//指针每次+1,指向下一个元素并输出它 } return 0; }
a[i]=new int c[[i]];这一句的意思是给a[i]这个指针新申请c[i]个空间,等同于咱们开了LP个一维的指针数组,这些数组每个都有一个专用的指针a[i],每一个数组有c[i]个元素。
到这里,咱们已经讲完了利用指针开动态数组数组的具体作法,这样能够颇有效率的优化你的程序,赶忙用起来吧!!!