指针在处理数组时很有用,我们可以用指针指向已有的数组,也可以从堆上分配内存然后把这块内存当做一个数组使用。数组表示法和指针表示法在某种意义上可以互换。不过,它们并不完全相同,后面的“数组和指针的差别”中会详细说明。
单独使用数组名字时会返回数组地址。我们可以把地址赋给指针,如下所示:
int vector[5] = {1, 2, 3, 4, 5};
int *pv = vector;
pv
变量是指向数组第一个元素而不是指向数组本身的指针。给pv
赋值是把数组的第一个元素的地址赋给pv
。
我们可以只用数组名字,也可以对数组的第一个元素用取地址操作符,如下所示。这些写法是等价的,都会返回vector
的地址。用取地址操作符更繁琐一些,不过也更明确。
printf("%p\n",vector);
printf("%p\n",&vector[0]);
有时候也会使用&vector
这个表达式获取数组的地址,不同于其他表示法,这么做返回的是整个数组的指针,其他两种方法得到是整数指针。这种类型的用法会在4.8节解释。
我们可以把数组下标用在指针上,实际上,pv[i]
这种表示法等价于:
*(pv + i)
pv
指针包含一个内存块的地址,方括号表示法会取出pv
中包含的地址,用指针算术运算把索引i
加上,然后解引新地址返回其内容。
就像我们在1.3.1节中讨论的那样,给指针加上一个整数会把它持有的地址增加这个整数和数据类型长度的乘积,这一点对于给数组名字加上整数也适用。下面两个语句是等价的:
*(pv + i)
*(vector + i)
假设vector
位于地址100,pv
位于地址96,表4-1和图4-4说明了如何利用数组下标和指针算术运算分别从数组名字和指针得到不同的值。
值 | 等价表达式 | |||
---|---|---|---|---|
92 |
&vector[-2] |
vector - 2 |
&pv[-2] |
pv - 2 |
100 |
vector |
vector+0 |
&pv[0] |
pv |
100 |
&vector[0] |
vector+0 |
&pv[0] |
pv |
104 |
&vector[1] |
vector + 1 |
&pv[1] |
pv + 1 |
140 |
&vector[10] |
vector + 10 |
&pv[10] |
pv + 10 |
给数组地址加1实际加了4,也就是整数的长度,因为这是一个整数数组。对于第一个和最后一个操作,我们越过了数组边界,这不是好习惯,不过也提醒我们在用索引和指针访问数组元素时要谨慎。
数组表示法可以理解为“偏移并解引”操作。vector[2]
表达式表示从vector
开始,向右偏移两个位置,然后解引那个位置获取其值,其中vector
是指向数组起始位置的指针。如果用取地址操作符和数组表示法,就像&vector[-2]
,其实就是去掉了解引操作,可以解释为向右偏移两个位置然后返回地址。
下面的代码说明了标量相加操作的实现中指针的使用。这个操作接受一个值然后给vector
的每个元素乘上这个值:
pv = vector;
int value = 3;
for(int i=0; i<5; i++) {
*pv++ *= value;
}