转载请注明出处:
http://www.cppblog.com/greatws/archive/2008/09/05/61057.html32位系统,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)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
//填写代码:
}
void main(void)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
char ptr[16]="abcdefg";
char temp='@';
insert(ptr, temp);
printf("%s\n;",ptr);
}
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
我很容易想到
void insert(char *str, char tmp)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
*((__int64*)(str + 1)) = *(__int64*)str;
*str = tmp;
}
可是结果却很令人惊讶,输出@abcddfg,有一个字节不对。仔细一想,应该是把64位变量放到2个寄存器中了。
用OD反一下,看下主函数里的关键地方,OH,前面分配栈的一句是sub esp,18
1
00401030 /$ A1 DCB64000 mov eax, dword ptr [40B6DC]
2
00401035 |? 8945 EC mov dword ptr [ebp-14], eax
3
00401038 |? 8B0D E0B64000 mov ecx, dword ptr [40B6E0]
4
0040103E |? 894D F0 mov dword ptr [ebp-10], ecx
5
00401041 |? 33D2 xor edx, edx ; namespac.0040E2B8
6
00401043 |? 8955 F4 mov dword ptr [ebp-C], edx
7
00401046 |? 8955 F8 mov dword ptr [ebp-8], edx
8
00401049 |? C645 EB 40 mov byte ptr [ebp-15], 40
9
0040104D |? 0FB645 EB movzx eax, byte ptr [ebp-15]
10
00401051 |. 50 push eax
11
00401052 |? 8D4D EC lea ecx, dword ptr [ebp-14]
12
00401055 |? 51 push ecx
13
00401056 |. E8 A5FFFFFF call 00401000
14
0040105B |? 83C4 08 add esp, 8
第一行,0x04B6DC就是常量字符串"abcdefg"的地址,把分2次每次4个送入栈,完成char ptr[16]的初始化,第8 9行是把
'@'放入eax,第10行把最后一个参数入栈,也就是@,11行把ebp
-14也就是ptr传给ecx,12行把ptr入栈,也就是倒数第二个参数,然后调用下面的函数。
1
00401000 /$ 55 push ebp
2
00401001 |. 8BEC mov ebp, esp
3
00401003 |. 8B45 08 mov eax, dword ptr [ebp+8]
4
00401006 |. 8B4D 08 mov ecx, dword ptr [ebp+8]
5
00401009 |? 8B11 mov edx, dword ptr [ecx]
6
0040100B |. 8950 01 mov dword ptr [eax+1], edx
7
0040100E |? 8B49 04 mov ecx, dword ptr [ecx+4]
8
00401011 |? 8948 05 mov dword ptr [eax+5], ecx
9
00401014 |? 8B55 08 mov edx, dword ptr [ebp+8]
10
00401017 |. 8A45 0C mov al, byte ptr [ebp+C]
11
0040101A |. 8802 mov byte ptr [edx], al
12
0040101C |? 5D pop ebp
13
0040101D |. 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)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
*(__int64*)str <<= 8;
*str = tmp;
}
运行,结果正确
可以看出,在32位系统使用64位变量需要很注意,尤其是在赋值的时候,比如我上边的例子。往往在一个大工程里,出现这样的问题,很难查出原因来,因此,需要格外注意。还有在多线程的时候,一个读一个写,由于使用2个寄存器,就有可能在一个写线程操作到一个64位数的32位的时候,线程正好切换到读线程,导致产生一些奇怪的数据,而且这种奇怪的情况并不是每次运行都能体现出来,造成的损失可想而知。所以对跨线程使用64位变量必须严格进行同步。
by greatws
posted on 2008-09-05 22:22
greatws 阅读(3260)
评论(4) 编辑 收藏 引用