以下是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术语中,表示占用内存的内容)与用于引用该对象的expression之间存在差异.
当您声明一个数组时,例如
int a[10];
expression a
指定的object是一个数组(即,一个连续的内存块,其大小足以容纳10个int
值),expression a的类型是"int
的10元素数组",或int [10]
.如果expression a
不是作为sizeof
或&
运算符的操作数出现在上下文中,则其类型将隐式转换为int *
,其值是第一个元素的地址.
对于sizeof
运算符,如果操作数是T [N]
类型的表达式,则结果是数组对象中的字节数,而不是指向该对象的指针中的字节数:N * sizeof T
.
在&
运算符的情况下,该值是数组的地址,与数组第一个元素的地址相同,但表达式的type不同:给定声明T a[N];
,表达式&a
的类型是T (*)[N]
,value与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
从表面上看,推进p
会使原始值增加sizeof int
(4),而推进ap
会使原始值增加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;
which 相当于
*((i)+(a)) = 10;
which 相当于
i[a] = 10;
是的,C中的数组下标是可交换的;看在god 的份上,千万不要在生产代码中这样做.
由于数组下标是根据指针操作定义的,因此可以将下标运算符应用于指针类型和数组类型的表达式:
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
从这里,高维数组的模式应该是清晰的.
总之,数组不是指针.在大多数情况下,数组expressions被转换为指针类型.