观海听涛
Waiting for you

不以善小而不为

26 Aug 2013

最近在做项目的时候遇到一个小问题,简单描述如下:

代码:

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来调试。

总结:这是一个基础问题,但有时候恰恰是基础问题容易让人摸不着头脑。万丈高楼平地起,只有一层一层的建踏实,这栋楼才是健壮的,而建成高楼只是迟早的问题。