当前位置:  开发笔记 > 编程语言 > 正文

具有"共同初始序列"的联合'双关'结构:为什么C(99+)而不是C++规定了'联合类型的可见声明'?

如何解决《具有"共同初始序列"的联合'双关'结构:为什么C(99+)而不是C++规定了'联合类型的可见声明'?》经验,为你挑选了2个好方法。

我已经通过迷宫找到了解决这个问题的方法,我想我已经对它进行了非常全面的总结.我发布这个作为答案,因为它似乎解释了C语句的(IMO非常误导)意图和C++不继承它的事实.如果我发现进一步的支持材料或情况发生变化,这将随着时间的推移而演变.

这是我第一次尝试总结出一个非常复杂的局面,这似乎不明确的,甚至很多语言的建筑师,所以我会欢迎,关于如何提高这个答案澄清/建议 - 或者只是一个更好的答案,如果任何人有一个.

最后,一些具体的评论

通过隐约相关的线程,我发现下面的答案被@tab -并大加赞赏所包含的链接(照明,如果没有定论),GCC和工作组的缺陷报告:答案通过标签上的StackOverflow

GCC链接包含一些有趣的讨论,并揭示了委员会和编译器供应商的一部分相当大的混淆和相互矛盾的解释 - 围绕C和C++ 中union成员struct,双关语和别名的主题.

最后,我们链接到主要事件 - 另一个BugZilla线程,错误65892,包含一个非常有用的讨论.特别是,我们找到了两个关键文件中的第一个:

C99中添加行的来源

C提案N685是关于union类型声明可见性的附加条款的起源.通过一些声称(参见GCC线程#2)对"公共初始序列"容差的完全误解,N685确实旨在允许放宽structTU内的"公共初始序列" 的别名规则,意识到某些union包含的实例所说的struct类型,正如我们从这句话中看到的那样:

建议的解决方案是要求如果通过公共初始序列(如上所述)的别名是可能的,则可以看到联合声明.因此,如果需要,以下TU提供这种别名:

union utag {
    struct tag1 { int m1; double d2; } st1;
    struct tag2 { int m1; char c2; } st2;
};

int similar_func(struct tag1 *pst2, struct tag2 *pst3) {
     pst2->m1 = 2;
     pst3->m1 = 0;   /* might be an alias for pst2->m1 */
     return pst2->m1;
}

根据海湾合作委员会的讨论和下面的评论,如@ ecatmur's,这个提议 - 似乎要求推测性地允许任何struct类型的别名,在union这个TU 中有一些可见的实例- 似乎已经受到很大的嘲笑,很少被实施.

显而易见的是,如果没有完全削弱许多优化措施来满足对附加条款的这种解释是多么困难 - 几乎没有什么好处,因为很少有编码人员想要这种保证,而那些做的人只能开启fno-strict-aliasing(IMO表明更大的问题).如果实施,这种限额更有可能吸引人们并与其他声明的虚假互动union,而不是有用.

从C++中省略该行

继之以及我在其他地方发表的评论之后,@ Potatoswatter在这里的答案中指出:

可见性部分是故意从C++中省略的,因为它被广泛认为是荒谬和无法实现的.

换句话说,看起来C++故意避免采用这个附加条款,可能是因为它广泛存在的荒谬性.在要求"记录"引用时,Potatoswatter提供了关于线程参与者的以下关键信息:

那次讨论中的人基本上都是"记录在案".Andrew Pinski是一个铁杆GCC后端人.Martin Sebor是一名活跃的C委员会成员.Jonathan Wakely是一名活跃的C++委员会成员和语言/图书馆实施者.该页面比我能写的任何内容都更具权威性,清晰性和完整性.

Potatoswatter,在相同的SO纱线之上相连,得出结论,C++故意排除这条线,离开指针无需特殊处理(或者在最好的,实现定义的处理)到公共初始序列.他们的待遇是否将在未来具体确定,与其他任何指针相比,还有待观察; 与我下面关于C的最后一节相比.目前,它不是(而且,IMO,这是好的).

这对C++和实际的C实现意味着什么?

因此,从N685的邪恶行......" 一边" ......我们又回到了假设指针进入公共初始序列没有特殊的混叠方面.仍然.值得确认的是,没有它,C++中的这一段意味着什么.好吧,上面的第二个GCC线程链接到另一个gem:

C++缺陷1719.该提案已达到 DRWP状态:"DR问题的解决方案反映在当前的工作文件中.工作文件是该标准未来版本的草案" -引用.这是在C++之后的14或者至少在我在这里的最终草案之后(N3797) - 并提出了一个重要的,并且在我看来有启发性地重写了这一段的措辞,如下所示.我正在强调我认为是重要的变化, {这些评论}是我的:

具有活动成员 的标准布局联合中{"active"表示union结构类型实例,而不仅仅是类型}(9.5 [class.union]),T1允许读取 {以前"检查"}非静态数据构件m 另一联合成员的结构类型的T2提供m是共同的初始序列的一部分T1T2.[ 注意:通过非易失性glvalue读取volatile对象具有未定义的行为(7.1.6.1 [dcl.type.cv]). - 尾注]

这似乎澄清旧措辞的含义是:对我来说,它说,任何明确允许中"双关语" union成员structs的公共初始序列,必须做到通过一个实例母公司union -而不是基于的类型structs(例如指向它们的指针传递给某个函数).这个措辞似乎排除了任何其他解释, N685.我会说,C会采取这种做法.嘿,说到哪,见下文!

结果是 - 正如@ecatmur和GCC门票所证明的那样 - 这在C++中定义了这样的union成员struct,实际上在C中,受到与任何其他2个官方无关指针相同的严格别名规则的约束.现在可以更清楚地定义能够读取非活动union成员struct的公共初始序列的明确保证,不包括N685针对C 尝试的模糊且难以想象的繁琐强制执行"可见性" .通过此定义,主要编译器具有一直表现为C++的预期.至于C?

在C++中用C++澄清可能会逆转这一行

同样非常值得注意的是,C委员会成员Martin Sebor也希望用这种优秀的语言来解决这个问题:

Martin Sebor 2015-04-27 14:57:16 UTC如果你们其中一个人可以解释它的问题我愿意写一篇论文并将其提交给WG14并要求更改标准.

Martin Sebor 2015-05-13 16:02:41 UTC上周有机会与Clark Nelson讨论这个问题.Clark过去曾致力于改进C规范的混叠部分,例如在N1520(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm).他同意,就像N1520中指出的问题一样,这也是一个值得WG14重新审视和修复的突出问题."

Potatoswatter鼓舞人心地总结道:

C和C++委员会(通过马丁和克拉克)将试图找到共识并敲定措辞,以便标准最终能说出它意味着什么.

我们只能希望!

再次,欢迎所有进一步的想法.



1> underscore_d..:

我已经通过迷宫找到了解决这个问题的方法,我想我已经对它进行了非常全面的总结.我发布这个作为答案,因为它似乎解释了C语句的(IMO非常误导)意图和C++不继承它的事实.如果我发现进一步的支持材料或情况发生变化,这将随着时间的推移而演变.

这是我第一次尝试总结出一个非常复杂的局面,这似乎不明确的,甚至很多语言的建筑师,所以我会欢迎,关于如何提高这个答案澄清/建议 - 或者只是一个更好的答案,如果任何人有一个.

最后,一些具体的评论

通过隐约相关的线程,我发现下面的答案被@tab -并大加赞赏所包含的链接(照明,如果没有定论),GCC和工作组的缺陷报告:答案通过标签上的StackOverflow

GCC链接包含一些有趣的讨论,并揭示了委员会和编译器供应商的一部分相当大的混淆和相互矛盾的解释 - 围绕C和C++ 中union成员struct,双关语和别名的主题.

最后,我们链接到主要事件 - 另一个BugZilla线程,错误65892,包含一个非常有用的讨论.特别是,我们找到了两个关键文件中的第一个:

C99中添加行的来源

C提案N685是关于union类型声明可见性的附加条款的起源.通过一些声称(参见GCC线程#2)对"公共初始序列"容差的完全误解,N685确实旨在允许放宽structTU内的"公共初始序列" 的别名规则,意识到某些union包含的实例所说的struct类型,正如我们从这句话中看到的那样:

建议的解决方案是要求如果通过公共初始序列(如上所述)的别名是可能的,则可以看到联合声明.因此,如果需要,以下TU提供这种别名:

union utag {
    struct tag1 { int m1; double d2; } st1;
    struct tag2 { int m1; char c2; } st2;
};

int similar_func(struct tag1 *pst2, struct tag2 *pst3) {
     pst2->m1 = 2;
     pst3->m1 = 0;   /* might be an alias for pst2->m1 */
     return pst2->m1;
}

根据海湾合作委员会的讨论和下面的评论,如@ ecatmur's,这个提议 - 似乎要求推测性地允许任何struct类型的别名,在union这个TU 中有一些可见的实例- 似乎已经受到很大的嘲笑,很少被实施.

显而易见的是,如果没有完全削弱许多优化措施来满足对附加条款的这种解释是多么困难 - 几乎没有什么好处,因为很少有编码人员想要这种保证,而那些做的人只能开启fno-strict-aliasing(IMO表明更大的问题).如果实施,这种限额更有可能吸引人们并与其他声明的虚假互动union,而不是有用.

从C++中省略该行

继之以及我在其他地方发表的评论之后,@ Potatoswatter在这里的答案中指出:

可见性部分是故意从C++中省略的,因为它被广泛认为是荒谬和无法实现的.

换句话说,看起来C++故意避免采用这个附加条款,可能是因为它广泛存在的荒谬性.在要求"记录"引用时,Potatoswatter提供了关于线程参与者的以下关键信息:

那次讨论中的人基本上都是"记录在案".Andrew Pinski是一个铁杆GCC后端人.Martin Sebor是一名活跃的C委员会成员.Jonathan Wakely是一名活跃的C++委员会成员和语言/图书馆实施者.该页面比我能写的任何内容都更具权威性,清晰性和完整性.

Potatoswatter,在相同的SO纱线之上相连,得出结论,C++故意排除这条线,离开指针无需特殊处理(或者在最好的,实现定义的处理)到公共初始序列.他们的待遇是否将在未来具体确定,与其他任何指针相比,还有待观察; 与我下面关于C的最后一节相比.目前,它不是(而且,IMO,这是好的).

这对C++和实际的C实现意味着什么?

因此,从N685的邪恶行......" 一边" ......我们又回到了假设指针进入公共初始序列没有特殊的混叠方面.仍然.值得确认的是,没有它,C++中的这一段意味着什么.好吧,上面的第二个GCC线程链接到另一个gem:

C++缺陷1719.该提案已达到 DRWP状态:"DR问题的解决方案反映在当前的工作文件中.工作文件是该标准未来版本的草案" -引用.这是在C++之后的14或者至少在我在这里的最终草案之后(N3797) - 并提出了一个重要的,并且在我看来有启发性地重写了这一段的措辞,如下所示.我正在强调我认为是重要的变化, {这些评论}是我的:

具有活动成员 的标准布局联合中{"active"表示union结构类型实例,而不仅仅是类型}(9.5 [class.union]),T1允许读取 {以前"检查"}非静态数据构件m 另一联合成员的结构类型的T2提供m是共同的初始序列的一部分T1T2.[ 注意:通过非易失性glvalue读取volatile对象具有未定义的行为(7.1.6.1 [dcl.type.cv]). - 尾注]

这似乎澄清旧措辞的含义是:对我来说,它说,任何明确允许中"双关语" union成员structs的公共初始序列,必须做到通过一个实例母公司union -而不是基于的类型structs(例如指向它们的指针传递给某个函数).这个措辞似乎排除了任何其他解释, N685.我会说,C会采取这种做法.嘿,说到哪,见下文!

结果是 - 正如@ecatmur和GCC门票所证明的那样 - 这在C++中定义了这样的union成员struct,实际上在C中,受到与任何其他2个官方无关指针相同的严格别名规则的约束.现在可以更清楚地定义能够读取非活动union成员struct的公共初始序列的明确保证,不包括N685针对C 尝试的模糊且难以想象的繁琐强制执行"可见性" .通过此定义,主要编译器具有一直表现为C++的预期.至于C?

在C++中用C++澄清可能会逆转这一行

同样非常值得注意的是,C委员会成员Martin Sebor也希望用这种优秀的语言来解决这个问题:

Martin Sebor 2015-04-27 14:57:16 UTC如果你们其中一个人可以解释它的问题我愿意写一篇论文并将其提交给WG14并要求更改标准.

Martin Sebor 2015-05-13 16:02:41 UTC上周有机会与Clark Nelson讨论这个问题.Clark过去曾致力于改进C规范的混叠部分,例如在N1520(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm).他同意,就像N1520中指出的问题一样,这也是一个值得WG14重新审视和修复的突出问题."

Potatoswatter鼓舞人心地总结道:

C和C++委员会(通过马丁和克拉克)将试图找到共识并敲定措辞,以便标准最终能说出它意味着什么.

我们只能希望!

再次,欢迎所有进一步的想法.



2> Kaz..:

我怀疑这意味着不仅可以通过联合类型,而且可以在联合之外访问这些公共部分.也就是说,假设我们有这个:

union u {
  struct s1 m1;
  struct s2 m2;
};

现在假设在某个函数中我们有一个struct s1 *p1指针,我们知道这个指针是从m1这种联合的成员中解除的.我们可以将它转换为struct s2 *指针并仍然访问与之相同的成员struct s1.但是在范围的某处,union u必须显示声明.它必须是完整的声明,它通知编译器成员是struct s1struct s2.

可能的意图是,如果范围中存在这样的类型,则编译器知道struct s1并且struct s2是别名的,因此通过struct s1 *指针的访问被怀疑真正访问a struct s2或反之亦然.

如果没有任何可见的联合类型以这种方式连接这些类型,就没有这样的知识; 可以应用严格别名.

由于C++中没有措辞,那么为了利用该语言中的"常见初始成员放松"规则,您必须通过联合类型路由访问,这通常是通常所做的:

union u *ptr_any;
// ...
ptr_any->m1.common_initial_member = 42;
fun(ptr_any->m2.common_initial_member);  // pass 42 to fun


即使在C89模式下,gcc也不再识别通过其各个类型的指针访问的联合成员的公共初始序列规则,即使所讨论的对象是联合,并且使用联合的代码与代码执行的代码在同一个转换单元中成员; 我认为这种编译器行为无法被认为是合法的,因为C89中的任何内容都不会禁止这种用法.
@underscore_d怀疑必须是正确的,因为在什么情况下我们会访问工会成员,这样工会的(完整)声明是*不在范围内?唯一可行的方法是获取指向该成员的指针并将其传递出作用域.而且这种情况并没有被排除在外:它只是受制于仍然在范围内的"联盟"类型.这必须意味着意图是`union`类型通知转换器结构涉及一个联合(所以要小心:指向那些结构类型的指针可能是联盟成员).
@underscore_d事实是,它实际上不是*允许*而是*限制*!从程序员的角度来看,这是一个补贴.但是标准是实现者*的一组要求*(其中许多是根据程序行为给出的).C标准*更严格*:它*要求*实现者在优化时要小心并支持该用法.如果该代码移植到C++,它具有未定义的行为.有效的C是未定义的C++,虽然没有用C++诊断,但对C++来说并不是好兆头.将C转换为C++时,没有人想要*new*UB问题!
@underscore_d:标准的意图很清楚,编译器编写者是否喜欢它.在C89中,从另一个翻译单元接收的代码指向共享CIS并且可能是其他翻译单元中的联合的一部分的两个结构类型,必须将对一个CIS的访问视为对两者的CIS的潜在访问.从实际的角度来看,C99之前的主流编译器一致(你知道有什么例外吗?)认为CIS规则扩展到类型惩罚指针而不仅仅是联合,因为这使得规则更有用......
......更狭隘地解释它没有任何好处.尽管如此,C99的作者忽略了这一点并将该规则视为仅适用于通过联合进行访问,但认识到必须有一种方法可以告诉编译器两种结构类型的CIS可以别名.他们使用包含两种结构的联合的定义作为提供此类通知的手段,而不是发明新的语法.一个程序,它将指向一个转换单元中定义的并集的成员的指针传递给另一个单元,在这种单元中,它们将以这样的方式使用,从而导致CIS中的别名...
@underscore_d:这条规则的目的是允许C89下严格符合的代码很容易在C99下严格符合要求,或者标准的作者愿意禁止严格符合C89的结构而不提供任何替换.我不知道gcc的历史,但我认为它在2005-2009左右的某个地方开始尝试对CIS成员的别名进行攻击.我相信海湾合作委员会从不关心两种结构类型的联盟是否存在决定CIS成员是否可以别名,但那是因为...
......当它总是表现得像工会定义可能存在时,没有义务关心它.相反,好像没有联合定义存在 - 即使一个人确实存在 - 是一个重大改变,无论gcc的维护者声称它不是多么响亮.
@underscore_d这是很多希望:在某些领域,C++比C更宽松.
当然,但在这里我的意思是"忽略",因为GCC和Clang _do_拥有发展良好的优化器,它们利用严格的混叠,并且在"明显声明的'联盟''中没有做出别名的例外".所以这是一种由于并发症/分歧引起的积极无知,而不是通过将该条款视为其他事物的副作用而隐含的一种
@underscore_d:[参见上面的注释]如果结构s1和s2的联合的存在不允许使用s1*来访问s2的公共成员,那么代码如何可移植地访问已知的指针的公共成员如果类型可能具有不同的对齐要求[例如s1包含uint16_t但s2包含一些uint16_t和一些uint64_t],并且如果不是s1的所有实例都满足s2的对齐,则识别s1或s2(但它不知道哪个)?根据我的理解,将未对齐的s1*转换为包含s2的联合类型将是UB,不是吗?
...不符合C99,但可以通过将联合定义复制到第二个翻译单元来使其符合要求.由于没有其他机制可以使这样的C89代码在不进行大量重写的情况下符合标准,我发现很难相信关于可见联合定义的规则并不是为了使这些代码符合规范.虽然我同意一个好的语言需要让编译器知道哪些东西不会混淆,但如果它必须对CIS规则慷慨,那么C就缺乏这方面......
@underscore_d:......这种慷慨不会使C不足.良好的别名分析需要一种方法,通过这种方法,程序可以说"我将使用此类型的内存作为此类型直到另行通知"和"我完成了使用此类型的内存作为此类型"; 为了提高效率,两种通知都应具有指示存储器的当前内容是否具有任何意义的手段.
推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有