当我看到它们时,有很多C语言(作为一个主要的C ++程序员)让我的皮肤爬行。
struct A
{
int id;
int bar;
char *quux;
};
struct B
{
int id;
double foo1;
double foo2;
};
struct C
{
int id;
void *something;
};
union X
{
int id;
struct A a;
struct B b;
struct C c;
};
union X x;
我看到这样做,例如,GTK +,它以这种方式处理事件。有一组结构,每个结构包含有关特定事件类型的信息,每个结构都以int id
开头,与X.id
对齐,以便可以用来确定联合中的结构类型实际初始化。
struct A
{
int count;
};
struct B
{
struct A a;
void *foo;
float bar;
};
struct X
{
struct B b;
int wibble;
int wobble;
int wubble;
int flob;
};
struct X *x = make_X(); // malloc me an X and initialize it
我看到这个习惯用于C对象的主动引用计数。让我害怕的成语的特定部分是这样的部分,为了获得引用计数数据,我们的struct X*
直接转换为struct A*
,它依赖于在没有填充的情况下创建的结构,以及它们的元素按照定义中指定的顺序。
这些习语是否在C中得到了很好的定义?总是,永远,永远不会?经验证据表明它们会在某些情况下起作用,但我不知道它们是否正在工作,因为它们被定义为可行,或者它们是否依赖于某种未定义的行为。 GTK依赖其中一个这一事实并没有让我更愿意相信它的定义很好,这不是linux家伙第一次依赖平台特定的行为。我非常感谢标准相关部分的一些链接,但我不确定从哪里开始寻找。
答案 0 :(得分:3)
C11 6.7.2.1结构和联合说明符p15表示
在结构对象中,非位字段成员和单位 哪些位字段驻留的地址按顺序增加 他们被宣布。 适当地指向结构对象的指针 已转换,指向其初始成员(或者如果该成员是 位字段,然后到它所在的单元,反之亦然。 结构对象中可能有未命名的填充,但不在其中 开始。
因此可以安全地假设x
在您的示例中指向x->b.a.count
。
答案 1 :(得分:2)
C标准规定在结构的第一个成员之前可能没有填充。