最近在做项目的时候遇到一个小问题,简单描述如下:
代码:
int main(int argc, char* argv[])
{
unsigned char i;
char j=128;
i=j;
printf("%02x\n",i);
printf("%02x\n",j);
return 0;
}
输出:
80
ffffff80
分析:
其实很多人看到这儿会嘲笑我这么简单的问题都不知道,这不是显而易见的吗!是的,这个问题很基础,应该是每一个程序猿都应该清楚的。但是我仍然相信身边有很多人可能都没有在意这个问题,也不知道到底为什么会出现这个问题,说到底都是我们学得还不够细不够精,太浮躁,算了,废话不多说,来看看为什么会出现这个问题。
我们知道unsigned char 表数的范围是0-256,char的表数范围是-127-127(通常)。很显然上面的程序范了一个错误,把128赋值给char型的变量,溢出了,一但发生溢出,其行为肯定就和我们预想的不一样。
再进一步分析,128表示成二进制是:1000 0000,这个数据被存放到了变量中,而我们知道char型变量的最高位是表示符号位的,于是计算机把这个变量理解为一个负值。
那么为什么输出会是ffffff80呢,我查看了一下这段代码的汇编形式,如下:
8: unsigned char i;
9: char j=0x82;
00401028 mov byte ptr [ebp-8],82h
10: i=j;
0040102C mov al,byte ptr [ebp-8]
0040102F mov byte ptr [ebp-4],al
11: printf("%02x\n",i);
00401032 mov ecx,dword ptr [ebp-4]
00401035 and ecx,0FFh
0040103B push ecx
0040103C push offset string "%x\n" (0042201c)
00401041 call printf (00401070)
00401046 add esp,8
12: printf("%02x\n",j);
00401049 movsx edx,byte ptr [ebp-8]
0040104D push edx
0040104E push offset string "%x\n" (0042201c)
00401053 call printf (00401070)
00401058 add esp,8
13: return 0;
请注意00401049 movsx edx,byte ptr [ebp-8]这句,意思是取偏移为ebp-8处的一个字节,做符号扩展后放到edx中。所以128就是在这个地方被作为一个有符号数进行了符号扩展,高位全变成1,然后放到edx(32位)中的,那么printf输出的时候自然就是0xffffff80了。由此也看得出来,Printf出来的内容不见得就是内存中实际存储的内容,应该避免用printf来调试。
总结:这是一个基础问题,但有时候恰恰是基础问题容易让人摸不着头脑。万丈高楼平地起,只有一层一层的建踏实,这栋楼才是健壮的,而建成高楼只是迟早的问题。