所谓数组指针,即是指向数组的指针。指针变量用来存放数组的地址值,通过星号操作 ‘ * ‘ 来间接访问该地址下的数据。
如我们定义一个int型数组:
1 2 3 |
int num[3] = { 1, 2, 3 }; |
那么便会在内存中(栈)开辟一个地址连续的空间,用来存放这三个数据,我们假设起始地址为0xff0,那么这三个数在内存中会按如下排列,其中地址每次加4,是因为一个int型数据占用了四个字节。
我们再定义一个数组指针,用来指向这个数组:
1 2 3 |
int *p = num; |
因为数组本质上就是通过地址索引的,如上面的图示中,如果要读取出第二个数据num[1],那么从汇编上来看,需要知道这个数组的首地址0xff0,再根据下标[1],计算出它的真正地址:0xff0+1*sizeof(int) = 0xff0 + 4 = 0xff4,再取目标地址0xff4里面的数据,即为2。
而在C语言中,数组名就代表了数组的首地址,也就是说在本例中,num即代表了0xff0。
我们用 int *p = num这个操作,将num的地址0xff0赋值给p,而p也是一个变量,它也需要存放在内存中,而内存是需要地址去访问的,所以p在内存中也是有一个自己的地址的。p内部存放的数据就是刚刚赋值的0xff0,假设p在内存地址为0xfe8处,那么用图的形式表示为:
由上图我们可以看到p的值就是数组的首地址值0xff0,如果我们要访问数组的3个值,就可以不用直接操作数组了,可以通过如下方式获得:
1 2 3 4 5 |
*(p+0) //等价与p[0] *(p+1) //等价与p[1] *(p+2) //等价于p[2] |
因为num即代表了数组的首地址,而num的值(0xff0)赋值给了p,所以num[1]和p[1]两者表达的意思是一样的。
对于*(p+1)这样的操作为什么也可以呢?是因为p+1操作对应的汇编上,相当于这样一段指令(当然以下不是真的汇编指令,而是写成了C格式的汇编操作过程):
1 2 3 4 5 6 |
eax = p; //eax = 0xff0 eax = eax + 0x4; //eax = 0xff0 + 4 = 0xff4, 其中0x4其实编译器会自动计算好, //计算过程相当于: sizeof(int) * 1 = 0x4, int型换成其他数据类型也是一样的。 |
一番操作后我们就得到了数组第二个数的地址值eax=0xff4了,此时*(p+1) 就等价于 *eax, 星 * 操作后即获得地址值0xff4对应的内容2。
代码示例:
运行结果: