我的理解是,数组只是指向值序列的常量指针,当您在 C 中声明数组时,您就是在声明一个指针并为其指向的序列分配空间。
但这让我感到困惑:以下代码:
char y[20];
char *z = y;
printf("y size is %lu\n", sizeof(y));
printf("y is %p\n", y);
printf("z size is %lu\n", sizeof(z));
printf("z is %p\n", z);
使用 Apple GCC 编译时给出以下结果:
y size is 20
y is 0x7fff5fbff930
z size is 8
z is 0x7fff5fbff930
(我的机器是64位,指针是8字节长)。
如果“y”是常量指针,为什么它的大小为 20,就像它指向的值序列一样?在编译期间,变量名“y”是否在适当的时候被内存地址替换?那么,数组是 C 语言中的某种语法糖,在编译时会被转换为指针吗?
最佳答案
以下是 C 标准中的确切语言 (n1256):
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of thesizeof
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.
这里要记住的重要一点是,对象(在 C 语言中,意思是占用内存的东西)和用于引用的表达式之间存在差异到那个对象。
当你声明一个数组时
int a[10];
表达式 a
指定的对象是一个数组(即,足够大以容纳 10 个 int
值的连续内存块),并且 < em>表达式 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 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 thatE1[E2]
is identical to(*((E1)+(E2)))
. Because of the conversion rules that apply to the binary+
operator, ifE1
is an array object (equivalently, a pointer to the initial element of an array object) andE2
is an integer,E1[E2]
designates theE2
-th element ofE1
(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
从这里开始,高维数组的模式应该很清晰。
所以,总而言之:数组不是指针。在大多数情况下,数组表达式会转换为指针类型。
关于c - 在C语言中,数组是指针还是用作指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4607128/