当询问C中常见的未定义行为时,灵魂比我提到的严格别名规则更加开明.
他们在说什么?
我们最近在大学里开了一个关于多种语言编程特色的讲座.
讲师写下了以下功能:
inline u64 Swap_64(u64 x)
{
u64 tmp;
(*(u32*)&tmp) = Swap_32(*(((u32*)&x)+1));
(*(((u32*)&tmp)+1)) = Swap_32(*(u32*) &x);
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
虽然我完全理解这在可读性方面也是非常差的风格,但他的主要观点是这部分代码在生产代码中运行良好,直到它们实现了高优化级别.然后,代码将什么都不做.
他说,变量的所有赋值tmp都将由编译器优化.但为什么会这样呢?
我知道有些情况下变量需要声明为volatile,这样编译器就不会触及它们,即使他认为它们永远不会被读或写,但我不知道为什么会发生这种情况.
假设我有floatIEEE 754 binary32的保证.给定一个与存储的有效浮点相对应的位模式std::uint32_t,如何float以最有效的标准兼容方式将其重新解释为?
float reinterpret_as_float(std::uint32_t ui) {
return /* apply sorcery to ui */;
}
Run Code Online (Sandbox Code Playgroud)
我有几种方法,我知道/怀疑/假设有一些问题:
通过reinterpret_cast,
float reinterpret_as_float(std::uint32_t ui) {
return reinterpret_cast<float&>(ui);
}
Run Code Online (Sandbox Code Playgroud)
或者等价的
float reinterpret_as_float(std::uint32_t ui) {
return *reinterpret_cast<float*>(&ui);
}
Run Code Online (Sandbox Code Playgroud)
哪个遭受别名问题.
通过union,
float reinterpret_as_float(std::uint32_t ui) {
union {
std::uint32_t ui;
float f;
} u = {ui};
return u.f;
}
Run Code Online (Sandbox Code Playgroud)
这实际上并不合法,因为它只允许从最近写的成员读取.然而,似乎有些编译器(gcc)允许这样做.
通过std::memcpy,
float reinterpret_as_float(std::uint32_t ui) {
float f;
std::memcpy(&f, &ui, 4);
return f;
}
Run Code Online (Sandbox Code Playgroud)
哪种AFAIK是合法的,但复制单个单词的函数调用似乎很浪费,尽管它可能会被优化掉.
通过reinterpret_cast …
c++ standards-compliance type-conversion language-lawyer c++11
下面的代码通过一些位攻击执行快速反平方根操作.该算法可能是由Silicon Graphics在1990年代早期开发的,它也出现在Quake 3中. 更多信息
但是我从GCC C++编译器收到以下警告:解除引用类型惩罚指针将破坏严格别名规则
我应该使用static_cast,reinterpret_cast还是dynamic_cast在这种情况下使用?
float InverseSquareRoot(float x)
{
float xhalf = 0.5f*x;
int32_t i = *(int32_t*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
Run Code Online (Sandbox Code Playgroud) c++ strict-aliasing gcc-warning undefined-behavior type-punning
我已经在Stack Overflow中阅读了很多关于严格别名的QA,但它们都很常见,而且讨论总是倾向于引用C++标准的深层细节,这些细节几乎总是很难理解.特别是在标准时,不要直接说话,而是用泥泞不清楚的方式描述.所以,我的问题可能是这里有大量质量保证的重复,但是,请回答一个具体的问题:
这是做"nonalias_cast"的正确方法吗?:
template<class OUT, class IN>
inline auto nonalias_cast(IN *data) {
char *tmp = reinterpret_cast<char *>(data);
return reinterpret_cast<OUT>(tmp);
}
float f = 3.14;
unsigned *u = nonalias_cast<unsigned *>(&f);
*u = 0x3f800000;
// now f should be equal 1.0
Run Code Online (Sandbox Code Playgroud)
我猜答案是否定的.但是有什么好的解决方法吗?当然,除了禁用严格别名标志.联盟也不是一个方便的选择,除非有一种方法在nonalias_cast函数体内部适合联合黑客.memcpy这里也不是一个选项 - 数据更改应该是同步的.
一个不可能的梦想或一个难以捉摸的现实?
UPD:
好的,既然我们得到了一个否定答案"是否可能?" 问题,我想问你一个困扰我的额外问题:
将如何您解决这个任务?我的意思是有很多实际的任务,更多的是要求"玩一点点"的方法.例如,假设您必须像这样编写IEEE-754浮点转换器.我更关注问题的实际方面:如何有一个解决方法来实现目标?至少在"#$ $的痛苦"方式.