最近在做项目的时候遇到一个小问题,简单描述如下:
代码:
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来调试。
总结:这是一个基础问题,但有时候恰恰是基础问题容易让人摸不着头脑。万丈高楼平地起,只有一层一层的建踏实,这栋楼才是健壮的,而建成高楼只是迟早的问题。