stdcall與 cdecl的區別

2022-01-31 22:07:21 字數 3610 閱讀 5738

1、_stdcall是pascal程式的預設呼叫方式,通常用於win32 api中,函式採用從右到左的壓棧方式,自己在退出時清空堆疊。vc將函式編譯後會在函式名前面加上下劃線字首,在函式名後加上"@"和引數的位元組數。 int f(void *p) -->> _f@4(在外部組合語言裡可以用這個名字引用這個函式)

2、c呼叫約定(即用__cdecl關鍵字說明)(the c default calling convention)按從右至左的順序壓引數入棧,由呼叫者把引數彈出棧。對於傳送引數的記憶體棧是由呼叫者來維護的(正因為如此,實現可變引數 vararg的函式(如printf)只能使用該呼叫約定)。另外,在函式名修飾約定方面也有所不同。 _cdecl是c和c++程式的預設呼叫方式。每乙個呼叫它的函式都包含清空堆疊的**,所以產生的可執行檔案大小會比呼叫_stdcall函式的大。函 數採用從右到左的壓棧方式。vc將函式編譯後會在函式名前面加上下劃線字首。

[下面是轉的,原文不知道在哪。。。。]

我們知道在進行函式呼叫時,有幾種呼叫方法,分為c式,pascal式。在c和c++中c式呼叫是預設的,除非特殊宣告。二者是有區別的,下面我們用例項說明一下:  

1.   __cdecl   :c和c++預設呼叫方式  

例子:  

void   input(   int   &m,int   &n);/*相當於void   __cdecl   input(int   &m,int   &n);*/  

以下是相應的彙編**:  

00401068       lea                   eax,[ebp-8]   ;取[ebp-8]位址(ebp-8),存到eax  

0040106b       push                 eax                   ;然後壓棧  

0040106c       lea                   ecx,[ebp-4]   ;取[ebp-4]位址(ebp-4),存到ecx  

0040106f       push                 ecx                   ;然後壓棧  

00401070       call                 @ilt+5(input)   (0040100a);然後呼叫input函式  

00401075       add                   esp,8               ;恢復棧  

從以上呼叫input函式的過程可以看出:在呼叫此函式之前,首先壓棧ebp-8,然後壓棧ebp-4,然後呼叫函式input,最後input函式呼叫 結束後,利用esp+8恢復棧。由此可見,在c語言呼叫中預設的函式修飾_cdecl,由主呼叫函式進行引數壓棧並且恢復堆疊。  

下面看一下:位址ebp-8和ebp-4是什麼?  

在vc的view下選debug   windows,然後選registers,顯示暫存器變數值,然後在選debug   windows下面的memory,輸入ebp-8的值和ebp-4的值(或直接輸入ebp-8和-4),看一下這兩個位址實際儲存的是什麼值,實際上是 變數   n   的位址(ebp-8),m的位址(ebp-4),由此可以看出:在主呼叫函式中進行實參的壓棧並且順序是從右到左。另外,由於實參是相應的變數的引用,也 證明實際上引用傳遞的是變數的位址(類似指標)。  

總結:在c或c++語言呼叫中預設的函式修飾_cdecl,由主呼叫函式進行引數壓棧並且恢復堆疊,實參的壓棧順序是從右到左,最後由主調函式進行堆疊恢復。由於主呼叫函式管理堆疊,所以可以實現變參函式。另外,命名修飾方法是在函式前加乙個下劃線(_).  

2.   winapi   (實際上就是pascal,callback,_stdcall)  

例子:  

void   winapi   input(   int   &m,int   &n);  

看一下相應呼叫的彙編**:  

00401068       lea                   eax,[ebp-8]  

0040106b       push                 eax  

0040106c       lea                   ecx,[ebp-4]  

0040106f       push                 ecx  

00401070       call                 @ilt+5(input)   (0040100a)  

從以上呼叫input函式的過程可以看出:在呼叫此函式之前,首先壓棧ebp-8,然後壓棧ebp-4,然後呼叫函式input,在呼叫函式input之後,沒有相應的堆疊恢復工作(為其它的函式呼叫,所以我沒有列出)  

下面再列出input函式本身的彙編**:(實際此函式不大,但做彙編例子還是大了些,大家可以只看前和後,中間**與此例子無關)  

39:   void   winapi   input(   int   &m,int   &n)  

40:        

004011cc       jmp                   input+18h   (00401128)  

61:  

62:       }  

004011d1       pop                   edi  

004011d2       pop                   esi  

004011d3       pop                   ebx  

004011d4       add                   esp,48h  

004011d7       cmp                   ebp,esp  

004011d9       call                 __chkesp   (004015b0)  

004011de       mov                   esp,ebp  

004011e0       pop                   ebp  

004011e1       ret                   8  

最後,我們看到在函式末尾部分,有ret   8,明顯是恢復堆疊,由於在32位c++中,變數位址為4個位元組(int也為4個位元組),所以彈棧兩個位址即8個位元組。  

由此可以看出:在主呼叫函式中負責壓棧,在被呼叫函式中負責恢復堆疊。因此不能實現變參函式,因為被調函式不能事先知道彈棧數量,但在主調函式中是可以做到的,因為引數數量由主調函式確定。  

下面再看一下,ebp-8和ebp-4這兩個位址實際儲存的是什麼值,ebp-8位址儲存的是n   的值,ebp   -4儲存的是m的值。說明也是從右到左壓棧,進行引數傳遞。  

總結:在主呼叫函式中負責壓棧,在被呼叫函式中負責彈出堆疊中的引數,並且負責恢復堆疊。因此不能實現變參函式,引數傳遞是從右到左。另外,命名修飾方法 是在函式前加乙個下劃線(_),在函式名後有符號(@),在@後面緊跟引數列表中的引數所佔位元組數(10進製),如:void   input(int   &m,int   &n),被修飾成:_input@8  

對於大多數api函式以及視窗訊息處理函式皆用   callback   ,所以呼叫前,主調函式會先壓棧,然後api函式自己恢復堆疊。  

stdcall 與 cdecl 的區別

stdcall 與 cdecl 的區別 幾乎我們寫的每乙個windows api函式都是 stdcall型別的,首先,需要了解兩者之間的區別 windows的函式呼叫時需要用到棧 stack,一種先入後出的儲存結構 當函式呼叫完成後,棧需要清除,這裡就是問題的關鍵,如何清除?如果我們的函式使用了 c...

stdcall 與 cdecl 的區別

幾乎我們寫的每乙個windows api函式都是 stdcall型別的,首先,需要了解兩者之間的區別 windows的函式呼叫時需要用到棧 stack,一種先入後出的儲存結構 當函式呼叫完成後,棧需要清除,這裡就是問題的關鍵,如何清除?如果我們的函式使用了 cdecl,那麼棧的清除工作是由呼叫者,用...

stdcall與cdecl的區別

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