stdcall與cdecl的區別

2021-07-25 08:58:43 字數 3634 閱讀 9832

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呼叫...