正如其他人已经指出的,将指向非字符类型的指针转换为指向不同非字符类型的指针,然后取消引用是未定义的行为。
printf("%08lx\n", *(unsigned long *)&fValue) 调用未定义的行为并不一定意味着运行一个试图执行此类讽刺的程序会导致硬盘驱动器擦除或使鼻恶魔从鼻子中喷出(未定义行为的两个标志)。在sizeof(unsigned long)==sizeof(float) 和两种类型具有相同对齐要求的计算机上,printf 几乎肯定会做人们期望它做的事情,即打印所讨论的浮点值的十六进制表示。
这不足为奇。 C 标准公开邀请实现来扩展语言。严格来说,这些扩展中的许多都属于未定义行为的领域。例如,POSIX 函数dlsym 返回一个void*,但该函数通常用于查找函数的地址而不是全局变量。这意味着 dlsym 返回的 void 指针需要转换为函数指针,然后取消引用以调用函数。这显然是未定义的行为,但它仍然适用于任何符合 POSIX 的平台。这不适用于哈佛架构机器,在该机器上,函数指针的大小与数据指针的大小不同。
类似地,将指向float 的指针转换为指向无符号整数的指针,然后取消引用恰好适用于几乎任何具有几乎任何编译器的计算机上,其中该无符号整数的大小和对齐要求与float.
也就是说,使用unsigned long 很可能会给您带来麻烦。在我的计算机上,unsigned long 的长度为 64 位,并且具有 64 位对齐要求。这与浮点数不兼容。最好在我的电脑上使用uint32_t。
union hack 是解决这个烂摊子的一种方法:
typedef struct {
float fval;
uint32_t ival;
} float_uint32_t;
分配给 float_uint32_t.fval 并从 ``float_uint32_t.ival` 访问过去是未定义的行为。在 C 中不再是这种情况。据我所知,没有编译器会为联合黑客攻击鼻恶魔。这不是 C++ 中的 UB。这是非法的。在 C++11 之前,兼容的 C++ 编译器必须抱怨才能兼容。
任何更好的解决这个问题的方法是使用 %a 格式,它自 1999 年以来一直是 C 标准的一部分:
printf ("%a\n", fValue);
这很简单、容易、可移植,并且不会出现未定义的行为。这将打印所讨论的双精度浮点值的十六进制/二进制表示。由于printf 是一个古老的函数,所有float 参数在调用printf 之前都转换为double。此转换必须与 1999 版 C 标准完全一致。可以通过调用 scanf 或其姐妹来获取确切的值。