轉載請注明出處:
http://www.shnenglu.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)


{
//填寫代碼:
}
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
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)


{
*(__int64*)str <<= 8;
*str = tmp;
}
運行,結果正確
可以看出,在32位系統使用64位變量需要很注意,尤其是在賦值的時候,比如我上邊的例子。往往在一個大工程里,出現這樣的問題,很難查出原因來,因此,需要格外注意。還有在多線程的時候,一個讀一個寫,由于使用2個寄存器,就有可能在一個寫線程操作到一個64位數的32位的時候,線程正好切換到讀線程,導致產生一些奇怪的數據,而且這種奇怪的情況并不是每次運行都能體現出來,造成的損失可想而知。所以對跨線程使用64位變量必須嚴格進行同步。
by greatws
posted on 2008-09-05 22:22
greatws 閱讀(3366)
評論(4) 編輯 收藏 引用