struct包装:如何在开头添加struct成员?
Posted
技术标签:
【中文标题】struct包装:如何在开头添加struct成员?【英文标题】:struct packing: how to add struct members at the beginning? 【发布时间】:2019-08-28 19:59:16 【问题描述】:我正在 C89 中实现一棵二叉树,并且我试图通过组合在所有节点结构之间共享公共属性。因此我有以下代码:
enum foo_type
FOO_TYPE_A,
FOO_TYPE_B
;
struct foo
enum foo_type type;
;
struct foo_type_a
struct foo base;
struct foo * ptr;
;
struct foo_type_b
struct foo base;
char * text;
;
我在所有结构定义中都包含一个struct foo
类型的成员作为它们的初始成员,以便提供对enum foo_type
持有的值的访问,而不管结构类型如何。为了实现这一点,我期望一个指向结构对象的指针指向它的初始成员,但我不确定这个假设在这种情况下是否成立。对于 C99,标准规定如下(参见 ISO/IEC 9899:1999 6.7.2.1 §13)
一个指向结构对象的指针,经过适当的转换,指向它的初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然。结构对象中可能有未命名的填充,但不是在其开头。
虽然所有结构都共享一个共同的 struct foo
对象作为它们的初始成员,但填充开始发挥作用。虽然struct foo
只有一个成员,大小与int
相同,但struct foo_type_a
和struct foo_type_b
都包含指针成员,这在某些情况下会增加对齐并因此增加填充。
因此,考虑到这种情况,C 编程语言(C89 或任何后续版本)是否确保通过指向对象的指针访问 struct foo::type
的值是安全的,无论该对象的类型是 struct foo
还是包含struct foo
类型的对象作为其第一个成员,例如struct foo_type_a
或struct foo_type_b
?
【问题讨论】:
struct foo
总是相同,无论它是在另一个结构中还是单独存在。其他结构成员不能使struct foo
的两个实例在内存中具有不同的布局
@n.m.:不需要更多代码。很明显,他们想知道 C 1989 是否保证将指向结构的指针转换为指向其第一个成员类型的指针会产生指向其第一个成员的指针。
我不得不问,你为什么要在 1989 C 中开发新代码?
是的。引用的保证以及所有pointer-to-struct
类型应具有相同表示的保证是conformant 类型别名的基础。见What is the strict aliasing rule?
@UnholySheep 我担心的是 C 标准还指出对象需要“适当转换”的警告,我不确定指针类型之间的简单转换是否代表合适的转换。例如,如果对齐从 4 增加到 8 并且字节顺序魔法移动了重要位,那么天真的强制转换可能会导致问题。
【参考方案1】:
正如您自己引用的 C 标准,您所描述的内容受 C99 及更高版本的支持。
似乎它也受 C89 支持,因为您引用的语言已经存在于 1988 年的 ANSI-C 文档中:
3.5.2.1 结构和联合说明符
...
在结构对象中,非位域成员和单元 位域所在的地址按顺序增加 在其中声明它们。指向结构对象的指针,适当地 cast,指向它的初始成员(或者如果该成员是位域, 然后到它所在的单元),反之亦然。也许有 因此是结构对象内的未命名孔,但不是在其 开始,根据需要实现适当的对齐。
【讨论】:
但是那个条件不是依赖于指针被“适当地转换”吗?如果只需要在指向结构的指针之间进行简单转换,那么我想没有必要指定正确转换对象的重要性。 @RAM:“适当地转换”只是意味着转换为正确的类型。如果对转换要求有任何更繁重的要求,它会说些什么。 @RAM:使用标准用于描述的语言,给定union STOOGE MOE moe; LARRY larry; CURLY curly; *p;
,(MOE*)p
将等同于p->moe
;两者都将产生一个指针,该指针可以检查MOE
的任何成员,这些成员是与*p
中的任何对象共享的公共初始序列的一部分。在 gcc 和 clang 使用的 Standard 的解释下,(MOE*)p
和 p->moe
仍然是等价的,但是它们都产生的指针要么必须在使用前转换为字符指针,要么与函数一起使用像memcpy
这会将它们视为字符指针。以上是关于struct包装:如何在开头添加struct成员?的主要内容,如果未能解决你的问题,请参考以下文章