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 *. 当bar在foobar联合成员中时,根据 6.7.2.1 结构和联合说明符允许。但这里我不知道。
如果未声明为 a 的成员,是否允许将指向bar对象的指针转换为指向对象的指针?foobarbarfoobar
问题不在于它是否可以在特定的编译器中工作。我很确定它适用于当前版本中的所有常见编译器。问题在于它是否是合法的 C 代码。
这是我目前的研究。
来自 C11 的 n1570 草案的参考资料:
6.5.2.3 结构和工会成员 § 6
...如果联合包含多个共享公共初始序列的结构(见下文),并且联合对象当前包含这些结构之一,则允许检查其中任何一个的公共初始部分......
6.7.2.1 结构和联合说明符 § 16
... 一个指向联合对象的指针,经过适当的转换,指向它的每个成员......,反之亦然......
一般来说,没有。
假设您有以下定义:
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 字节边界上对齐,则会出现未定义的行为。