1
區別vc++
的c/c++
函式有兩種基本的呼叫約定:
__stdcall
、__cdecl
__stdcall
__cdecl
函式**
cint__stdcalladds(int a,int b)
int__cdecladdc(int a,int b)
asm32
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
lea edi,[ebp-40h]
mov ecx,10h
mov eax,0cccccccch
rep stos dword ptr [edi]
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0ch]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 8
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
lea edi,[ebp-40h]
mov ecx,10h
mov eax,0cccccccch
rep stos dword ptr [edi]
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0ch]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
函式呼叫
cadds(1,2);
addc(1,2);
asm32
push 2
push 1
call @ilt+85(adds)
push 2
push 1
call @ilt+50(addc)
add esp,8
說明:
1、函式 adds、addc 的呼叫約定分別是__stdcall、__cdecl,它們32位彙編**的不同之處僅僅在於ret 8和ret; 2、
adds(1,2)
與addc(1,2)的32
位彙編**不同之處在於後者多了乙個
add esp,8;3
、暫存器
esp儲存著程式堆疊的棧頂位址
呼叫adds(1,2)
的過程是這樣的:
push 2
、push 1
用來把引數壓入堆疊,壓入了兩個
4位元組的
int,所以
esp的數值會減去
8。然後
call
語句呼叫
adds
的彙編**。形參
a的數值是
dword ptr [ebp+8]
,即push 1
壓入的1
;形參b
的數值是
dword ptr [ebp+0ch]
,即push 2
壓入的2
。ebp
其實是esp
的初始值(
mov ebp,esp
),所以引數a、
b是取自堆疊的。
adds
返回時呼叫的是
ret 8
,亦即返回時會將暫存器
esp的數值加上
8,完成堆疊的清除工作。
呼叫addc(1,2)的過程與adds(1,2)大致相同,不同之處在於ret不會修改暫存器esp,不會完成堆疊的清除工作。在執行add esp,8
時,才會將
esp暫存器的數值加上
8,完成堆疊的清除工作。
簡單的說,就是:__stdcall的函式在返回時會自動清除堆疊中的引數;__cdecl的函式在返回時不會自動清除堆疊中的引數,清除工作由呼叫者完成。
2 函式指標
再看看下面的例子:
函式呼叫
cint(__stdcall*pfns)(int,int)=&adds;
(*pfns)(1,2);
int(__cdecl*pfnc)(int,int)=&addc;
(*pfnc)(1,2);
asm32
mov dword ptr [ebp-14h],offset @ilt+85(adds) (0040105a)
mov esi,esp
push 2
push 1
call dword ptr [ebp-14h]
cmp esi,esp
call _chkesp (00401f1e)
mov dword ptr [ebp-18h],offset @ilt+50(addc) (00401037)
mov esi,esp
push 2
push 1
call dword ptr [ebp-18h]
add esp,8
cmp esi,esp
call _chkesp (00401f1e)
呼叫(*pfns)(1,2)
比呼叫adds(1,2)
多了如下**:
mov esi,esp //
儲存暫存器
esp的數值至暫存器
esi
... ... ...
cmp esi,esp //
檢視暫存器
esp的數值是否變化了
call _chkesp (00401f1e) //
暫存器esp
的數值變化了,提示出錯
也就是說:通過函式指標呼叫函式,會檢查暫存器
esp的數值是否被正常恢復。
如果強制
pfns
指向addc
,然後執行
(*pfns)(1,2);
int(__stdcall*pfns)(int,int)=(int(__stdcall*)(int,int))&addc;
(*pfns)(1,2);
執行時會產生如下錯誤提示,說明暫存器
esp
產生了錯誤:
stdcall 與 cdecl 的區別
stdcall 與 cdecl 的區別 幾乎我們寫的每乙個windows api函式都是 stdcall型別的,首先,需要了解兩者之間的區別 windows的函式呼叫時需要用到棧 stack,一種先入後出的儲存結構 當函式呼叫完成後,棧需要清除,這裡就是問題的關鍵,如何清除?如果我們的函式使用了 c...
stdcall 與 cdecl 的區別
幾乎我們寫的每乙個windows api函式都是 stdcall型別的,首先,需要了解兩者之間的區別 windows的函式呼叫時需要用到棧 stack,一種先入後出的儲存結構 當函式呼叫完成後,棧需要清除,這裡就是問題的關鍵,如何清除?如果我們的函式使用了 cdecl,那麼棧的清除工作是由呼叫者,用...
stdcall與 cdecl的區別
1 stdcall是pascal程式的預設呼叫方式,通常用於win32 api中,函式採用從右到左的壓棧方式,自己在退出時清空堆疊。vc將函式編譯後會在函式名前面加上下劃線字首,在函式名後加上 和引數的位元組數。int f void p f 4 在外部組合語言裡可以用這個名字引用這個函式 2 c呼叫...