这是来自 C 标准 (n1256) 的确切语言:
6.3.2.1 左值、数组和函数指示符
...
3 除非它是 sizeof 运算符或一元 & 运算符的操作数,或者是用于初始化数组的字符串字面量,类型为 ''array of type' 的表达式' 被转换为类型为 ''pointer to type'' 的表达式,它指向数组对象的初始元素并且不是左值。如果数组对象具有寄存器存储类,则行为未定义。
这里要记住的重要一点是,object(在 C 术语中,意思是占用内存的东西)和用于引用的 expression 之间是有区别的到那个对象。
当你声明一个数组比如
int a[10];
表达式 a 指定的对象 是一个数组(即,一块大到足以容纳 10 个 int 值的连续内存块),而表达式 a 的类型是“int 的 10 元素数组”,或 int [10]。如果表达式 a 出现在不是sizeof 或& 运算符的操作数的上下文中,则其类型隐式转换为int *,其值为地址的第一个元素。
对于sizeof 运算符,如果操作数是T [N] 类型的表达式,则结果是数组对象中的字节数,而不是指向该对象的指针:N * sizeof T。
在&运算符的情况下,值为数组的地址,与数组的第一个元素的地址相同,但表达式的类型不同的是:给定声明T a[N];,表达式&a的类型是T (*)[N],或指向T的N元素数组的指针。值与a相同或&a[0](数组的地址与数组中第一个元素的地址相同),但类型的不同很重要。例如,给定代码
int a[10];
int *p = a;
int (*ap)[10] = &a;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
你会看到输出顺序为
p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80
IOW,前进p 将sizeof int (4) 添加到原始值,而前进ap 添加10 * sizeof int (40)。
更标准的语言:
6.5.2.1 数组下标
约束
1 一个表达式的类型应该是''pointer to object type'',另一个表达式应该是整数类型,结果的类型是''type''。
语义
2 后缀表达式后跟方括号[] 中的表达式是数组对象元素的下标名称。下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))。由于适用于二进制+ 运算符的转换规则,如果E1 是一个数组对象(等效地,一个指向数组对象初始元素的指针)并且E2 是一个整数,E1[E2] 指定E2-E1 的第一个元素(从零开始计数)。
因此,当您为数组表达式下标时,幕后发生的事情是计算与数组中第一个元素的地址的偏移量并取消引用结果。表达式
a[i] = 10;
等价于
*((a)+(i)) = 10;
相当于
*((i)+(a)) = 10;
相当于
i[a] = 10;
是的,C 中的数组下标是可交换的;看在上帝的份上,千万不要在生产代码中这样做。
由于数组下标是根据指针运算定义的,因此您可以将下标运算符应用于指针类型和数组类型的表达式:
int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
p[i] = some_initial_value();
这里有一个方便的表格来记住其中的一些概念:
声明:Ta[N];
表达式类型转换为值
---------- ---- ------------ -----
a T [N] T * a中第一个元素的地址;
等同于写 &a[0]
&a T (*)[N] 数组的地址;值是一样的
同上,但类型不同
sizeof a size_t 数组中包含的字节数
对象(N * sizeof T)
*a[0] 处的 T 值
a[i] a[i] 处的 T 值
&a[i] T * a[i] 的地址
声明:T a[N][M];
表达式类型转换为值
---------- ---- ------------ -----
a T [N][M] T (*)[M] 第一个子数组的地址 (&a[0])
&a T (*)[N][M] 数组的地址(与
以上,但类型不同)
sizeof a size_t 包含的字节数
数组对象(N * M * sizeof T)
*a T [M] T * a[0]的值,即地址
第一个子数组的第一个元素
(与 &a[0][0] 相同)
a[i] T [M] T * a[i]的值,即地址
第 i 个子数组的第一个元素
&a[i] T (*)[M] 第i个子数组的地址;相同的值
以上,但不同类型
sizeof a[i] size_t 第 i 个子数组中包含的字节数
对象 (M * sizeof T)
*a[i] T 第 i 个第一个元素的值
子数组 (a[i][0])
a[i][j] a[i][j] 处的 T 值
&a[i][j] T * a[i][j] 的地址
声明:T a[N][M][O];
表达式类型转换为
---------- ---- ------------
一个 T [N][M][O] T (*)[M][O]
&a T (*)[N][M][O]
*a T [M][O] T (*)[O]
a[i] T [M][O] T (*)[O]
&a[i] T (*)[M][O]
*a[i] T [O] T *
a[i][j] T [O] T *
&a[i][j] T (*)[O]
*a[i][j] T
a[i][j][k]
从这里开始,高维数组的模式应该很清楚了。
因此,总而言之:数组不是指针。在大多数情况下,数组表达式被转换为指针类型。