为什么要转换为指针然后取消引用?

bob*_*bay 19 c c++ casting

我正在通过这个例子,它有一个函数输出一个十六进制位模式来表示任意浮点数.

void ExamineFloat(float fValue)
{
    printf("%08lx\n", *(unsigned long *)&fValue);
}
Run Code Online (Sandbox Code Playgroud)

为什么要取fValue的地址,转换为无符号长指针,然后取消引用?是不是所有的工作都等同于直接转换为无符号长?

printf("%08lx\n", (unsigned long)fValue);
Run Code Online (Sandbox Code Playgroud)

我试过了,答案不一样,很困惑.

Dan*_*our 28

(unsigned long)fValue
Run Code Online (Sandbox Code Playgroud)

根据"通常的算术转换",这会将float值转换为值unsigned long.

*(unsigned long *)&fValue
Run Code Online (Sandbox Code Playgroud)

这里的目的是获取fValue存储的地址,假设在该地址中没有float但是unsigned long在该地址,然后读取该地址unsigned long.目的是检查用于存储float内存的位模式.

如图所示,这会导致未定义的行为.

原因:您可能无法通过指向与对象类型"不兼容"的类型的指针来访问对象."兼容"类型例如是(unsigned)char和每个其他类型,或共享相同初始成员的结构(在此处说到C).有关详细(C11)列表,请参阅§6.5/ 7 N1570(请注意,我对"兼容"的使用与参考文本中的使用不同 - 更广泛.)

解决方案:转换为unsigned char *,访问对象的各个字节并组装unsigned long出来:

unsigned long pattern = 0;
unsigned char * access = (unsigned char *)&fValue;
for (size_t i = 0; i < sizeof(float); ++i) {
  pattern |= *access;
  pattern <<= CHAR_BIT;
  ++access;
}
Run Code Online (Sandbox Code Playgroud)

注意(如@CodesInChaos指出的那样)上面将浮点值视为首先存储其最高有效字节("big endian").如果你的系统对浮点值使用不同的字节顺序,你需要调整它(或重新排列上面的字节,unsigned long对你来说更实用).

  • 只要float和整数的字节顺序相同(忽略UB),原始代码就可以工作.您的代码假定为big-endian.我将`memcpy`用于`uint32_t`(以及匹配大小的断言). (5认同)
  • 在C++中允许/定义`reinterpret_cast <unsigned long&>(fValue)`(当然,假设类型大小合适)? (2认同)
  • @celtschk如果实际使用该引用不会算作严格的别名违规,我会非常惊讶. - "T1类型的左值表达式可以转换为对另一个类型T2的引用.结果是左值或者xvalue引用与原始左值相同的对象,但是具有不同的类型.没有创建临时值,没有副本是如果允许类型别名规则"[(src)](http://en.cppreference.com/w/cpp/language/reinterpret_cast),则只能安全地访问生成的引用或转换函数. (2认同)