今天在调试代码的时候,碰到一个很奇怪的现象,一条简单的语句,执行结果就是不对。
代码如下
int pack_size = recbuffer[1] + (rec_buffer_[0] << 8)
其中recbuffer为char类型,recbuffer[1]的值是0xd6,recbuffer[0]的值是0x01,预期的结果pack_size应为0x01d6。
根据c++的算术运算隐式转换规则,recbuffer[1]和recbuffer[0]都会被提升为整型,pack_size = 0xd6 + 0x100, 而实际执行结果确是0xd6。令我百思不得其解。
实在是没办法,将代码拆分为如下:
int pack_size = rec_buffer_[0]; pack_size <<= 8; pack_size += rec_buffer_[1];
结果依然一样,还是0xd6,单步跟踪后,发现在pack_size+=recbuffer[1]这里,pack_size += rec_buffer_[1]执行后,pack_size从0x100变为0xd6,高位居然丢失了!
没办法,转到汇编,得到如下代码:
int pack_size = rec_buffer_[0]; 01422A2A mov eax,dword ptr [ebp-14h] 01422A2D movsx ecx,byte ptr [eax+71h] 01422A31 mov dword ptr [pack_size],ecx pack_size <<= 8; 01422A34 mov eax,dword ptr [pack_size] 01422A37 shl eax,8 01422A3A mov dword ptr [pack_size],eax pack_size += rec_buffer_[1]; 01422A3D mov eax,dword ptr [ebp-14h] 01422A40 movsx ecx,byte ptr [eax+72h] 01422A44 add ecx,dword ptr [pack_size] 01422A47 mov dword ptr [pack_size],ecx
终于看到问题所在了,在将recbuffer[1]提升为int类型时,编译器使用了movsx指令,而movsx的含义是:
MOVSX说明:带符号扩展传送指令
符号扩展的意思是,当计算机存储某一个有符号数时,符号位位于该数的第一位,所以,当扩展一个负数的时候需要将扩展的高位全赋为1.对于正数而言,符号扩展和零扩展MOVZX是一样的,将扩展的高位全赋为0.
也即0xd6经过提升为整型后变为0xffffffd6,加上0x100后,正好变成0xd6,造成了高位丢失的假象!
原来问题在于recbuffer的类型为char,这里0xd6是一个负数,将其修改为uchar后,恢复正常。看来在今后的代码中对于类型声明还是要多加注意。
也怪我在调试器中一直将数值显示改为16进制类型,如果显示为10进制,早早可以看到d6是个负数,也不会如此大费周章了。