我的理解是数组只是指向一系列值的常量指针,当你在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中的某种语法糖,它在编译时只是转换为指针的东西?
答案 0 :(得分:55)
以下是C标准(n1256)的确切语言:
6.3.2.1左值,数组和函数指示符
...
3除非它是sizeof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串文字,否则表达式的类型为''数组 type < / em>''被转换为类型为''指向 type '的指针的表达式,它指向数组对象的初始元素,而不是左值。如果数组对象具有寄存器存储类,则行为未定义。
要记住的重要一点是对象(用C语言表示占用内存的东西)和用于引用的表达式之间存在差异那个对象。
声明诸如
之类的数组时int a[10];
由表达式 a
指定的对象是一个数组(即,一个足够大的内存块,可容纳10 int
个值),表达式的类型是“int
”的10元素数组,或int [10]
。如果表达式 a
出现在除sizeof
或&
运算符的操作数之外的上下文中,则其类型将隐式转换为{{1} },它的值是第一个元素的地址。
对于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);
IOW,推进p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80
将p
(4)添加到原始值,而推进sizeof int
则添加ap
(40)。
更多标准语言:
6.5.2.1数组下标
约束
1其中一个表达式应具有类型''指向对象类型''的指针,另一个表达式应具有整数类型,结果类型为'' type ''。
语义
2后缀表达式后跟方括号10 * sizeof int
中的表达式是数组对象元素的下标。下标运算符[]
的定义是[]
与E1[E2]
相同。由于适用于二进制(*((E1)+(E2)))
运算符的转换规则,如果+
是一个数组对象(等效地,指向数组对象的初始元素的指针),并且E1
是一个整数,E2
指定E1[E2]
的{{1}}个元素(从零开始计数)。
因此,当你下标一个数组表达式时,幕后发生的是计算数组中第一个元素地址的偏移量并取消引用结果。表达式
E2
相当于
E1
相当于
a[i] = 10;
相当于
*((a)+(i)) = 10;
是的,C中的数组下标是可交换的;为了上帝的爱,永远不要在生产代码中这样做。
由于数组下标是根据指针操作定义的,因此可以将下标运算符应用于指针类型的表达式以及数组类型:
*((i)+(a)) = 10;
这是一个方便的表格来记住其中的一些概念:
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
从这里开始,高维数组的模式应该清晰。
因此,总结一下:数组不是指针。在大多数情况下,数组表达式将转换为指针类型。
答案 1 :(得分:23)
数组不是指针,但在大多数表达式中,数组名称的计算结果为指向数组第一个元素的指针。因此,使用数组名称作为指针非常非常容易。您经常会看到用于描述这个术语的“衰变”,如“数组衰减为指针”。
一个例外是作为sizeof
运算符的操作数,其结果是数组的大小(以字节为单位,而不是元素)。
与此相关的另外几个问题:
函数的数组参数是虚构的 - 编译器实际上传递了一个普通指针(这不适用于C ++中的引用数组参数),因此无法确定传递给数组的数组的实际大小函数 - 你必须以其他方式传递该信息(可能使用显式的附加参数,或使用sentinel元素 - 如C字符串那样)
另外,获取数组中元素数量的常用习惯是使用如下宏:
#define ARRAY_SIZE(arr) ((sizeof(arr))/sizeof(arr[0]))
这有一个接受数组名称,工作位置或指针的问题,它会在没有编译器警告的情况下给出无意义的结果。存在更安全的宏版本(特别是对于C ++),当它与指针而不是数组一起使用时会产生警告或错误。请参阅以下SO项目:
注意:C99 VLA(可变长度数组)可能不遵循所有这些规则(特别是,它们可以作为具有被调用函数已知的数组大小的参数传递)。我对VLA的经验很少,据我所知,它们没有被广泛使用。但是,我想指出上述讨论可能对VLA采用不同的方式。
答案 2 :(得分:6)
sizeof
,编译器知道操作数是数组还是指针。对于数组,它给出了数组占用的字节数。您的数组是char[]
(而sizeof(char)
是1),因此sizeof
恰好为您提供了元素数量。为了获得一般情况下的元素数量,常见的习语是(此处为int
):
int y[20];
printf("number of elements in y is %lu\n", sizeof(y) / sizeof(int));
指针sizeof
给出原始指针类型占用的字节数。
答案 3 :(得分:1)
除了其他人所说的,也许这篇文章有助于:http://en.wikipedia.org/wiki/C_%28programming_language%29#Array-pointer_interchangeability
答案 4 :(得分:1)
在
char hello[] = "hello there"
int i;
和
char* hello = "hello there";
int i;
在第一个实例中(折扣对齐)将为hello存储12个字节,其中已分配的空间初始化为 hello there ,而在第二个 hello中,存储在其他地方(可能是静态空间)并初始化hello
以指向给定的字符串。
hello[2]
以及*(hello + 2)
将在两个实例中都返回“e”。
答案 5 :(得分:-2)
如果'y'是常量指针,为什么它的大小为20,就像它指向的值序列一样?
因为z
是变量的地址,并且将始终为您的机器返回8。您需要使用取消引用指针(&amp;)来获取变量的内容。
编辑:两者之间的区别很好:http://www.cs.cf.ac.uk/Dave/C/node10.html