无类型指针调用错误问题
关于段老师发的无类型指针的调用错误问题
- 先声明一下,下面这段话是和段老师讨论过的,并非个人瞎猜,大家可以放心看。
无类型指针的坑?
void*
类型的指针可以指向任意类型的变量,当我们调用它时,必须先把它强制转换为它所指变量的类型的指针。
这造成了一个问题,那就是这个指针因为指向了不确定类型的变量,而变量所占的字节数和存储方式都是不确定的。
比如说:int
和 float
类型占 4 字节,long long
和 double
类型占 8 字节;整数采用补码的存储方式,浮点数则遵循 IEEE 754 标准。
void *
:“我指向了一个内存,但是我不知道这里面存了什么类型的数据,调用的时候记得要告诉我正确的类型奥!”
但是写那份代码的同学却告诉了 void *
错误的类型,他的代码如下。
double x = 1.34567;
void * p = &x;
cout << (int)x << endl;
cout << * (int *)p << endl;
可以看出来 p
指针此时指向了一个 double
类型数据的地址,但是却被强制类型转换告知这个地址里存了一个 int
类型的数据,这就是错误的原因。
出错的具体过程?
接上文,这时 p
便会复制一个自己的副本和它自己指向同一个内存,这就是“两盘水煮鱼”的由来。
假设副本叫 p1
,因为被强制转换告知内存里是一个 int
类型的数据,所以 p1
是一个 int *
类型的指针。
然后程序通过解引用 p1
来读取数据。
这一下可不得了,您猜怎么着?出现了两个问题!
- 原数据是
double
类型,使用 IEEE 754 标准存储,但是却被按照int
类型补码的方式读取,这显然会导致错误。 - 原数据占 64 位内存,但是
int
类型只有 32 位。按照int
类型存储的话,会导致高位的 32 位数据因为溢出而丢失。
让我们更进一步,用 IEEE 754 标准来表示 1.34567,它的二进制表示如下:(断开的地方是 32 位)
00111111111101011000011111011101 01000100000100110101010101000111
副本指针 p1
要读取一个 int
类型的数据,只读出来了较低的 32 位,即:
01000100000100110101010101000111
按照二进制补码的规则翻译回十进制,得到:1142117703,也就是那个错误答案了。
错误的修正?
如果想要把 p
指向的数据强制转换为 int
类型输出,应该这么写:
double x = 1.34567;
void * p = &x;
cout << (int)x << endl;
cout << (int)( * (double *) p ) << endl;
具体来说,应该先告诉 p
正确的类型,让它顺利的把数据拿出来,再做强制转换。