一、“自以为是”引发的错误
1、问题来源:
# include <iostream>
using namespace std;
int main (void)
{
int i = -185;
int * p = &i;
char * q = (char *)p;
char ch = *q;
cout << ch << '\t' << (short)ch << '\t' << (int)ch << endl;
cout << (char)i << '\t' << (short)i << '\t' << i << endl;
return 0;
}
最近在学习C++,闲来无事突发奇想随便写了几行代码,如上所示。本以为输出结果会是:
G -185 -185
G -185 -185
Press any key to continue
而最终结果却是:
G 71 71
G -185 -185
Press any key to continue
2、这种错误想法的来源
如下图:
int x = -185在内存中的二进制存储:
错误出就出在了误以为ch的内存,就是q指向的地址,即0X18FF3C这一个字节,并以此错误为基础认为:
ch 是0100 0111十进制为71即71的ASCII对应字符G;
那么(short)ch当然是占用了0X18FF3C和0X18FF3D两个字节,其二进制为1111 1111 0100 0111,所以结果为-185;
(int)ch则占用了x的所有四个字节内存,结果依然为-185;
自以为是这样,而实际并非如此,即“自以为是而以人为非也”。那么真正的存储形式是怎样的呢?
二、问题的关键所在
1、系统为ch分配的内存在哪?
首先我们来看变量ch的地址测试结果:
注意测试时,由于cout的特性需要在&ch和q前加上(int *),否则输出的就是字符形式的类似于乱码的地址了。由测试结果可以看出,指针q的指向和指针p的指向相同,但是ch的地址并非“自以为是”的q指向的地址。而是下图所示的存储形式:
测试结果:
存储形式:
2、解释所有问题:
这样就可以解释为什么,强制转换x和强制转换ch的输出结果有差异的问题了:
(1)、ch的地址并非q指向的地址,q指向的地址也并非为ch所占有,只是ch通过*q读取了q指向地址的存储值0100 0111,并复制了该二进制到自己所占有的内存之中即0X18FF30。而强制类型转换(short)ch,(int)ch都只读取了ch仅有的0100 0111,所以其值也为71(或者G)。
(2)、对于如何通过ch和*q的区别,我们可以用以下代码具体地完全地区分开(对于C++的cout使用时的细节还不是特别熟悉。所以用C代码测试):
# include <stdio.h>
int main (void)
{
int i = -185;
int * p = &i;
char * q = (char *)p;
char ch = *q;
printf("%d\t%d\t%c\n", i,(short)i, (char)i);
printf("%d\t%d\t%c\n", *p, *((short*)p), *(char *)p);
printf("%d\t%d\t%c\n", *((int *)q), *((short *)q) ,*q);
printf("%d\t%d\t%c\n", (int)ch, (short)ch, ch);
printf("%#x\t%#x\t%#x\n", i,(short)i, (char)i);
printf("%#x\t%#x\t%#x\n", *p, *((short*)p), *(char *)p);
printf("%#x\t%#x\t%#x\n", *((int *)q), *((short *)q) ,*q);
printf("%#x\t%#x\t%#x\n", (int)ch, (short)ch, ch);
return 0;
}
代码运行结果为:
-185 -185 G
-185 -185 G
-185 -185 G
71 71 G
0xffffff47 0xffffff47 0x47
0xffffff47 0xffffff47 0x47
0xffffff47 0xffffff47 0x47
0x47 0x47 0x47
Press any key to continue
q是指向x地址首字节的指针,只要将其类型char * 强制转换成int * ,其特点与属性就与指针p完全相同,也对x有了完全的操作权限。而ch仅仅是x地址首字节存储内容的拷贝,和x没有任何直接关系 。