这里是C标准文件(n1256)中的确切语言:
6.3.2.1 左值,数组和函数设计者
...
3 除非它是sizeof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串字面量,否则具有类型“type数组”的表达式将转换为具有类型“指向数组对象初始元素的type指针”的表达式,且不是左值。如果数组对象具有寄存器存储类,则行为未定义。
重要的是要记住,在C术语中,有一个区别在于对象(即占用内存的东西)和用于引用该对象的表达式之间。
当您声明一个数组时,例如
int a[10];
由表达式a
指定的对象是一个数组(即足以容纳10个int
值的连续内存块),而表达式a的类型为"10个元素的int
数组",或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 p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
你将看到类似以下的输出
p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80
我解释一下,当你使用下标操作符访问数组元素时,实际上计算了该元素相对于数组首地址的偏移量,并将该偏移量加到首地址上,从而得到该元素的地址。例如,在表达式
p[i]
中,
p
是一个指向数组第一个元素的指针,
i
是要访问的元素的索引。因此,
p+i
计算出了第 i 个元素的地址,然后
p[i]
就可以得到该元素的值。需要注意的是,
p+i
和
&p[i]
是等价的。
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();
下面是一个方便的表格,可以帮助您记住一些概念:
声明: T a[N];
表达式 类型 转换为 值
---------- ---- ------------ -----
a T [N] T * a的第一个元素的地址; 相当于写 &a[0]
&a T (*)[N] 数组的地址; 值与上面相同,但类型不同
sizeof a size_t 数组对象中包含的字节数 (N * sizeof T)
*a T a[0]处的值
a[i] T a[i]处的值
&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] T a[i][j]处的值
&a[i][j] T * a[i][j]的地址
声明: T a[N][M][O];
表达式 类型 转换为
---------- ---- -----------
a 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] T
从这里开始,更高维数组的模式应该很清楚了。
因此,总结一下:数组不是指针。在大多数情况下,数组表达式会被转换为指针类型。