[英]Cast struct pointer to another struct
此代码段打印值5
。 我不明白为什么。
#include <stdio.h>
struct A
{
int x;
};
struct B
{
struct A a;
int y;
};
void printA(struct A *a)
{
printf("A obj: %d\n", a->x);
}
int main()
{
struct B b = {
{
5
},
10
};
struct A *a = (struct A*)&b;
printA(a);
printf("Done.\n");
return 0;
}
当我创建b
,指向它的指针将指向数据{ {5}, 10 }
。
当我将&b
为struct A*
,我向编译器保证这个struct A*
指向数据类型为int
的单个数据元素的结构。 相反,我提供了一个指向数据类型struct A
, int
的两个数据元素的结构的指针。
即使第二个变量被忽略(因为struct A
只有一个数据成员),我仍然提供一个结构,其成员的数据类型为struct A
,而不是int
。
因此,当我通过在a
给printA
,线a->x
进行,基本上,要求访问的第一数据元素a
。 的第一数据元素a
的数据类型的struct A
,这是一种类型不匹配,由于%d
期待一个数字,而不是一个struct A
。
到底发生了什么?
当我创建
b
,指向它的指针将指向数据{ {5}, 10 }
。
是的,从某种意义上讲,它是适合类型且价值正确的C初始化程序的文本。 这文字本身不应该按字面意思理解为结构的价值。
当我将
&b
为struct A*
,我向编译器保证这个struct A*
指向数据类型为int的单个数据元素的结构。
不,不完全是。 您正在将表达式&b
的值转换为类型struct A *
。 结果指针实际指向struct A
是一个单独的问题。
相反,我提供了一个指向数据类型
struct A
,int
的两个数据元素的结构的指针。
不,不是“反而”。 鉴于struct B
的第一个成员是struct A
,并且C禁止在结构的第一个成员之前填充,指向struct B
的指针也指向struct A
- B的第一个成员 - 在一般意义上。 正如@EricPostpischi在注释中观察到的那样,C标准在您的特定情况下明确指定结果:给定struct B b
,将指针b
转换为类型struct A *
产生指向b
的第一个成员的struct A
,即struct A
即使第二个变量被忽略(因为
struct A
只有一个数据成员),我仍然提供一个结构,其成员的数据类型为struct A
,而不是int
。
struct B
表示的第一个sizeof(struct A)
字节构成其第一个成员的表示形式,即struct A
后者是前者的成员除了在记忆中的重叠之外没有任何物理表现。
即使语言没有明确指定它,给定你的变量b
作为struct B
声明,也没有实际的理由期望表达式(struct A*)&b == &b.a
将评估为false,并且毫无疑问,右手指针可用于访问struct A
因此,当我通过在一个给
printA
,线a->x
进行,基本上,要求访问的第一数据元素a
。
是的,这就是一个断言进入那a
确实指向一个struct A
。 正如您已经讨论的那样,它在您的情况下做了什
的第一数据元素
a
是数据类型的struct A
,
根据定义, *a
是struct A
具体来说,它是struct A
其表示与b
的表示的开头重叠。 如果没有这样的struct A
那么行为将是未定义的,但这不是问题。 像每个struct A
,它有一个由x
指定的成员,它是一个int
。
这是一种类型不匹配,因为
%d
期望数字,而不是struct A
你的意思是期待一个int
。 这就是它的结果。 这就是表达式a->x
读取的内容,假设行为是完全定义的,因为这是该表达式的类型。 在不同的情况下,行为可能确实没有定义,但在任何情况下,该表达式都不会提供struct A
到底发生了什么?
似乎正在发生的事情是你正在想象比C实际提供的更高级别的语义。 特别是,您似乎将结构的心智模型作为可区分成员对象的列表,这会导致您形成不正确的期望。
也许您更熟悉一种弱类型语言,如Perl,或动态类型语言,如Python,但C的工作方式不同。 您无法查看C对象并且有用地询问“您的类型是什么”? 相反,您通过用于访问它的表达式的静态类型的镜头来查看每个对象。
语言 - 律师解释为什么代码是好的:
作为一种特殊情况,指向结构类型的指针等效于指向其第一个成员的指针。 C176.7.2§15的相关部分说:
指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。
这意味着(struct A*)&b
很好。 &b
适当地转换为正确的类型。
没有违反“严格别名”,因为我们符合C176.5§7:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,...
- 聚合或联合类型,包括其成员中的上述类型之一
初始成员的有效类型是struct A
在print函数中发生的左值访问很好。 struct B
也是一种聚合类型,在其成员中包含struct A
,因此无论顶部引用的初始成员规则如何,都不可能发生严格的别名冲突。
对于这种情况,C标准中有一条特殊规则。 C 2011 6.7.2.1 15说:
指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.