C++ 为什么强制转换到指针,然后取消引用?
我正在看这个例子,它有一个输出十六进制位模式的函数来表示任意浮点C++ 为什么强制转换到指针,然后取消引用?,c++,c,casting,C++,C,Casting,我正在看这个例子,它有一个输出十六进制位模式的函数来表示任意浮点 void ExamineFloat(float fValue) { printf("%08lx\n", *(unsigned long *)&fValue); } 为什么要取fValue的地址,强制转换为无符号长指针,然后取消引用?难道所有这些工作都不等同于对未签名long进行直接转换吗 printf("%08lx\n", (unsigned long)fValue); 我试过了,结果不一样,很困惑 C中的类型
void ExamineFloat(float fValue)
{
printf("%08lx\n", *(unsigned long *)&fValue);
}
为什么要取fValue的地址,强制转换为无符号长指针,然后取消引用?难道所有这些工作都不等同于对未签名long进行直接转换吗
printf("%08lx\n", (unsigned long)fValue);
我试过了,结果不一样,很困惑 C中的类型转换同时进行类型转换和值转换。浮点→ 无符号长转换截断浮点数的小数部分,并将该值限制在无符号长转换的可能范围内。从一种类型的指针转换到另一种类型的指针时,不需要更改值,因此使用指针类型转换是一种在更改与该表示关联的类型时保持内存中表示形式不变的方法
在这种情况下,这是一种能够输出浮点值的二进制表示形式的方法。浮点值具有内存表示形式:例如,字节可以使用 第一个表达式
*(unsigned long*)&fValue
将解释这些字节,就像它是unsigned long
值的表示一样。事实上,在C标准中,它会导致未定义的行为(根据所谓的“严格别名规则”)。在实践中,有一些问题需要考虑,例如持久性
第二个表达式(无符号长)fValue
符合C标准。它有一个确切的含义:
C11(n1570),§6.3.1.4实浮点和整数
当实浮点类型的有限值转换为除\u Bool
以外的整数类型时,小数部分被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义
这将根据“常用算术转换”将浮点
值转换为无符号长
值
这里的目的是获取存储fValue
的地址,假设该地址不是float
,而是无符号long
,然后读取该无符号long
。目的是检查用于在内存中存储浮点值的位模式
如图所示,这会导致未定义的行为。
原因:您可能无法通过指向与对象类型“不兼容”的类型的指针访问对象。“兼容”类型包括(unsigned
)char
和其他所有类型,或者共享相同初始成员的结构(这里指的是C)。有关详细的(C11)列表,请参见§6.5/7(请注意,我对“兼容”的使用与参考文本中的不同-更广泛。)
解决方案:强制转换为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;
}
无符号长模式=0;
无符号字符*访问=(无符号字符*)&fValue;
对于(大小i=0;i*(unsigned long*)&fValue
不等同于对unsigned long
的直接强制转换
转换为(无符号长)fValue
使用将浮点值转换为无符号长
值的常规规则,将fValue
的值转换为无符号长
值。该值在无符号长
中的表示形式(例如,以位表示)可能与相同值在浮点中的表示方式大不相同
转换*(unsigned long*)&fValue
形式上具有未定义的行为。它将fValue
占用的内存解释为一个unsigned long
。实际上(即,这是经常发生的情况,即使行为未定义)正如其他人已经指出的,这通常会产生一个与fValue
完全不同的值,将指向非字符类型的指针转换为指向不同非字符类型的指针,然后取消引用是未定义的行为
printf(“%08lx\n”、*(unsigned long*)&fValue)
调用未定义的行为并不一定意味着运行一个试图执行这种滑稽行为的程序将导致硬盘驱动器被擦除或鼻部恶魔从鼻部喷发(未定义行为的两个标志)(unsigned long)=sizeof(float)
并且这两种类型都有相同的对齐要求,printf
几乎肯定会做人们期望它做的事情,即打印所讨论的浮点值的十六进制表示
这并不奇怪。C标准公开邀请实现来扩展该语言。严格来说,这些扩展中的许多都是在未定义行为的领域。例如,POSIX函数返回一个void*
,但该函数通常用于查找函数的地址,而不是全局变量ble。这意味着需要将dlsym
返回的无效指针转换为函数指针,然后取消引用以调用该函数。这显然是未定义的行为,但它在任何符合POSIX的平台上都可以工作。这在哈佛体系结构的计算机上不起作用,在该计算机上,指向函数的指针大小不同汉做数据指针
类似地,将指向浮点
的指针强制转换为指向无符号整数的指针,然后在几乎所有具有编译器的计算机上进行解引用,其中该无符号整数的大小和对齐要求与浮点
的大小和对齐要求相同
也就是说,使用无符号long
可能会给您带来麻烦
*(unsigned long *)&fValue
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;
}
typedef struct {
float fval;
uint32_t ival;
} float_uint32_t;
printf ("%a\n", fValue);