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