Visual C 中函式呼叫方式淺探

2021-06-17 00:26:26 字數 2623 閱讀 7115

我們知道在進行函式呼叫時,有幾種呼叫方法,分為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之後,沒有相應的堆疊恢復工作(為其它的函式呼叫,所以我沒有列出)

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函式自己恢復堆疊。

如:push edx

push edi

push eax

push ebx

call getdlgitemtexta

你可以想一下,這幾個暫存器中存的都是什麼?

Visual C 中呼叫Windows服務初探

這裡把執行緒的優先順序設到最低,這樣不會耗用過多的系統效能。這個執行緒物件使用threadfunc作為執行緒函式,因此將這個執行緒函式補充完整 public static void threadfunc 安裝完後能過系統的服務管理器你就可以看到你的服務了,只要點選啟動就可以把它啟動,把時間向前改乙個...

函式呼叫方式

cdecl 是c declaration的縮寫,表示c語言預設的函式呼叫方法 所有引數從右到左依次入棧,這些引數由呼叫者清除,稱為手動清棧。被呼叫函式不需要求呼叫者傳遞多少引數,呼叫者傳遞過多或者過少的引數,甚至完全不同的引數都不會產生編譯階段的錯誤。stdcall 是standard call的縮...

函式呼叫方式

現代的程式語言的函式竟然有那麼多的呼叫方式。這些東西要完全理解還得通過彙編 才好理解。他們各自有自己的特點 其實這些呼叫方式的差別在主要在一下幾個方面 1.引數處理方式 傳遞順序,訪問 利用盞還是暫存器 2.函式的結尾處理方式 善後處理 如 棧的恢復由誰恢復?函式內恢復 還是呼叫後恢復 以下是理論 ...