数组的地址 – 具有&符号和没有&符号之间的区别
我有一个看起来像这样的结构:
struct packet { int a; char data[500]; }; typedef struct packet packet_t;
我有点困惑为什么以下代码为每个printf输出相同的地址:
void myfunction() { packet_t packet; printf("%p\n", packet.data); //eg, outputs 0x7fff1c323c9c printf("%p\n", &packet.data); //eg, outputs 0x7fff1c323c9c }
有没有人对此有一个很好的解释?
我不知道为什么这会被拒绝,这是一个很好的问题,暴露了C的混乱行为。
之所以出现混乱,是因为通常在定义数组时会创建一个真正的指针:
char data[100]; printf("%p\n", data); // print a pointer to the first element of data[] printf("%p\n", &data); // print a pointer to a pointer to the first element of data[]
因此,在典型的32位桌面系统上,为data
分配了4个字节,这是指向100个字符的指针。 Data
,指针,本身存在于内存中的某个位置。
在结构中创建数组时,不会分配指针。 相反,编译器会在运行时packet.data
引用转换为指针,但不会分配任何内存来存储它。 相反,它只使用&packet + offsetof(data)
。
我个人更喜欢语法一致并需要一个&符号,packet.data会产生某种编译时错误。
在大多数情况下,类型为“N元素数组T
”的表达式将转换为“指向T
指针”类型的表达式,其值将是数组中第一个元素的地址。 这是第一次printf
调用中发生的事情; 表达式packet.data
,其类型为char [500]
,将替换为char *
类型的表达式,其值为第一个元素的地址,因此您可以有效地打印&packet.data[0]
。
当数组表达式是一元&
运算符的操作数时,会出现此规则的一个例外。 表达式&packet.data
的类型是char (*)[500]
(指向char
500元素数组的指针)。
数组的地址与第一个元素的地址相同,因此对printf
两次调用都显示相同的值 ; 只是表达式的类型不同。 要迂腐,两个表达式都应该在printf
调用中转换为void *
( %p
转换说明符需要一个void *
参数):
printf("%p\n", (void *) packet.data); printf("%p\n", (void *) &packet.data);
那是因为数组衰减到指向序列中第一个元素的指针。 因此, packet.data
地址位置与&packet.data
或&packet.data[0]
。
因为除了make和packet.data导致编译错误之外,这是唯一合理的事情。 整数a和数据字符数组按顺序排列在堆栈中,不涉及解除引用。