这是 C 标准中的确切语言(n1256):
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof
operator or the unary &
operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
这里要记住的重要一点是,object(在 C 术语中,意味着占用内存的东西)和表达用于指代该对象。
当您声明一个数组时,例如
int a[10];
the object由指定表达 a
是一个数组(即,足够大以容纳 10 个连续的内存块int
值),以及类型表达a 是“10 元素数组int
", or int [10]
。如果表达 a
出现在除作为操作数之外的上下文中sizeof
or &
运算符,然后其类型隐式转换为int *
,其值为第一个元素的地址。
在这种情况下sizeof
运算符,如果操作数是类型的表达式T [N]
,那么结果是数组对象中的字节数,而不是指向该对象的指针中的字节数:N * sizeof T
.
在这种情况下&
运算符,其值为数组的地址,与数组第一个元素的地址相同,但是type表达式的不同:给定声明T a[N];
, 表达式的类型&a
is T (*)[N]
,或指向 T 的 N 元素数组的指针。value是相同的a
or &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
adds sizeof int
(4) 恢复原值,同时前进ap
adds 10 * sizeof int
(40).
更标准的语言:
6.5.2.1 Array subscripting
Constraints
1 One of the expressions shall have type ‘‘pointer to object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.
Semantics
2 A postfix expression followed by an expression in square brackets []
is a subscripted designation of an element of an array object. The definition of the subscript operator []
is that E1[E2]
is identical to (*((E1)+(E2)))
. Because of the conversion rules that apply to the binary +
operator, if E1
is an array object (equivalently, a pointer to the initial element of an array object) and E2
is an integer, E1[E2]
designates the E2
-th element of E1
(counting from zero).
因此,当您为数组表达式添加下标时,幕后发生的事情是计算距数组中第一个元素的地址的偏移量,并取消引用结果。表达方式
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();
这是一个方便记住其中一些概念的表格:
Declaration: T a[N];
Expression Type Converts to Value
---------- ---- ------------ -----
a T [N] T * Address of the first element in a;
identical to writing &a;[0]
&a; T (*)[N] Address of the array; value is the same
as above, but the type is different
sizeof a size_t Number of bytes contained in the array
object (N * sizeof T)
*a T Value at a[0]
a[i] T Value at a[i]
&a;[i] T * Address of a[i]
Declaration: T a[N][M];
Expression Type Converts to Value
---------- ---- ------------ -----
a T [N][M] T (*)[M] Address of the first subarray (&a;[0])
&a; T (*)[N][M] Address of the array (same value as
above, but different type)
sizeof a size_t Number of bytes contained in the
array object (N * M * sizeof T)
*a T [M] T * Value of a[0], which is the address
of the first element of the first subarray
(same as &a;[0][0])
a[i] T [M] T * Value of a[i], which is the address
of the first element of the i'th subarray
&a;[i] T (*)[M] Address of the i-th subarray; same value as
above, but different type
sizeof a[i] size_t Number of bytes contained in the i'th subarray
object (M * sizeof T)
*a[i] T Value of the first element of the i'th
subarray (a[i][0])
a[i][j] T Value at a[i][j]
&a;[i][j] T * Address of a[i][j]
Declaration: T a[N][M][O];
Expression Type Converts to
---------- ---- -----------
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
从这里开始,高维数组的模式应该很清楚了。
所以,总而言之:数组不是指针。在大多数情况下,数组表达式被转换为指针类型。