无标签结构体重声明是否为兼容类型?C标准合规分析
咱们直接聚焦核心问题:无标签结构体的重声明是否属于兼容类型?
为了验证这个问题,我写了一段测试代码来探究类型转换的合法性:
typedef struct { int a; } A; typedef struct { struct { int a; }; int b; } B; A *BToA(B *b) { return (A *) b; } B *AToB(A *a) { return (B *) a; }
我们原本期望这段代码里的类型转换符合C 2011标准的6.7.2.1 第15条款:
指向结构体对象的指针经适当转换后,指向其初始成员(若该成员为位域,则指向其所在的存储单元),反之亦然。
这里需要明确:B内部的那个无标签结构体struct { int a; }我们暂时称它为A'。
不过标准里并没有明确定义“适当转换”的具体范围。我们可以先做个假设:如果b是指向A'类型对象的有效指针,那么(A *) b属于适当转换;同理,如果a是指向B中A'的指针,(B *) a也属于适当转换。但这就引出了更关键的问题:A *是否为指向A'类型对象的有效指针?
我们可以结合C标准的条款一步步推导:
- 根据6.7.6.1条款:如果类型
A与A'兼容,那么指针类型A *与A' *就是兼容的。 - 再看6.2.7条款对兼容类型的定义:
若两种类型相同,则它们属于兼容类型……此外,在不同翻译单元中声明的两个结构体、联合体或枚举类型,若其标签和成员满足以下要求则兼容:若其中一个带有标签,另一个必须带有相同标签;若两者在各自翻译单元中均已完成声明,则需满足额外要求:成员需一一对应,每对对应成员的类型兼容;若某成员带有对齐说明符,另一成员需带有等效的对齐说明符;若某成员带有名称,另一成员需带有相同名称;对于结构体,对应成员的声明顺序需一致……
- 而6.7.2.3 第5条款明确规定:
每个未包含标签的结构体、联合体或枚举类型声明,均表示一种不同的类型
由此可知,A和A'并非同一类型——因为它们是同一翻译单元里的两个无标签结构体声明。那它们是否属于兼容类型呢?6.2.7条款里提到的兼容情况仅针对不同翻译单元中的结构体,而本例中两者在同一个翻译单元内,所以完全不满足兼容的条件。
总结下来:同一翻译单元内的无标签结构体声明都是独立的不同类型,彼此不兼容。因此A *和A' *也不兼容,之前代码里的类型转换是否符合标准中的“适当转换”并没有明确的合法性保障。
内容的提问来源于stack exchange,提问作者Eric Postpischil




