c-将结构强制转换为包含它的并集合法吗



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

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

#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("%sn", 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;
}

它甚至在没有警告的情况下编译,并给出预期的结果

desc bar
desc bar

这里的要点是与//legal评论。我已将bar *转换为foobar *。当bar位于foobar并集的成员中时,根据6.7.2.1结构和并集说明符允许使用。但在这里我不知道。

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

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


这是我目前的研究。

草案n1570中C11:的参考文献

6.5.2.3结构和工会成员§6

。。。如果并集包含共享一个公共初始序列的几个结构(见下文),如果并集对象当前包含其中一个结构,允许检查公共其中任何一个的开头部分。。。

6.7.2.1结构和并集说明符§16

。。。指向联合对象,经过适当转换,指向其每个成员。。。,反之亦然。。。

我头顶上的一枚严重地雷:

void copydesc(const union foobar * fb, union foobar * fb_copy) {
static_assert(sizeof(*fb) == sizeof(union foobar), "broken compiler");
// NOTE: Buffer-overflow when struct cast smaller than `union foobar`.
memcpy(fb_copy, fb, sizeof(*fb));
}

大多数程序员都不会理解这一点,而很少有人会理解这一问题,因为这太有价值了,不能在上面浪费时间。编译器/静态分析工具可能不会对你的技术提出警告。

一种更安全的方法,仍然不一定是防弹的,是添加一个具有相同的第一共享字段的struct,添加构造函数测试,并首先列出常见的struct,就像您在示例中出色地将它们打包一样,以使其更具可读性。

每当你发现自己在怀疑一些不太常见的构造是否正确时,这是一个坏兆头。可以说,如果你真的知道自己在做什么(这是C的一个特性),这样的构造是有用的,但这会带来重大风险,即独立正确部件的意外连锁反应(如上面的缓冲区溢出)会导致严重问题。

一般来说,没有

假设您有以下定义:

struct foo {
int flag;
double foo;
};
struct bar {
int flag;
int bar;
};
union foobar {
struct foo foo;
struct bar bar;
};

CCD_ 11的可能排列将是8,而CCD_。所以如果你这样做:

struct bar b;
union foobar *fb = (union foobar *)&b;

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

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

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

相关内容

最新更新