无类型指针调用错误问题

发布时间 2023-10-26 01:46:06作者: ZTer

关于段老师发的无类型指针的调用错误问题

  • 先声明一下,下面这段话是和段老师讨论过的,并非个人瞎猜,大家可以放心看。

无类型指针的坑?

  • void*类型的指针可以指向任意类型的变量,当我们调用它时,必须先把它强制转换为它所指变量的类型的指针。

这造成了一个问题,那就是这个指针因为指向了不确定类型的变量,而变量所占的字节数和存储方式都是不确定的。

比如说:intfloat 类型占 4 字节,long longdouble 类型占 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 来读取数据。

这一下可不得了,您猜怎么着?出现了两个问题!

  1. 原数据是 double 类型,使用 IEEE 754 标准存储,但是却被按照 int 类型补码的方式读取,这显然会导致错误。
  2. 原数据占 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 正确的类型,让它顺利的把数据拿出来,再做强制转换。