但是这令人困惑我:以下代码:
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中的某种语法糖,在编译时会转化为指针吗?
#1 楼
以下是C标准(n1256)的确切语言:6.3.2.1左值,数组和函数指示符
...
3除
sizeof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串文字,将类型为“ array of type”的表达式转换为类型为“ pointer to type”的表达式,指向数组对象的初始元素,并且不是左值。如果数组对象具有寄存器存储类,则行为是不确定的。这里要记住的重要一点是,对象之间存在差异(用C表示,这意味着需要
声明诸如
int a[10];
之类的数组时,由对象指定的对象。表达式
a
是一个数组(即,一个足以容纳10个int
值的连续内存块),表达式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)添加到原始值。 更多标准语言:
2在后缀表达式后跟方括号
[]
的表达式是数组对象元素的下标名称。下标运算符[]
的定义是E1[E2]
与(*((E1)+(E2)))
相同。由于适用于二进制+
运算符的转换规则,如果E1
是数组对象(相当于指向数组对象初始元素的指针),而E2
是整数,则E1[E2]
指定E2
的第E1
个元素(计数)从零开始)。因此,当您给数组表达式下标时,实际上是从数组中第一个元素的地址开始计算偏移量,结果是取消引用。表达式
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
从这里开始,高维数组的模式应该很清楚。
因此,总而言之:数组不是指针。在大多数情况下,数组表达式都会转换为指针类型。
#2 楼
数组不是指针,尽管在大多数表达式中,数组名称的计算结果是指向数组第一个元素的指针。因此,使用数组名称作为指针非常非常容易。您经常会看到“衰减”一词用于描述这一点,例如“数组衰减为指针”。一个例外是
sizeof
运算符的操作数,其结果是大小数组的字节数(以字节为单位,而不是元素)。与此相关的另外两个问题:函数的数组参数是虚构的-编译器确实通过了一个普通的指针(这不适用于C ++中的数组引用),因此您无法确定传递给函数的数组的实际大小-您必须以其他方式传递该信息(也许使用显式的附加参数) ,或使用前哨元素-像C字符串一样)
此外,获取数组中元素数量的常见用法是使用类似以下的宏:
#define ARRAY_SIZE(arr) ((sizeof(arr))/sizeof(arr[0]))
这有一个问题,即接受一个数组名称(在它可以工作的地方),或者一个指针(它在没有编译器警告的情况下给出无用的结果)。存在更安全的宏版本(尤其是对于C ++),该宏版本在与指针而不是数组一起使用时会生成警告或错误。请参见以下SO项:
C ++版本
更好的C版本(尽管仍然不是很安全)
注意:C99 VLA(可变长度数组)可能不遵循所有这些规则(特别是,可以将它们作为参数传递,并具有被调用函数知道的数组大小)。我对VLA的经验很少,据我所知,它们并未得到广泛使用。但是,我确实要指出,以上讨论可能对VLA有所不同。
评论
所以做了sizeof异常是因为它很有用...我不知道实际上有一种方法可以知道数组的大小! (尽管它仍然没有那么有用,因为它只能找到固定大小的数组的大小,但是我想比为相同的目的定义许多常量要好)
–萨尔瓦多p
2011年1月6日在18:17
#3 楼
sizeof
在编译时求值,并且编译器知道操作数是数组还是指针。对于数组,它给出了数组占用的字节数。您的数组是一个char[]
(并且sizeof(char)
为1),因此sizeof
恰好为您提供了元素数量。要获取一般情况下的元素数量,一个常见的习惯用法是(对于int
而言):int y[20];
printf("number of elements in y is %lu\n", sizeof(y) / sizeof(int));
对于指针
sizeof
给出了原始指针所占用的字节数指针类型。#4 楼
在char hello[] = "hello there"
int i;
和
char* hello = "hello there";
int i;
在第一个实例(折扣对齐)中,将存储12个字节用于你好,已分配的空间初始化为你好,而在第二个你好,存储在其他地方(可能是静态空间),并且
hello
初始化为指向给定的字符串。但是在两种情况下都是e'。
#5 楼
除了其他人所说的之外,这篇文章也许还可以帮助您:http://en.wikipedia.org/wiki/C_%28programming_language%29#Array-pointer_interchangeability#6 楼
如果'y'是常量指针,为什么它的大小为20,就像它指向的值的序列一样?
因为
z
是变量,并将始终为您的计算机返回8。您需要使用取消引用指针(&)才能获取变量的内容。编辑:两者之间有很好的区别:http://www.cs.cf.ac.uk /Dave/C/node10.html
评论
他正在询问y,而您正在询问关于z,这令人困惑。很清楚为什么z的大小为8。对此你没有答案。此外,&是C中的地址运算符;取消引用运算符为*。
–彼得-恢复莫妮卡
18年1月15日在8:07
评论
该站点C ++常见问题的可能重复问题:数组名称是C中的指针吗?