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

问题描述 投票:9回答:3

出于在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'中的B的指针,则(B *) a是合适的转换。

所以问题是:A *是否是指向A'类型对象的有效指针?

根据6.7.6.1A *A' *兼容,如果AA'兼容。

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

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

由于它们不是同一类型,它们是否兼容? 6.2.7中的文字说如果在单独的翻译单元中声明它们是兼容的,但这些是在相同的翻译单元中。

c language-lawyer
3个回答
1
投票

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

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


回覆。根据6.7.2.1/13,你问题中的代码,

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

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

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

为了进一步分析。


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; }

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


0
投票

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

  1. 在决定一种类型的值或指针是否可以被强制转换为另一种类型的值或指针时,不使用铸造操作符而不产生诊断。请注意,为此目的,单独声明的结构即使在结构上相同也是不兼容的,但是当在编译单元之间传递结构或指针时,这种兼容性是无关紧要的。
  2. 在决定是否可以安全地将一个类型的值或指针指向另一个类型的代码时。如果结构相同的类型不被视为与此目的兼容,则在编译单元之间传递未标记的结构是不可能的。编译器过去认为结构相同的类型即使在编译单元中也与此目的兼容,并且编译器在其中一种或两种类型未标记的情况下从未有任何充分的理由,但是因为标准没有强制要求对于编译器而言,通过巧妙地假设指向一个这样的类型的指针不会被用来访问另一个类的成员,它已成为时髦的,从而无意中削弱了语言。

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

© www.soinside.com 2019 - 2024. All rights reserved.