函数篇里说过指针能存某一类型变量的地址。数组是一大块连续的同类型数据,那指针能指向数组吗?能,而且这两者关系密到经常被当成一回事,但严格说它们不是同一样东西。
数组名就是首元素的地址
看一组对照实验。下面代码用三种写法打印同样 5 个字符数组的地址和值:
#include <stdio.h>
int main() {
char vowels[] = {'A', 'E', 'I', 'O', 'U'};
char *pvowels = vowels; // 直接把数组名给它
int i;
/* 打地址:三种写法完全一样 */
for (i = 0; i < 5; i++) {
printf("&vowels[%d]: %p | pvowels+%d: %p | vowels+%d: %p\n",
i, &vowels[i], i, pvowels + i, i, vowels + i);
}
/* 打值:两种写法一样 */
for (i = 0; i < 5; i++) {
printf("vowels[%d]: %c | *(pvowels+%d): %c | *(vowels+%d): %c\n",
i, vowels[i], i, *(pvowels + i), i, *(vowels + i));
}
return 0;
}典型输出:
&vowels[0]: 0x7ffe...17, pvowels+0: 0x7ffe...17, vowels+0: 0x7ffe...17
&vowels[1]: 0x7ffe...18, pvowels+1: 0x7ffe...18, vowels+1: 0x7ffe...18
...(地址逐个差 1 字节,因为 char 占 1 字节)
vowels[0]: A, *(pvowels+0): A, *(vowels+0): A
vowels[1]: E, *(pvowels+1): E, *(vowels+1): E
...结论有两条:
&vowels[i]、pvowels + i、vowels + i三个是同一个地址。- 数组名本身是"指向首元素的常量指针",
vowels、&vowels[0]、vowels + 0都指着同一块位置。
那么指针和数组是一回事吗
不是。名字骗人,地位不同:
- 数组名是常量:不能重新赋值,
vowels = someone_else;是错的;指针变量可以。 - 数组名只在声明它的作用域里有体积;传进函数退化成指针后,
sizeof得到的就不是元素数了。 - 但用起来,下标、取址、遍历,两者几乎可以互换着写,这也是为什么初学者总把二者挂钩。
用指针遍历数组
既然 arr[i] 等价于 *(arr + i),指针就能当下标用:
int arr[5] = {2, 4, 8, 12, 16};
int *p = arr;
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 2 4 8 12 16
}
printf("\n");也可以推着指针走(指针每次加 1 实际跨过一个元素的字节数,下一节细讲):
for (int *p = arr; p < arr + 5; p++) {
printf("%d ", *p);
}
printf("\n");指针能过下标瘾,但要注意边界
指针加下标和不加边界,跟数组越界是同一类危险。写循环锁死 i < 元素个数,不管用指针还是下标都是一样的纪律。
下一篇讲指针的另一个大用途,按引用传给函数,让函数真正改得动外面的变量。