unsigned u = *(unsigned*)&x; 是什么意思?在 C++ 中,其中 x 是浮点变量?

Bri*_*oco 1 c++ floating-point unsigned numerical-methods

由于我的数值分析课程考试临近,我正在寻找在 C/C++ 中表示浮点数的实现代码?然后我在github上找到了一行代码。您能告诉我,下面代码片段中第二行的含义是什么,以及它为何如此重要?

float x = ...;
unsigned u = *(unsigned*)&x; 
Run Code Online (Sandbox Code Playgroud)

use*_*522 6

unsigned只是缩写unsigned int并使用 C++ 风格的强制转换该行将转换为

unsigned int u = *reinterpret_cast<unsigned int*>(&x);
Run Code Online (Sandbox Code Playgroud)

但是请阅读下面为什么这在任何一种情况下都会导致未定义的行为。

(我建议不要使用问题中显示的行中的 C 风格转换,因为它们解析为哪种 C++ 风格转换并不明显。)

如果x是一个float变量,那么该行尝试将变量的对象表示重新解释float为 an 的对象表示unsigned int,基本上是将 的float内存重新解释为 an 的内存unsigned int,然后将unsigned int与该表示相对应的值存储在 中u

步骤一步,&x是一个指向x类型的指针float*reinterpret_cast<unsigned int*>(&x)是一个指向 的指针x,但现在是类型unsigned int*。然后*reinterpret_cast<unsigned int*>(&x)应该取消unsigned int*引用指向变量的指针,以从指向的内存位置float检索值,就好像存储在那里的字节代表一个值而不是一个值。最后应该使用该值来初始化它。unsigned intunsigned intfloatunsigned int u = u

float这会导致未定义的行为,因为通过指针访问对象是一种别名冲突unsigned int*。某些编译器具有可以启用此选项的选项(假设 和float具有unsigned int兼容的大小和对齐方式),但标准 C++ 语言本身不允许这样做。

一般来说,每当您看到reinterpret_cast(或可能解析为 a 的 C 风格转换reinterpret_cast)时,如果您不确切知道自己在做什么,则可能会导致未定义的行为。


从 C++20 开始,在没有未定义行为的情况下执行此操作的正确方法是使用std::bit_cast

float x = /*...*/;
auto u = std::bit_cast<unsigned>(x);
Run Code Online (Sandbox Code Playgroud)

或在 C++20 之前使用std::memcpy

float x = /*...*/;
unsigned u;
static_assert(sizeof(u) == sizeof(x));
std::memcpy(&u, &x, sizeof(u));
Run Code Online (Sandbox Code Playgroud)

尺寸验证是std::bit_cast自动完成的。static_assert即使没有 C++20,将and包装memcpy在类似的通用函数中以供重用也可能是个好主意。

这两者仍然要求 的表示x也是 a 的有效表示u。否则,行为仍然是未定义的。我不知道是否有任何 C++ 实现并不适用于这种float -> unsigned情况下的所有值。


另请注意:C 是一种不同的语言。C 中的规则可能很不同。例如,reinterpret_castC 中显然没有(unsigned*)可以解析的强制转换,并且对象模型非常不同。但在这种情况下,C 的别名规则将具有相同的效果。

  • user17732522,另一个陷阱:“unsigned”和“float”的大小不一定相同。 (2认同)