我想弄清楚,在C语言中,超级类 struct 是否被大量包含在子类 struct 中,而不仅仅是具有相同成员前缀的子类和超级类.
在下面的示例代码中,我试图阐明我的 idea :
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
enum type {
IS_A,
IS_B,
};
struct header {
enum type type;
};
struct a {
struct header hdr;
float x;
};
struct b {
struct header hdr;
int y;
};
void do_with_a(struct header *obj) {
if (obj->type == IS_A) {
struct a *a = (struct a *)obj;
printf("%f\n", a->x);
}
}
void do_with_b(struct header *obj) {
// Oops forgot to check the type tag
struct b *b = (struct b *)obj;
printf("%d\n", b->y);
}
int main() {
struct a *a = malloc(sizeof(*a));
a->hdr.type = IS_A;
a->x = 3.0;
do_with_a(&a->hdr);
do_with_b(&a->hdr);
}
我有理由相信,如果用"a"来称呼do_with_b()
,它将永远是未定义的行为.My primary question是指do_with_a()
是否总是定义为行为(假设我正确设置了类型标记),还是在编译器作者改变主意或改进分析时,这会给自己带来灾难.
作为一个sub-question:我认为将struct a *
转换成struct header *
乘&ap->hdr
或(struct header *)ap
都是很明确的定义,是这样吗?
看看C11标准,似乎有两个相关段落,一个在第6.7.2.1节第15段:
在 struct 对象中,非位字段成员和位字段所在的单元的地址按声明顺序递增.指向经过适当转换的 struct 对象的指针指向其初始成员...
6.5第7段中有一个:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- ...
- 在其成员中包含上述类型之一的聚合或联合类型...
在这两者之间,我不清楚这是对标准的预期解释,还是我太有希望了.
我试过用GCC和clang两种语言编译上述代码,它们在优化打开和关闭时的表现似乎都不一样.然而,当设置为-Wstrict-aliasing=1
时,GCC确实会发出两个向下转换的警告.该语言有些模糊,表示它"可能"打破严格的别名,其中对该标志的描述表明误报非常常见,因此这是不确定的:
undefined_test.c: In function ‘do_with_a’:
undefined_test.c:26:39: warning: dereferencing type-punned pointer might break strict-aliasing rules [-Wstrict-aliasing]
26 | struct a *a = (struct a *)obj;
| ^
undefined_test.c: In function ‘do_with_b’:
undefined_test.c:33:31: warning: dereferencing type-punned pointer might break strict-aliasing rules [-Wstrict-aliasing]
33 | struct b *b = (struct b *)obj;
|
相关问题:will casting around sockaddr_storage and sockaddr_in break strict aliasing
这个被接受的答案的最后部分基本上回答了这个问题,但我似乎并不满意.关于它的大多数 comments 似乎主要涉及"公共前缀"的情况,而不是"嵌套 struct "的情况.我不清楚"嵌套 struct "案是否得到了充分辩护.