将结构强制转换为包含它的联合是否合法?

Ser*_*sta 7 c struct casting unions language-lawyer

这是关于从不同结构打印公共成员的问题的后续

我认为联合允许检查它们的两个元素的共同初始序列。所以我以以下代码结束:

#include <stdio.h>

struct foo {
    const char *desc;
    float foo;
};
struct bar {
    const char *desc;
    int bar;
};
union foobar {
    struct foo foo;
    struct bar bar;
};

void printdesc(const union foobar * fb) {
    printf("%s\n", fb->foo.desc);          // allowed per 6.5.2.3 Structure and union members
}

int main() {

    struct bar bb = {"desc bar", 2};

    union foobar fb = { .bar=bb};

    printdesc((union foobar *) &(fb.bar)); // allowed per 6.7.2.1 Structure and union specifiers
    printdesc((union foobar *) &bb);       // legal?

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它编译时甚至没有警告并给出预期的结果

desc bar
desc bar
Run Code Online (Sandbox Code Playgroud)

这里的重点是带有// 合法?评论。我已将 abar *转换为foobar *. 当barfoobar联合成员中时,根据 6.7.2.1 结构和联合说明符允许。但这里我不知道。

如果未声明为 a 的成员,是否允许将指向bar对象的指针转换为指向对象的指针?foobarbarfoobar

问题不在于它是否可以在特定的编译器中工作。我很确定它适用于当前版本中的所有常见编译器。问题在于它是否是合法的 C 代码。


这是我目前的研究。

来自 C11 的 n1570 草案的参考资料:

6.5.2.3 结构和工会成员 § 6

...如果联合包含多个共享公共初始序列的结构(见下文),并且联合对象当前包含这些结构之一,则允许检查其中任何一个的公共初始部分......

6.7.2.1 结构和联合说明符 § 16

... 一个指向联合对象的指针,经过适当的转换,指向它的每个成员......,反之亦然......

dbu*_*ush 0

一般来说,没有

假设您有以下定义:

struct foo {
    int flag;
    double foo;
};
struct bar {
    int flag;
    int bar;
};
union foobar {
    struct foo foo;
    struct bar bar;
};
Run Code Online (Sandbox Code Playgroud)

的可能对齐方式struct foo为 8,而 的可能对齐方式struct bar为 4。因此,如果您这样做:

struct bar b;
union foobar *fb = (union foobar *)&b;
Run Code Online (Sandbox Code Playgroud)

您可能会遇到对齐问题。C11第 6.3.2.3p7 节规定:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。否则,当再次转换回来时,结果应等于原始指针。当指向对象的指针转换为指向字符类型的指针时,结果指向该对象的最低寻址字节。结果的连续增量,直到对象的大小,产生指向对象的剩余字节的指针。

因此,如果b未在 8 字节边界上对齐,则会出现未定义的行为。