約定stdcall 函式呼叫的三種約定,你都清楚嗎

2021-10-16 05:37:34 字數 1981 閱讀 4394

__cdecl、__stdcall、__fastcall是c/c++裡中經常見到的三種函式呼叫方式。其中__cdecl是c/c++預設的呼叫方式,__stdcall是windows api函式的呼叫方式,只不過我們在標頭檔案裡檢視這些api的宣告的時候是用了winapi的巨集進行代替了,而這個巨集其實就是__stdcall了。

我們知道,函式的呼叫過程是通過函式棧幀的不斷變化實現的:

函式的呼叫,涉及引數傳遞,返回值傳遞,呼叫後返回,這都是通過棧的變化來實現的,對於三種呼叫約定而言:

__cdecl:

c/c++預設方式,引數從右向左入棧,主調函式負責棧平衡。

__stdcall:

windows api預設方式,引數從右向左入棧,被調函式負責棧平衡。

__fastcall:

快速呼叫方式。所謂快速,這種方式選擇將引數優先從暫存器傳入(ecx和edx),剩下的引數再從右向左從棧傳入。因為棧是位於記憶體的區域,而暫存器位於cpu內,故訪問方式快於記憶體,故其名曰「__fastcall」。

下面從例項來認識一下這三種呼叫約定。先來看乙個簡單的不能再簡單的程式了:

三個函式的內容都是一樣的,不同的是使用了三種呼叫的方式。我們先來看看在main函式呼叫三個函式的時候的彙編**:

按照上面說的那樣,__cdecl按照引數從右向左的方式進入棧區,注意fun1()和fun3()的區別,fun1()在call fun1()之後執行了add esp,8。這一操作正是我們前面所說的進行棧的平衡。呼叫函式之前連續進行了兩次push操作將函式所需的實參5和2先後壓入了棧區,呼叫完成後,我們需要恢復呼叫前的狀態,則需調整棧頂指標esp的位置,這一工作由誰來完成就決定了兩種函式呼叫方式__cdecl(主調函式完成)和__stdcall(被調函式完成)的區別。上圖我們看到了__cdecl中由主調函式完成了,那麼__stdcall呢,在被調函式fun3()中,轉向被調函式結尾處的**,我們看到了這一句:

那麼fun1()結尾處又是如何呢?

看到了吧,這個ret指令後面跟沒跟值就決定了函式返回是棧指標esp需要增加的量。這樣,不需要主調函式再呼叫add指令為esp操作平衡棧區,節約了程式的開銷,一條指令開銷小,如果十萬百萬個這樣的呼叫,這個開銷就明顯了。

說完了__cdecl和__stdcall,再來看看__fastcall,如前面圖看到的呼叫時並未使用push指令向棧裡傳引數,而是使用了

mov  edx, 5

mov  ecx, 2

兩條指令。這樣直接將引數傳入暫存器,被調函式在執行的時候直接從暫存器取值即可,省去了從棧裡取出來給暫存器,再從暫存器取出來放入記憶體。

不過,說個題外話,ecx暫存器經常作為計數和c++裡this指標的傳遞媒介。在這種情況下,情況又是怎樣的呢,下次分析c++操作符 new 的時候再予以討論。ecx做計數器時,需要將ecx中儲存的實參先壓入棧區,計數操作完成後再pop出來。如此一來,這個fastcall倒顯得不那麼fast了。

當然,上面所說的這些操作都是由編譯器在背後為我們完成的,開發人員無需關心這些操作,對我們是透明的。不過,知其然更知其所以然方能立於不敗之地!

●輸入m獲取文章目錄

c語言與c++程式設計

分享c/c++技術文章

stdcall函式呼叫約定

被這個關鍵字修飾的函式,其引數都是從右向左通過堆疊傳遞的 fastcall 的前面部分由ecx,edx傳 函式呼叫在返回前要由被呼叫者清理堆疊。這個關鍵字主要見於microsoft visual c c gnu的c c 是另外一種修飾方式 attribute stdcall stdcall是函式呼叫...

呼叫約定 stdcall

cdecl與 stdcall這兩種呼叫約定之間的主要差別在於由誰來執行對引數的清理工作。如果是 cdecl,那麼主調函式將負責執行清理工作,如果是 stdcall那被調函式將負責執行清理。通常,我們傾向於使用 stdcall,它能夠減少生成 的規模。因為不需要每次呼叫時,都在返回後對esp進行操作,...

stdcall函式呼叫約定詳解

對 stdcall 的理解 在c語言中,假設我們有這樣的乙個函式 int function int a,int b 呼叫時只要用result function 1,2 這樣的方式就可以使用這個函式。但是,當高階語言被編譯成計算機可以識別的機器碼時,有乙個問題就凸現出來 在cpu中,計算機沒有辦法知道...