在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 回答
如果
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" .你不能这样做:
&reinterpret_cast<PunnerToUInt32*>(&x)
关于reinterpret_cast州的规则:
因为
DynamicType
float
和AliasedType
的组合中没有一个是真的,所以指针可能不会用于访问您正在执行的对象 . 使行为未定义 .有关更多信息,请参阅: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的资格:"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"
这是最后正确的部分因为
AliassedType
是PunnerToUInt32
"given a pointer to its non-static member or element"
这是违规行为,因为
DynamicType
x
不是PunnerToUInt32
的成员由于违反了第5部分对此指针的操作是未定义的行为 .
如果你关心一些推荐的阅读,你可以查看Empty Base Optimization如果没有,我会在这里给你主要的相关性:
因此你可以通过这样做来利用
reinterpret_cast
的第4个子弹:Live Example