通过C强制类型转换访问结构体的第一个字段是否违反了严格别名规则?

15

这段代码是否违反了严格别名规则?

struct {int x;} a;
*(int*)&a = 3
更抽象地说,只要原始的读写操作正确,将不同类型之间进行强制类型转换是否合法?

2
虚函数表可能是最先出现的,因此这是未定义行为领域。 - ildjarn
1
@ildjarn,C语言中不存在虚函数表。 - bdonlan
4
这个标签也被打上了 c++。;-] - ildjarn
@curiousguy:不,我只是不知道这个标签。 - Geoffrey Irving
C++答案:https://stackoverflow.com/q/50383187/5740428 - undefined
显示剩余4条评论
1个回答

26
首先,在C语言中强制转换指向结构体的指针以访问其中的第一个成员是合法的,这在C语言标准6.7.2.1/13中有所说明。该规定指出,在结构体对象内部,非位域成员和位于位域内的单元的地址按照声明顺序递增。指向结构体对象的指针经过适当转换后指向其初始成员(如果该成员是位域,则指向它所在的单元),反之亦然。结构体对象内部可能存在未命名的填充空间,但不会出现在起始位置。
其次,C语言中的别名规则(aliasing rule)如下(§6.5/7):只有以下类型的lvalue表达式才能访问一个对象的存储值:
- 与对象的有效类型相容的类型; - 对象的有效类型的限定版本的相容类型; - 与对象的有效类型相应的带符号或无符号类型; - 对象的有效类型的限定版本的相应带符号或无符号类型; - 包含上述任一类型之一的聚合体或联合体类型(包括子聚合体或包含联合体的成员的递归类型); - 字符类型。
在本例中,您将通过“与对象的有效类型相容的类型”和“包含上述任一类型之一的聚合体或联合体类型”类型的指针访问该对象,因此别名也没有问题。因此,在C语言中,通过将指向结构体的指针转换为所需成员的类型来访问结构体的第一个成员是完全合法的。
但在C++中,通常会在C++对象的开头找到虚表(vtable)等内容。但在您的特定情况下,您的结构体具有标准布局,因此这是明确允许的(参见n3290中的§9.2/20,感谢Luc Danton!- C++03显然有类似于POD对象的规定)。

C++ 绝对不会掩盖 vtable 的存在,但我只在没有这种奇怪情况的情况下应用它。 - Geoffrey Irving
@Geoffrey: "没有这种奇怪情况". 也就是可以轻松复制的类型?也就是你应该在你的问题中提到的东西? - ildjarn
1
好的,问题提到结构体只有一个整型字段,这意味着没有虚函数表等其他内容。标题已经相当冗长了。 - Geoffrey Irving
在这种特定情况下,它将是一个POD类型 - 不确定C ++中是否适用相同的保证(没有手头的规范)。 - bdonlan
7
对于C++11的相关规则,指向标准布局结构体类型对象的指针可以被“reinterpret_cast”为指向其初始成员的指针(参见n3290中的9.2类成员[class.mem]第20段)。这个规则(和任何涉及标准布局类型的东西)旨在模仿这里引用的C的规则。C++03将有类似的内容,不同之处在于它将基于POD结构体,而不是标准布局结构体。 - Luc Danton
1
你必须证明两件不同的事情:(1)强制转换已定义并返回有效指针 (2)可以对指针进行解引用,可以访问lvalue - curiousguy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接