随笔-16  评论-116  文章-0  trackbacks-0
转载请注明出处: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原内容按照原来的顺序排在'@'之后.

void insert(char *str, char tmp)
{
     
//填写代码:
}

void main(void)
{
    
char ptr[16]="abcdefg";
    
char temp='@';
    insert(ptr, temp);
    printf(
"%s\n;",ptr);
}


我很容易想到
void insert(char *str, char tmp)
{
    
*((__int64*)(str + 1)) = *(__int64*)str;
    
*str = tmp;
}

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

 100401030  /$  A1 DCB64000   mov     eax, dword ptr [40B6DC]  
 200401035  |?  8945 EC       mov     dword ptr [ebp-14], eax
 300401038  |?  8B0D E0B64000 mov     ecx, dword ptr [40B6E0]
 40040103E  |?  894D F0       mov     dword ptr [ebp-10], ecx
 500401041  |?  33D2          xor     edx, edx                         ;  namespac.0040E2B8
 600401043  |?  8955 F4       mov     dword ptr [ebp-C], edx
 700401046  |?  8955 F8       mov     dword ptr [ebp-8], edx
 800401049  |?  C645 EB 40    mov     byte ptr [ebp-15], 40
 90040104D  |?  0FB645 EB     movzx   eax, byte ptr [ebp-15]
1000401051  |.  50            push    eax
1100401052  |?  8D4D EC       lea     ecx, dword ptr [ebp-14]
1200401055  |?  51            push    ecx
1300401056  |.  E8 A5FFFFFF   call    00401000
140040105B  |?  83C4 08       add     esp, 8
第一行,0x04B6DC就是常量字符串"abcdefg"的地址,把分2次每次4个送入栈,完成char ptr[16]的初始化,第8 9行是把'@'放入eax,第10行把最后一个参数入栈,也就是@,11行把ebp-14也就是ptr传给ecx,12行把ptr入栈,也就是倒数第二个参数,然后调用下面的函数。

 100401000  /$  55            push    ebp
 200401001  |.  8BEC          mov     ebp, esp
 300401003  |.  8B45 08       mov     eax, dword ptr [ebp+8]
 400401006  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]
 500401009  |?  8B11          mov     edx, dword ptr [ecx]
 60040100B  |.  8950 01       mov     dword ptr [eax+1], edx
 70040100E  |?  8B49 04       mov     ecx, dword ptr [ecx+4]
 800401011  |?  8948 05       mov     dword ptr [eax+5], ecx
 900401014  |?  8B55 08       mov     edx, dword ptr [ebp+8]
1000401017  |.  8A45 0C       mov     al, byte ptr [ebp+C]
110040101A  |.  8802          mov     byte ptr [edx], al
120040101C  |?  5D            pop     ebp
130040101D  |.  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双精度左移指令(为什么是左移不是右移?同样注意高低位),保证数据不会丢失
void insert(char *str, char tmp)
{
    
*(__int64*)str <<= 8;
    
*str = tmp;
}
运行,结果正确

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


by greatws
posted on 2008-09-05 22:22 greatws 阅读(3318) 评论(4)  编辑 收藏 引用

评论:
# re: 32位系统上使用64位变量需要特别注意 2008-09-06 00:24 | clear
*((__int64*)(str + 1)) = *(__int64*)str;

这个可是即使在64位系统上,应该也不能保证正确的代码吧...
_int64数据在特定系统上都是有特定对齐要求的,不能简单的把一个地址转换到_int64*的说  回复  更多评论
  
# re: 32位系统上使用64位变量需要特别注意 2008-09-06 00:26 | clear
就是_int32*也是有对齐要求的,只不过x86系统把这个对齐要求降低到1罢了
在很多嵌入式系统里面,也是要求4字节对齐32位数据的说  回复  更多评论
  
# re: 32位系统上使用64位变量需要特别注意[未登录] 2008-09-06 14:05 | megax
这么做的意义是什么呢?这么做肯定是非法操作啊,没看懂。。。  回复  更多评论
  
# re: 32位系统上使用64位变量需要特别注意 2008-09-07 14:47 | 陈梓瀚(vczh)
编译器的bug……  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理