【问题标题】:compatible types vs. strict aliasing rules兼容类型与严格的别名规则
【发布时间】:2013-10-01 09:39:20
【问题描述】:

将一种类型转换为另一种类型是 C 语言中的一种常见策略,这依赖于 C 结构的布局具有一定保证的事实。诸如 GLib 之类的库依赖于它来实现类似继承的面向对象。基本上:

struct Base
{
  int x;
  int y;
};

struct Derived
{
  struct Base b;
  int z;
};

这可以将Base* 指针分配给Derived 对象的地址。

但我也知道“strict aliasing”规则,这是编译器隐含的假设,即不同类型的指针不能指向同一个地址。 (这使编译器能够执行某些优化。)

那么,这两件事是如何调和的呢?许多 C 库,包括 Glib、CPython 等,都使用上述策略在类型之间进行强制转换。他们都是简单地用no-strict-aliasing之类的标志编译吗?

【问题讨论】:

    标签: c


    【解决方案1】:

    在这种情况下没有违反严格别名。 struct Derived 包含 struct Base。语言标准明确允许这种行为。来自 C11 6.7.2.1 结构和联合说明符,第 15 段:

    一个指向结构对象的指针,经过适当的转换,指向它的初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然。

    【讨论】:

    • 如果 struct Derived 实际上并不包含 struct Base,而只是包含与 struct Base 相同的初始数据成员,该怎么办。换句话说,如果struct Derived 被定义为struct Derived { int x; int y; int z; };
    • 那你可能有问题;例如,编译器可能会选择使用不同的填充/打包特性。
    • @Channel72:C 的类型系统(大部分)是名义上的,而不是结构性的,因此拥有相同的成员是不够的;但是,有一个例外——联合中包含的结构的通用初始顺序规则;请注意,如果结构实际上并未包含在这样的联合中,则访问具有“错误”类型的结构在技术上仍然是未定义的行为(由于违反了有效的类型规则),即使编译器不能立即假设严格的别名这样的联合在范围内
    • @CarlNorum:如果您不通过编译指示手动调整填充,则不会有问题,因为在不同的翻译单元中可能存在包含这两种结构的联合,因此编译器总是有以相同的方式布置常见的初始序列
    • 请注意,6.5p7(臭名昭著的“严格别名”规则集)还包含旨在允许此类代码的显式语言:“对象的存储值只能由具有以下类型中的一种: ... 一种聚合或联合类型,在其成员中包含上述类型之一。” (相比之下,6.5p7不包含任何常见初始子序列的异常,无论有无联合,并且一些编译器确实破坏了假设例如sockaddr.sa_family可以别名sockaddr_in.sin_family的代码。)跨度>
    猜你喜欢
    • 2020-08-01
    • 2019-01-29
    • 2015-10-15
    • 2017-02-25
    • 2013-03-11
    • 2018-12-14
    • 2015-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多