通过联合键入C和C++中的结构

loo*_*oop 11 c c++ strict-aliasing language-lawyer type-punning

我用gcc和g ++编译了这个迂腐,我不会在任何一个中得到警告:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct a {
    struct a *next;
    int i;
};

struct b {
    struct b *next;
    int i;
};

struct c {
    int x, x2, x3;
    union {
        struct a a;
        struct b b;
    } u;
};

void foo(struct b *bar) {
    bar->next->i = 9;
    return;
}

int main(int argc, char *argv[]) {
    struct c c;
    memset(&c, 0, sizeof c);
    c.u.a.next = (struct a *)calloc(1, sizeof(struct a));
    foo(&c.u.b);
    printf("%d\n", c.u.a.next->i);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这在C和C++中是否合法?我读过关于打字的类型,但我不明白.有foo(&c.u.b)什么不同foo((struct b *)&c.u.a)吗?它们不一样吗?联合中结构的这种例外(来自3.3.2.3中的C89)说:

如果联合包含多个共享公共初始序列的结构,并且如果联合对象当前包含这些结构中的一个,则允许检查它们中的任何一个的公共初始部分.如果相应的成员具有一个或多个初始成员的序列的兼容类型,则两个结构共享共同的初始序列.

在联合中,第一个成员struct astruct a *next,第一个成员struct bstruct b *next.正如你所看到的那样,指针struct a *next被写入,然后在foo struct b *next中读取指针.它们兼容吗?它们都是指向结构的指针,指向任何结构的指针应该是相同的大小,所以它们应该是兼容的,布局应该是相同的吗?i从一个结构读取并写入另一个结构是否可以?我是否提交任何类型的别名或类型惩罚违规?

T.C*_*.C. 5

在C:

struct a并且struct b不兼容类型.即使在

typedef struct s1 { int x; } t1, *tp1;
typedef struct s2 { int x; } t2, *tp2;
Run Code Online (Sandbox Code Playgroud)

s1并且s2不兼容类型.(参见6.7.8/p5中的示例.)识别不兼容结构的一种简单方法是,如果两个结构类型兼容,则可以将一种类型的某些类型分配给另一种类型.如果您希望编译器在尝试执行此操作时进行投诉,则它们不是兼容类型.

因此,struct a *struct b *也是不兼容的类型,所以struct astruct b没有共享一个通用初始序列.在其他情况下,你的工会惩罚取决于工会双关的相同规则(6.5.2.3脚注95):

如果用于读取union对象的内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的适当部分重新解释为新类型中的对象表示形式在6.2.6中描述(一个过程有时被称为''punning'').这可能是陷阱表示.


在C++中,struct a并且struct b也不要共用一个初始序列.[class.mem]/p18(引用N4140):

如果相应的成员具有布局兼容类型并且两个成员都不是位字段,或者两者都是具有相同宽度的位字段(对于一个或多个初始成员的序列),则两个标准布局结构共享共同的初始序列.

[basic.types]/P9:

如果两种类型T1,并T2属于同一类型,然后T1T2布局兼容的类型.[ 注意:与布局兼容的枚举在7.2中描述.9.2中描述了与布局兼容的标准布局结构和标准布局联合.- 结束说明 ]

struct a *并且struct b *既不结构也不是工会,也不枚举; 因此,如果它们是相同的类型,它们只是布局兼容的,它们不是.

确实([basic.compound]/p3)

符合cv资格和cv不合格版本(3.9.3)的布局兼容类型的指针应具有相同的值表示和对齐要求(3.11).

但这并不意味着那些指针类型是布局兼容类型,因为该术语在标准中定义.