是否将未标记结构重新声明为兼容类型?

时间:2018-01-25 14:47:46

标签: c language-lawyer

出于this question所述的目的,我们希望这样做:

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,其中说“指向结构对象的指针,适当转换,指向其初始成员(或者如果该成员是位字段,则为它居住的单位),反之亦然。“

由于struct { int a; }内的B没有名称,我们称之为A'

“适当地”未明确定义。我假设如果A *是指向A'类型的对象的有效指针,则(A *) b执行适当的转换,类似地,如果a是指向位于A'的{​​{1}},然后B是合适的转化。​​

所以问题是:(B *) a是指向A *类型对象的有效指针吗?

如果A'A *兼容,则A' * AA'兼容。

Per 6.7.6.1,“如果类型相同,则两种类型具有兼容类型...此外,如果其标记和成员满足以下要求,则在单独的翻译单元中声明的两种结构,联合或枚举类型是兼容的:如果使用标记声明一个,则使用相同的标记声明另一个。如果两者都在其各自的翻译单元内的任何地方完成,则以下附加要求适用:其成员之间应存在一对一的对应关系,以便每对相应的成员被宣布为兼容类型;如果使用对齐说明符声明该对中的一个成员,则使用等效的对齐说明符声明另一个成员;如果该对的一个成员使用名称声明,则另一个成员使用相同的名称声明。对于两个结构,相应的成员应按相同的顺序声明......“

这些类型不能是6.2.7的相同类型:“不包含标记的结构,联合或枚举类型的每个声明都声明一个不同的类型。”

由于它们不是同一类型,它们是否兼容? 6.7.2.3 5中的文字表示如果在单独的翻译单元中声明它们是兼容的,但它们在同一个翻译单元中。

3 个答案:

答案 0 :(得分:1)

我没有看到标准中的任何内容表明两个struct都是兼容的,因此我会说它们不是。

唯一可以让你在结构之间有限兼容性的是使用union,如6.7.2.1§6中所述:

  

为了简化工会的使用,我们提出了一项特殊保证:如果工会包含   几个结构共享一个共同的初始序列(见下文),如果是联盟   对象当前包含这些结构中的一个,允许检查公共结构   其中任何一个的初始部分都是完整类型的联盟的声明   是可见的。

,比如

typedef struct { int a; } A;
typedef struct { union { struct { int a; }; A export; }; int b; } B;

A *BToA(B *b) { return &b->export; }
B *AToB(A *a) { return (B *) a; }

应该是安全的,但仅限于读取访问:标准并没有真正麻烦地指定“检查”常见的初始序列的含义,但似乎使用它来反对“修改”。

答案 1 :(得分:1)

正如您在问题中所阐述的那样,该标准清楚且明确地表示同一翻译单元中的两个结构定义struct { int a; }声明了两个不兼容的类型。尽管事实上这可能是“怪异的”#34; Compilers have always followed the standard

这对我来说似乎是合理的行为:如果你的项目中碰巧有语义上不相关的结构,巧合地有一个具有相同类型的成员列表,你确实希望编译器拒绝两者之间的意外分配。

重新。根据6.7.2.1/13,

,问题中的代码
  

匿名结构或联合的成员被视为包含结构或联合的成员。

所以我会将B的定义视为等同于:

typedef struct { int a; int b; } B;

用于进一步分析。

答案 2 :(得分:0)

在两种情况下,结构的兼容性是相关的:

  1. 在决定是否可以将一种类型的值或指针强制转换为另一种类型的值或指针时,不使用强制转换运算符且不生成诊断。请注意,为此目的,单独声明的结构即使它们在结构上相同也是不兼容的,但是当在编译单元之间传递结构或指针时,这种兼容性是无关紧要的。

  2. 在决定是否可以安全地将一个类型的值或指针指向另一个类型的代码时。如果结构相同的类型不被视为与此目的兼容,则在编译单元之间传递未标记的结构是不可能的。编译器过去认为结构相同的类型即使在编译单元中也与此目的兼容,并且编译器在其中一种或两种类型未标记的情况下从未有任何充分的理由,但是因为标准没有强制要求对于编译器而言,通过巧妙地假设指向一个这样的类型的指针不会被用来访问另一个人的成员,它已经变得时髦了。#/ p>

  3. 不幸的是,在编写标准时,其作者并不认为明确强制编译器已经在做的所有明显有用的事情以及哪些合理的编译器将继续这样做是不重要的。最终结果是,过去受支持且无争议的有用构造将是不可靠的,除非禁用其他有用的优化。