32位系统上使用64位变量需要特别注意

转载请注明出处: http://www.cppblog.com/greatws/archive/2008/09/05/61057.html

32位系统,eax,ecx,edx,ebx这些寄存器都是32位的,而要使用一个64位的变量,需要用到2个寄存器,或者一个寄存器用到2次,往往在某些地方就会出现意想不到的问题。

今天参加了CSDN的英雄会,有幸见了些名人,回到家上CSDN,看到个帖子
http://topic.csdn.net/u/20080905/16/3823c75d-c33b-4ea0-83b1-8386d03e6c6c.html
具体内容:

题目:
1、不能用库函数,要求达到效率o(1);
2、将符号'@'插入字符串ptr的首位,字符串ptr原内容按照原来的顺序排在'@'之后.

None.gif void  insert( char   * str,  char  tmp)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif     
//填写代码:
ExpandedBlockEnd.gif
}

None.gif
void  main( void )
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
char ptr[16]="abcdefg";
InBlock.gif    
char temp='@';
InBlock.gif    insert(ptr, temp);
InBlock.gif    printf(
"%s\n;",ptr);
ExpandedBlockEnd.gif}

None.gif

我很容易想到
None.gif void  insert( char   * str,  char  tmp)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
*((__int64*)(str + 1)) = *(__int64*)str;
InBlock.gif    
*str = tmp;
ExpandedBlockEnd.gif}

可是结果却很令人惊讶,输出@abcddfg,有一个字节不对。仔细一想,应该是把64位变量放到2个寄存器中了。
用OD反一下,看下主函数里的关键地方,OH,前面分配栈的一句是sub esp,18

 1 None.gif 00401030    / $  A1 DCB64000   mov     eax, dword ptr [40B6DC]  
 2 None.gif 00401035    |?    8945  EC       mov     dword ptr [ebp - 14 ], eax
 3 None.gif 00401038    |?   8B0D E0B64000 mov     ecx, dword ptr [40B6E0]
 4 None.gif0040103E   |?   894D F0       mov     dword ptr [ebp - 10 ], ecx
 5 None.gif 00401041    |?   33D2          xor     edx, edx                         ;  namespac.0040E2B8
 6 None.gif 00401043    |?    8955  F4       mov     dword ptr [ebp - C], edx
 7 None.gif 00401046    |?    8955  F8       mov     dword ptr [ebp - 8 ], edx
 8 None.gif 00401049    |?   C645 EB  40     mov      byte  ptr [ebp - 15 ],  40
 9 None.gif0040104D   |?   0FB645 EB     movzx   eax,  byte  ptr [ebp - 15 ]
10 None.gif 00401051    | .   50             push    eax
11 None.gif 00401052    |?   8D4D EC       lea     ecx, dword ptr [ebp - 14 ]
12 None.gif 00401055    |?    51             push    ecx
13 None.gif 00401056    | .  E8 A5FFFFFF   call     00401000
14 None.gif0040105B   |?   83C4  08        add     esp,  8
第一行,0x04B6DC就是常量字符串"abcdefg"的地址,把分2次每次4个送入栈,完成char ptr[16]的初始化,第8 9行是把 '@'放入eax,第10行把最后一个参数入栈,也就是@,11行把ebp - 14也就是ptr传给ecx,12行把ptr入栈,也就是倒数第二个参数,然后调用下面的函数。

 1 None.gif 00401000    / $   55             push    ebp
 2 None.gif 00401001    | .  8BEC          mov     ebp, esp
 3 None.gif 00401003    | .  8B45  08        mov     eax, dword ptr [ebp + 8 ]
 4 None.gif 00401006    | .  8B4D  08        mov     ecx, dword ptr [ebp + 8 ]
 5 None.gif 00401009    |?   8B11          mov     edx, dword ptr [ecx]
 6 None.gif0040100B   | .   8950   01        mov     dword ptr [eax + 1 ], edx
 7 None.gif0040100E   |?   8B49  04        mov     ecx, dword ptr [ecx + 4 ]
 8 None.gif 00401011    |?    8948   05        mov     dword ptr [eax + 5 ], ecx
 9 None.gif 00401014    |?   8B55  08        mov     edx, dword ptr [ebp + 8 ]
10 None.gif 00401017    | .  8A45 0C       mov     al,  byte  ptr [ebp + C]
11 None.gif0040101A   | .   8802           mov      byte  ptr [edx], al
12 None.gif0040101C   |?   5D            pop     ebp
13 None.gif0040101D   | .  C3            retn
3 4行把刚才入栈的ptr指针存入eax,ecx
第5行把char ptr[16]的前4个字节abcd存入edx,也就是0x64636261,注意高低位
然后把edx里的4个字节的数,写入ptr+1的位置,可见问题就出现在这里,一下写入4个字节,在ptr+1到ptr+4的位置,由于*(ptr+4)里的内容并未保存,所以被覆盖了,导致后面第2次读取的数据不正确,最后的结果也不会输出正确

看了下边网友的回帖,比较好的方法就是用移位,本来是数,移位肯定不会出问题,使用的是shld双精度左移指令(为什么是左移不是右移?同样注意高低位),保证数据不会丢失
None.gif void  insert( char   * str,  char  tmp)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
*(__int64*)str <<= 8;
InBlock.gif    
*str = tmp;
ExpandedBlockEnd.gif}
运行,结果正确

可以看出,在32位系统使用64位变量需要很注意,尤其是在赋值的时候,比如我上边的例子。往往在一个大工程里,出现这样的问题,很难查出原因来,因此,需要格外注意。还有在多线程的时候,一个读一个写,由于使用2个寄存器,就有可能在一个写线程操作到一个64位数的32位的时候,线程正好切换到读线程,导致产生一些奇怪的数据,而且这种奇怪的情况并不是每次运行都能体现出来,造成的损失可想而知。所以对跨线程使用64位变量必须严格进行同步。


by greatws
posted on 2008-09-05 22:22 greatws 阅读(2570) 评论(4)   编辑  收藏 引用
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值