这种类型是否定义明确?

时间:2016-06-23 19:50:22

标签: c++ undefined-behavior type-punning

this answer中阅读关于严格别名规则的引用,我在C ++ 11中看到以下内容:

  

如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:

     
      
  • ...

  •   
  • 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),

  •   
  • ...

  •   

所以我认为以下代码不会破坏严格的别名规则:

#include <iostream>
#include <cstdint>
#include <climits>
#include <limits>

struct PunnerToUInt32
{
    std::uint32_t ui32;
    float fl;
};

int main()
{
    static_assert(std::numeric_limits<float>::is_iec559 &&
                  sizeof(float)==4 && CHAR_BIT==8,"Oops");
    float x;
    std::uint32_t* p_x_as_uint32=&reinterpret_cast<PunnerToUInt32*>(&x)->ui32;
    *p_x_as_uint32=5;
    std::cout << x << "\n";
}

好的,满足严格的别名规则。由于任何其他原因,这仍然表现出未定义的行为吗?

2 个答案:

答案 0 :(得分:4)

您无法执行此操作:&reinterpret_cast<PunnerToUInt32*>(&x)

关于reinterpret_cast州的规则:

  

当对动态类型为DynamicType的对象的指针或引用为reinterpret_cast(或C样式转换)指针或对不同类型AliasedType的对象的引用时, cast总是成功,但是如果满足下列条件之一,结果指针或引用可能只用于访问对象:

     
      
  • AliasedType是(可能是cv资格的)DynamicType
  •   
  • AliasedTypeDynamicType都是(可能是多级,可能是每个级别的cv限定)指向同一类型的指针T
  •   
  • AliasedTypeDynamicType
  • 的(可能是cv认证的)已签名或无符号变体   
  • AliasedType是聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归地,包含联合的子聚合和非静态数据成员的元素) :这使得在给定指向其非静态成员或元素的指针的情况下获取指向结构或联合的可用指针是安全的。
  •   
  • AliasedTypeDynamicType
  • 的(可能是cv限定的)基类   
  • AliasedTypecharunsigned char:这允许将任何对象的对象表示检查为unsigned char
  • 的数组   

因为DynamicType floatAliasedType PunnerToUInt32的组合中没有一个是真的,指针可能不会被用来访问对象,你是这样做。使行为未定义。

有关详细信息,请参阅:Why Doesn't reinterpret_cast Force copy_n for Casts between Same-Sized Types?

修改

分解4个 th 项目符号大小块产生:

  1. AliasedType”“以下为PunnerToUInt32
  2. “是一种汇总类型或工会类型”由于PunnerToUInt32符合aggregate type的资格要求,因此符合条件:

      
        
    • 数组类型
    •   
    • 类类型(通常为structunion),具有      
          
      • 没有私人或受保护的非静态数据成员
      •   
      • 没有用户提供的构造函数,包括从公共基础继承的构造函数(允许显式默认或删除构造函数)
      •   
      • 没有虚拟,私有或受保护的基类
      •   
      • 没有虚拟成员函数
      •   
    •   
  3. “将上述类型之一保存为元素或非静态成员(包括递归地,包含的联合的子聚合和非静态数据成员的元素)”再次{{1因为它是PunnerToUInt32成员

  4. 而有资格
  5. “这样可以安全地获取指向结构或联合的可用指针”
    这是最后正确的部分,因为float flAliassedType
  6. “给出指向其非静态成员或元素的指针”
    这是违规行为,因为PunnerToUInt32 DynamicType不是x的成员< / LI>

    由于违反第5部分对此指针的操作是未定义的行为。

    如果你关心一些推荐的阅读,你可以查看Empty Base Optimization,如果没有,我会在这里给你主要的相关性:

      

    StandardLayoutTypes需要空基优化,以保持指向使用PunnerToUInt32转换的标准布局对象的指针指向其初始成员的要求

    因此,您可以通过这样做来利用reinterpret_cast的4 th 项目符号:

    reinterpret_cast

    Live Example

答案 1 :(得分:0)

如果p_x_as_uint32以某种方式指向x 1 ,则*p_x_as_uint32=5将通过类型为{的glvalue访问类型为float的对象{1}},这将导致未定义的行为。

有争议的“访问”是赋值,所有重要的是使用的glvalue的类型(uint32_t)和访问的对象的实际类型(uint32_t)。用于获取指针的受折磨的一系列演员阵容无关紧要。

值得记住的是,存在严格的别名规则以启用基于类型的别名分析。然而,所采取的路线受到折磨,if you can legally "create a situation where an int* and a float* can simultaneously exist and both can be used to load or store the same memory, you destroy TBAA"。如果您认为标准的措辞允许您这样做,那么您可能错了,但如果您是对的,那么您所发现的只是标准措辞中的缺陷。

1 类成员访问是未定义的行为(通过省略),因为没有实际的float对象。但是,类成员访问权限不是严格别名规则含义内的"access";后者的意思是“读取或修改对象的值”。