轉(zhuǎn)載請(qǐng)注明出處:
http://www.shnenglu.com/greatws/archive/2008/09/05/61057.html32位系統(tǒng),eax,ecx,edx,ebx這些寄存器都是32位的,而要使用一個(gè)64位的變量,需要用到2個(gè)寄存器,或者一個(gè)寄存器用到2次,往往在某些地方就會(huì)出現(xiàn)意想不到的問(wèn)題。
今天參加了CSDN的英雄會(huì),有幸見(jiàn)了些名人,回到家上CSDN,看到個(gè)帖子
http://topic.csdn.net/u/20080905/16/3823c75d-c33b-4ea0-83b1-8386d03e6c6c.html
具體內(nèi)容:
題目:
1、不能用庫(kù)函數(shù),要求達(dá)到效率o(1);
2、將符號(hào)'@'插入字符串ptr的首位,字符串ptr原內(nèi)容按照原來(lái)的順序排在'@'之后.
void insert(char *str, char tmp)


{
//填寫(xiě)代碼:
}
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;
}
可是結(jié)果卻很令人驚訝,輸出@abcddfg,有一個(gè)字節(jié)不對(duì)。仔細(xì)一想,應(yīng)該是把64位變量放到2個(gè)寄存器中了。
用OD反一下,看下主函數(shù)里的關(guān)鍵地方,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個(gè)送入棧,完成char ptr[16]的初始化,第8 9行是把
'@'放入eax,第10行把最后一個(gè)參數(shù)入棧,也就是@,11行把ebp
-14也就是ptr傳給ecx,12行把ptr入棧,也就是倒數(shù)第二個(gè)參數(shù),然后調(diào)用下面的函數(shù)。
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個(gè)字節(jié)abcd存入edx,也就是0x64636261,注意高低位
然后把edx里的4個(gè)字節(jié)的數(shù),寫(xiě)入ptr+1的位置,可見(jiàn)問(wèn)題就出現(xiàn)在這里,一下寫(xiě)入4個(gè)字節(jié),在ptr+1到ptr+4的位置,由于*(ptr+4)里的內(nèi)容并未保存,所以被覆蓋了,導(dǎo)致后面第2次讀取的數(shù)據(jù)不正確,最后的結(jié)果也不會(huì)輸出正確
看了下邊網(wǎng)友的回帖,比較好的方法就是用移位,本來(lái)是數(shù),移位肯定不會(huì)出問(wèn)題,使用的是shld雙精度左移指令(為什么是左移不是右移?同樣注意高低位),保證數(shù)據(jù)不會(huì)丟失
void insert(char *str, char tmp)


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