数组和指针
我们知道在编程中有个公式:程序=数据+算法,算法以后我们再说,而对于数据应该首先搞清楚他在计算机中如何存储,如何找到他。数组就是一个最好的存储多个数据的一个容器或者集合,我们可以通过索引去找到我们需要的数据,也可以通过指针找到。
在说指针和数组之前我们应该先看看内存。只有本数据调到内存里面才能让CPU更好的读取和处理它。内存是插在主板上的一块带芯片的板子,主要是用来保存数据的。在编程中我们可以把它抽象成由不同的块组成的呈梯子状的一天长板,如图:
他的每一个框就是用来存数数据的。当然每一个框也有自己的编号,我们叫内存地址,也叫物理地址,简称地址。每一个框我们同样可以想象为两个格子,一个存储地址,我们叫他Addr部,另一个存储数据,我们叫他Data部。
我们这里说的数组就是一组地址连续的物理块,也就说一段内存。数组的大小就是他拥有的物理块的个数。因为他是连续的,我以我们很快的把它遍历出来。每一个块上Data部所存数的就是我们数组的每项对应的值。而指针是一个特殊的内存块,他的特殊在于两个方面:
1. 只有一个物理块
2. 他的Data存的不是不是值,而是另一个块的Addr
因此指针值的变化就意味着所指向的目标的变化。举个例子,如图:
A代表一个数组,每块虚线上面的是值,下面的是地址,他的地址是连续的。P是一个指针,它指向A的第三块,也就是说P的Data是A[2](数组从0开始计数的)的Addr,所以就有A[2]++为5++=6,而P++就是1003++=1004,那么*P为P所指目标的值就是1004的值6了。&P则为P的地址2443和A没有多大关系。所以一般数组的遍历索引和指针遍历,而指针遍历则必须制定她们的指向关系。还有数组的名称实质也是与数组相关联的指针。
二维数组:
通常所见的数组除了一维数组,还有二维数组,就像我们高数里面学的矩阵。现在就想,数组我们抽象成了一维的线性块,很容易存储一维数组,那么怎么来存储二维数组呢?其实质也很简单,看个例子就明白了:
因为二维数组的每一组的长度是有限的,所以我们完全可以把他们组合成一维数组。至于他的遍历,我们知道二维数组是有行和列区分的,我一我们用行和列就可以遍历,比如A[0][0]=1,
A[2][1]=8。
对于二维数组因其有行列之分,则对于他的指针也就有了行指针和列指针了。行指针指的是二维数组平面结构中的行,他的++是行的++。比如行指针P->A[0][0],则P++->A[1][0].
列指针则就是每行的列了,不过列指针也可以看做二维数组线性存储的一位数组的指针,因为当指针超出列的长度时会自动跳到下一行。他的数组名既是他的行指针,也是首行的列指针。
数组的申明和使用:
说起数组的申明,我们先引深下内存的知识。内存我们在编程的过程中可以看做有三部分组成:静态存储区、堆(Heap)和栈(stack)。这里的栈可不是那个“先进后出”的栈,而是内存中用来存储数据的栈,在以往的面试交流中,有同学容易混淆这些概念。静态区顾名思义就是存储静态数据的地方,也就是用Static申明的数据。栈是内存中存储数据最灵活的地方,当你申明的数据跳出其所在地作用域时会自动回收,减少垃圾,不过他的空间是有限的。堆则是一块比较大的数据存储区域,它上面主要存储我们用New或者Malloc申请的数据,相对于栈,他不会自动回收数据,所以在每次我们用完对上的数据时都要用delete或者free释放他们的空间,以免造成内存泄露。
一维数组的申明:
int a[]={1,2,3}; int b[3]={1,2,3}; int c[5]={1,2,3}; //长度5,其c[3],c[4]为默认0.
他们存在栈上。
double *p = new double[10];
int n = 10; double *p = new double[n]; // n不必是常量
他们一般存在堆上,用完要释放的哦。 另外全局数组存在静态存储区内。
二维数组的申明:
int a1[2][3]={{1,2,3},{4,5,6}};
int a2[2][3]={{1},{}}; // 初始化,第一行为1,0,0;第二行为0,0,0
int a3[][3]={{},{1,2,3}};
int a4[2][]={{1,2,3},{4,5,6}}; // 错误。必须声明第二维的数目
int a5[][3]={1,2,3,4,5,6}; // 正确
至于他们和指针的使用方法,看几个列子:
1. 一维数组的指针表示:
n
const int ARRAY_SIZE = 5;
int a[ARRAY_SIZE] = {1,2,3,4,5};
int *p = a;
a+i 则指数组a的第i项;
p+i 同上
*(a+i) 第i项的值
*a+i 为a[0]的值+i
p[i] 同样为a的第i项
p++偏移一个int字节
2. 二维数组的指针表示:
int a[ ][3] = {{1,2,3},{4,5,6}};
考察:a 表示第一行的首列
a+i 表示第i行的行指针
*(a+i) 表示第i行的首列列指针
*(a+i)+j 表示第i行第j列的列指针
*(*(a+i)+j) 上以指针的值
&a[ i ][ j ] 同于 *(a+i)+j
联系下哦,运行下下面的例子,想想是什么、为什么?
1.使用一级指针访问二维数组:
int a[ ][3] = {{1,2,3},{4,5,6}};
int *p=a[0]; // 将二维数组展开成一维数组的方式访问
int *p = a; // 错误!
考察: p+i, *(p+i), *p+i的含义
2. 使用指向一维数组的指针访问二维数组:
int (*p)[3]=a; // 也可写成:int (*p)[3](a);
int *p[3]=a; // 错误! Int* p[3];
考察:p, p+i, *(p+i), *p+i, *(p+i)+j, *(*(p+i)+j)的含义