1. 程式人生 > >C++語言學習(十二)——C++語言常見函數調用約定

C++語言學習(十二)——C++語言常見函數調用約定

調用函數 操作 開發 混合 類成員 修飾 fast 順序 處理

C++語言學習(十二)——C++語言常見函數調用約定

一、C++語言函數調用約定簡介

C /C++開發中,程序編譯沒有問題,但鏈接的時候報告函數不存在,或程序編譯和鏈接都沒有錯誤,但只要調用庫中的函數就會出現堆棧異常等現象。上述現象出現在C和C++的代碼混合使用的情況下或在C++程序中使用第三方庫(非C++語言開發)的情況下,原因是函數調用約定(Calling Convention)和函數名修飾(Decorated Name)規則導致的。函數調用約定決定函數參數入棧的順序,以及由調用者函數還是被調用函數負責清除棧中的參數等問題,而函數名修飾規則決定編譯器使用何種名字修飾方式來區分不同的函數,如果函數之間的調用約定不匹配或者名字修飾不匹配就會產生以上的問題。

C++語言中的函數調用約定主要針對三個問題:
A、函數參數的入棧順序
B、清理棧的主體(負責清理棧的主體:函數自身還是調用函數者)
C、函數名稱重整
調用約定主要是指函數被調用的方式,C++語言的函數調用約定主要有stdcall,fastcall,pascal,cdecl,thiscall等約定。
在C++中,為了允許操作符重載和函數重載,C++編譯器通常按照某種規則改寫每一個入口點的符號名,以便允許同一個名字(具有不同的參數類型或者是不同的作用域)有多個用法,而不會打破現有的基於C的鏈接器。這項技術通常被稱為名稱改編(Name Mangling)或者名稱修飾(Name Decoration)。C++編譯器廠商通常選擇自己的名稱修飾方案。

二、C++語言常見函數調用約定

1、__stdcall

__stdcall是StandardCall的縮寫,是C++的標準調用方式。__stdcall調用約定的規則如下:
A、所有參數從右到左依次入棧,如果是調用類成員的話,最後一個入棧的是this指針。
B、被調用函數自動清理堆棧,返回值在EAX。
C、函數修飾名約定:VC將函數編譯後會在函數名前面加上下劃線前綴,在函數名後加上"@"和參數的字節數。

2、__cdecl

__cdecl是C DECLaration的縮寫(declaration,聲明),表示C語言默認的函數調用方法。__cdecl調用約定規則如下:
A、所有參數從右到左依次入棧

B、所有參數由調用者清除,稱為手動清棧。返回值在EAX中
C、函數修飾名約定:VC將函數編譯後會在函數名前面加上下劃線前綴?
由於由調用者清理棧,所以允許可變參數函數存在,如int sprintf(char buffer,const char format,...)。

3、__fastcall

__fastcall是快速調用約定,通過寄存器來傳送參數。__fastcall調用約定的規則如下:
? A、用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送?
? B、被調用函數在返回前清理傳送參數的內存棧?,返回值在EAX中
? C、函數修飾名約定:VC將函數編譯後會在函數名前面加上"@"前綴,在函數名後加上"@"和參數的字節數?。

4、thiscall

thiscall是唯一一個不能明確指明的函數修飾符,thiscall只能用於C++類成員函數的調用,同時thiscall也是C++成員函數缺省的調用約定。由於成員函數調用還有一個this指針,因此必須特殊處理。
thiscall調用約定如下:
A、采用桟傳遞參數,參數從右向左入棧。如果參數個數確定,this指針通過ECX傳遞給被調用者;如果參數個數不確定,this指針在所有參數壓棧後被壓入堆棧。
B、對參數個數不定的,調用者清理堆棧,否則由被調函數清理堆棧
thiscall?不是關鍵字,程序員不能使用。

5、__pascal

__pascal?語言的調用約定,跟?__stdcall?一樣,參數按照從右至左的方式入棧,函數自身清理堆棧,返回值在EAX中。VC?中已經廢棄,建議使用?stdcall?代替。

C++語言學習(十二)——C++語言常見函數調用約定