首页 文章

这种类型是否定义明确?

提问于
浏览
3

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 回答

  • 4

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

    问题的"access"是赋值,所有重要的是使用的glvalue的类型( uint32_t )和访问的对象的实际类型( float ) . 用来获得指针的折磨系列演员是无关紧要的 .

    值得记住的是,存在严格的别名规则以启用基于类型的别名分析 . 然而折磨的路线是,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" . 如果您认为发现标准's wording somehow allowed you to do this, you are probably wrong, but if you were right, then all you' ve是标准措辞中的缺陷 .


    1类成员访问是未定义的行为(通过省略),因为没有实际的 PunnerToUInt32 对象 . 但是,类成员访问权限不是严格别名规则含义中的"access";后者意味着"to read or modify the value of an object" .

  • 0

    你不能这样做: &reinterpret_cast<PunnerToUInt32*>(&x)

    关于reinterpret_cast州的规则:

    当动态类型为DynamicType的对象的指针或引用是reinterpret_cast(或C样式强制转换)为指针或对不同类型AliasedType的对象的引用时,强制转换总是成功,但结果指针或引用只能使用如果满足以下条件之一,则访问该对象:AliasedType是(可能是cv限定的)DynamicType AliasedType和DynamicType都是(可能是多级,可能在每个级别的cv限定)指向相同类型的指针T AliasedType是(可能是cv-qualified)动态类型的有符号或无符号变体AliasedType是聚合类型或联合类型,它将上述类型之一保存为元素或非静态成员(包括递归地,子聚合和非静态数据成员的元素)包含的联合):这使得在给定指向其非静态成员或元素的指针的情况下获得指向结构或联合的可用指针是安全的 . AliasedType是DynamicType的一个(可能是cv限定的)基类AliasedType是char或unsigned char:这允许将任何对象的对象表示检查为unsigned char数组

    因为 DynamicType floatAliasedType 的组合中没有一个是真的,所以指针可能不会用于访问您正在执行的对象 . 使行为未定义 .

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

    EDIT:

    打破第4个子弹咬合大小块产生:

    • AliasedType
      这里被认为是 PunnerToUInt32

    • "is an aggregate type or a union type"
      PunnerToUInt32 符合资格,因为它符合aggregate type的资格:

    数组类型类类型(通常是struct或union),没有私有或受保护的非静态数据成员,没有用户提供的构造函数,包括从公共库继承的那些(允许显式默认或删除构造函数)no virtual,private,或受保护的基类没有虚拟成员函数

    • "which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions)"
      再次 PunnerToUInt32 因为它是 float fl 成员而有资格

    • "this makes it safe to obtain a usable pointer to a struct or union"
      这是最后正确的部分因为 AliassedTypePunnerToUInt32

    • "given a pointer to its non-static member or element"
      这是违规行为,因为 DynamicType x 不是 PunnerToUInt32 的成员

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

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

    StandardLayoutTypes需要空基本优化,以便维护指向使用reinterpret_cast转换的标准布局对象的指针指向其初始成员的要求

    因此你可以通过这样做来利用 reinterpret_cast 的第4个子弹:

    PunnerToUInt32 x = {13, 42.0F};
    auto y = reinterpret_cast<PunnerToUInt32*>(&x.ui32);
    

    Live Example

相关问题