我有一个如下所示的结构体:
struct packet {
int a;
char data[500];
};
typedef struct packet packet_t;
我有点困惑为什么下面的代码对每个printf输出相同的地址:
void myfunction() {
packet_t packet;
printf("%p\n", packet.data); //e.g., outputs 0x7fff1c323c9c
printf("%p\n", &packet.data); //e.g., outputs 0x7fff1c323c9c
}
对此,有谁能给出一个好的解释?
6条答案
按热度按时间iqjalb3h1#
在大多数情况下,一个类型为“
T
的N元素数组“的表达式将被转换为类型为“指向T
的指针“的表达式,其值将是数组中第一个元素的地址。这是在第一次printf
调用中发生的事情;类型为char [500]
的表达式packet.data
被替换为类型为char *
的表达式,其值为第一个元素的地址,因此实际上打印的是&packet.data[0]
。当数组表达式是一元
&
运算符的操作数时,此规则有一个例外;表达式&packet.data
的类型是char (*)[500]
(指向char
的500元素数组的指针)。数组的地址与第一个元素的地址相同,因此对
printf
的两次调用显示相同的 value;只是表达式的类型不同,为了学究气,两个表达式都应该在printf
调用中强制转换为void *
(%p
转换说明符需要一个void *
参数):ccgok5k52#
这是因为数组衰减到指向序列中第一个元素的指针,所以
packet.data
地址位置与&packet.data
或&packet.data[0]
相同。qoefvg9y3#
根据C11(6.3.2.1.3)节,当数组用作
sizeof
和一元&
运算符的操作数时,数组衰减为指向其第一个元素 ( 除外)的指针。因此,
packet.data
* 不会 * 在表达式&packet.data
中衰减为&packet.data[0]
,结果将是char (*)[500]
类型的指针,并且在数值上与其第一个元素packet.data[0]
的地址相同。dgsult0t4#
虽然现有的答案在技术上已经是正确的,而且还包括相关的C ISO标准部分,但我觉得它根本没有说明为什么这些问题重要。
既然问题是关于"差异",我将尝试提供一个实际的差异。
考虑:
编译并运行:
这里有两点需要注意:
1.编译器会对不兼容的指针类型发出一个错误(已经在其他答案中解释过了)。所以这样一来,它们不仅在学术上不同,而且在实际上也不同(类型方面)。
(char *)
和(char (*)[10])
的指针算法是不同的。在第一行printf中,我们可以看到所有不同的变量都指向同一个地址。* 然而 *,在执行指针算法时,它们的值会出现偏差。在(char *)
的情况下,指针算法按预期工作。变量加1将使地址增加1。对于(char (*)[10])
,变量加1将使地址增加10。clj7thdc5#
因为这是除了使&packet.data导致编译错误之外唯一合理的做法,Integer a和data char数组在堆栈中是按顺序排列的,不涉及解引用。
pkmbmrz76#
我不知道为什么这个问题被否决了,这是一个很好的问题,它暴露了C的一个令人困惑的行为。
混淆的原因是通常当你定义一个数组时,会创建一个真实的指针:
因此,在典型的32位桌面系统上,为
data
分配4个字节,data
是指向100个字符的指针。Data
,指针本身存在于内存中的某个地方。当你在一个结构体中创建一个数组时,并没有分配指针。相反,编译器在运行时将对
packet.data
的引用转换成一个指针,但不分配任何内存来存储它。相反,它只使用&packet + offsetof(data)
。就我个人而言,我更希望语法保持一致,并且需要一个“与”符号,因为packet.data会生成某种编译时错误。