一个对象可以有多个有效类型吗?

时间:2016-08-05 08:07:32

标签: c language-lawyer strict-aliasing

在ABI未将填充插入联合的平台上考虑以下代码:

union { int xi; } x;
x.xi = 1;

我认为第二行显示未定义的行为,因为它违反了严格的别名规则:

  1. x.xi引用的对象与x引用的对象相同。两者都是相同的存储区域,术语对象在ISO 9899:2011§3.15中定义为:

      

    对象

         

    执行环境中的1个数据存储区域,其内容可以表示值

         

    2注意引用时,对象可能被解释为具有特定类型;见6.3.2.1。

    由于对象只是一个存储区域,因此我得出结论,由于xx.xi占用相同的存储空间,因此它们是同一个对象。

  2. x有效类型union { int xi; },因为它是声明的类型。见§6.5¶6:

      

    6用于访问其存储值的对象的有效类型是对象的声明类型(如果有)。 87)如果存储了一个值通过具有非字符类型的左值的没有声明类型的对象,左值的类型成为该访问的对象的有效类型以及不修改存储值的后续访问。如果使用memcpymemmove将值复制到没有声明类型的对象中,或者将其复制为字符类型数组,则为该访问和后续访问修改对象的有效类型不修改值的是从中复制值的对象的有效类型(如果有的话)。对于没有声明类型的对象的所有其他访问,对象的有效类型只是用于访问的左值的类型。

               

    87)分配的对象没有声明的类型。

    根据¶6的措辞,显然每个对象只能有一种有效类型。

  3. 在语句x.xi中,我通过左键x键入x.xi来访问int。这不是§6.5¶7中列出的类型之一:

      

    7对象的存储值只能由具有以下类型之一的左值表达式访问: 88)

         
        
    • 与对象的有效类型兼容的类型
    •   
    • 与对象的有效类型兼容的类型的限定版本,
    •   
    • 对应于有效类型的有符号或无符号类型的类型   对象,
    •   
    • 对应于合格版本的有符号或无符号类型的类型   有效的对象类型,
    •   
    • 聚合或联合类型,其中包含上述类型之一   成员(包括,递归地,子集合或包含的联合的成员),或
    •   
    • 字符类型。
    •   
               

    88)此列表的目的是指定对象可能或可能没有别名的情况。

  4. 因此,第二行显示未定义的行为。

  5. 由于这种解释显然是错误的,我对标准的误读在哪里?

2 个答案:

答案 0 :(得分:5)

错误认为xx.xi是同一个对象。

union是一个对象,它包含成员对象 1 。它们是不同的对象,每个对象都有自己的类型。

1。(引用自:ISO / IEC 9899:20x1 6.2.5 20型)
联合类型描述了一组重叠的非成员对象,每个成员对象 它有一个可选的指定名称,可能是不同的类型。

答案 1 :(得分:3)

除了禁止使用指针访问其他类型的东西的规则之外,术语"对象"指的是连续的存储分配。自动或静态持续时间的每个单独变量都是一个独立的对象(因为实现可以在整个内存中任意分散它们),但malloc创建的任何内存区域都是单个对象 - 实际上类型为" char []&# 34;,无论其中的内容被索引和访问多少种不同的方式。

如果除了字符指针类型的特殊规则之外,对于字符数组类型的适当对齐的对象,或者对于没有声明的对象,存在相应的规则,则可以使关于指针类型访问的C89规则可行。有效的类型" char []"。以这种方式解释规则会将其应用程序限制为已声明类型的对象。这将允许大多数在1989年实际应用的优化,但随着编译器变得越来越复杂,能够对分配的存储应用类似的优化变得更加可取;由于没有为此制定规则,因此对于允许或不允许的内容几乎没有明确的说明。

到1999年,基于指针的种类之间存在很大的重叠 访问一些需要执行的程序,以及基于指针的访问类型 编译器假设程序不会这样做,所以任何一个C99标准 会要求某些C99实现效率降低 比起过去,或者让C99编译器随意使用 大型代码集依赖于某些编译器没有的技术 支持。

C99的作者,而不是通过定义解决问题 指令指定不同的别名模式,试图"澄清"它 通过添加要求应用不同定义的语言 "对象"从其他地方使用的那个,或者要求每个分配 区域包含一个单一类型的阵列或单个结构 可能包含一个灵活的阵列成员。后一种限制可能是有用的 在从头开始设计的语言中,但实际上会失效 大量的C代码。然而,幸运或不幸的是,标准的作者要摒弃这种草率的起草,因为编译器编写者至少在最近之前对编写有用所需的内容更感兴趣。尽量遵守写得不好的标准。

如果想要编写可与高质量编译器一起使用的代码,请确保以编译器必须忽略忽略的方式完成任何别名(例如,如果函数接收类型T*的参数,将它投射到U*,然后 以U*的形式访问对象,不是钝的编译器应该没有 无法识别该功能可能真正访问T*)。如果有人想编写可以使用最容易被编译的编译器的代码......这是不可能的,因为标准并不要求实现无法处理除可能设计和无用之外的任何事情。程序。如果有人想编写适用于gcc的代码,那么作者支持结构的意愿将远远超过标准对它们所说的内容。