VC預設為__stdcall,
BCB預設為__cdecl,
Delphi預設為__fastcall。
由于BCB使用Delphi的VCL庫,
是以也必須使用__fastcall。
關鍵字
調用規則
參數傳遞方向
傳回
參數寄存器
堆棧的清除
__cdecl
C調用規則
從右向左
EAX
無
調用者
__fastcall
寄存器
從左向右
EAX、EBX、ECX
被調用者
__stdcall
Win32标準
__pascal
Pascal
__msfastcall
Ms寄存器
EAX/EDX
ECX、EDX
摘自《面向狀态的程式設計與C++ Builder》第四章第一節 Lewolf著
1.4.1 __cdecl、_cdecl、cdecl關鍵字
這是在C++ Builder中特有的關鍵字,__cdecl、_cdecl、cdecl可以用來修飾變量或者函數,其含義是指定的變量或者函數使用C語言的調用規則和命名規則。在C++ Builder中C調用規則是預設的設定,可以通過Project的Option菜單在Advanced Compiler标簽中來改變預設設定。
對于C命名規則的變量或函數,具有大小些敏感、編譯後變量名稱前下劃線前導符,這與Pascal語言是不一樣的。對于函數,C調用規則要求使用堆棧傳遞參數,參數傳遞的順序為自右向左依次壓入堆棧,由調用者自行清除堆棧,C調用規則允許向函數傳遞不定參數,但這種調用方式系統需要更多的開銷來完成參數的比對,除非特殊用途一般盡量不要使用,這種調用方式最典型的例子是控制台程式中的main函數,程式運作時可以不帶參數,也可以帶多個參數。
在一個獨立的程式中,采用那種調用規則或者命名規則影響并不十分大,但是在多種語言混合程式設計,或者編寫帶有資料輸出或函數輸出的可執行子產品(可執行檔案和動态連結庫)時,命名規則和調用規則顯得尤為重要,使用不當則可能導緻程式無法正常運作,甚至使系統崩潰。
__cdecl、_cdecl、cdecl的使用文法如下:
cdecl <data/function definition> ;
_cdecl <data/function definition> ;
__cdecl <data/function definition> ;
例如:
int cdecl I; //編譯後辨別符為“_I”
void __cdecl Fun(); //編譯後函數名為“_Fun”
1.4.2 __fastcall、_fastcall關鍵字
__fastcall和_fastcall也是C++ Builder中特有的關鍵字,隻能用于修飾函數,其作用是指定函數使用“寄存器”調用規則,使用文法如下:
return-type _fastcall function-name(parm-list)
return-type __fastcall function-name(parm-list)
例如:
void __fastcall GetName(int Index);
使用“寄存器”調用規則的函數,其參數傳遞順序為自左向右依次傳遞,并且将前三個參數盡可能的使用EAX、EBX、ECX這三個寄存器傳遞,對于前三個參數中浮點類型、結構類型等超過四個位元組的變量,和第四個及以後的參數則采用堆棧來傳遞,是以采用“寄存器”調用規則的函數隻能傳遞固定數量的參數。
在C++ Builder中,所有屬于VCL的成員函數,必須是__fastcall類型,編譯器将“寄存器”調用規則和C調用規則、Pascal調用規則以及Win32的标準等其它調用規則是同等對待的,是以__fastcall關鍵字不能和__cdecl、__pascal、__stdcall等關鍵字聯合使用,也不能和__export關鍵字同時使用(實際上使用__fastcall規則的函數是允許輸出的,隻是這種調用規則編寫的dll或可執行子產品隻能被C++ Builder或者Delphi開發的程式載入并調用輸出的函數)。
被指定為“寄存器”調用的函數會在編譯時被冠以前導符@,這将使得C及C++的函數命名過于混亂,但是這種調用規則有很高的執行效率,是以在C++ Builder中幾乎所有VCL的成員函數都使用這種調用規則。
但是__fastcall和_fastcall是C++ Builder之前的C++編譯器所沒有的調用規則,為了保證相容性,可以使程式連接配接以前版本的編譯器生成的庫檔案,C++ Builder提供了-VC指令行選項,預設狀态下,該選項是關閉的,當需要時,可以通過指令行編譯并選擇-VC選項。
1.4.3 __msfastcall、__msreturn關鍵字
__msfastcall和__msreturn也是C++ Builder中特有的關鍵字,使用這兩個關鍵字可以為程式提供和Microsoft相容的“寄存器調用”規則以及和Microsoft相容的函數傳回的規則。
被__msfastcall修飾的函數,其參數傳遞順序為前兩個參數如果長度小于4位元組,則分别使用ECX和EDX寄存器傳遞,其餘的參數和前兩個不能使用寄存器傳遞的參數則按照從右向左的順序通過堆棧來傳遞,堆棧由被調用函數負責清除。
__msreturn是使用Microsoft相容的“寄存器調用”傳回規則,在這種傳回規則中,傳回值的長度在4~8個位元組時,将采用EAX/EDX寄存器傳回函數值。
1.4.4 __pascal、_pascal、pascal關鍵字
__pascal、_pascal、pascal關鍵字表示一個變量或者函數遵循Pascal的調用和命名規則。
在Pascal的命名規則中,對大小寫并不敏感,而且不象C調用規則,會在變量和函數明成前面加前導符。Pascal調用規則中,參數的傳遞是按照從左到右的順序依次壓入堆棧,并且由被調用函數自行清除堆棧。
Pascal調用規則破壞了C語言的本身的調用體系,同時也帶來了更多的靈活性,被表示為__pascal調用規則的函數,編譯後的目标檔案中,名稱會被全部轉換為大寫字母,在Pascal語言中,過程和函數的書寫是對大小些不敏感的,但是在C++ Builder中采用Pascal調用規則的函數,在C++源檔案中仍然需要遵循C語言中對大小些必須嚴格一緻的要求,Pascal調用規則的大小寫不敏感性隻能展現在動态連結庫、或者輸出、輸入函數以及資料的時侯。也正是這樣的原因,對一些采用Object Pascal編寫的第三方控件或者書寫不是很規範的Pascal程式代碼,使用C++ Builder編譯的時候,會出現找不到函數原型的錯誤,實際上可能隻是程式中函數名稱的大小些不一緻造成的。
1.4.5 __stdcall、_stdcall關鍵字
在Windows程式設計中,有一種Win32的标準調用規則,基本上所有的Windows API函數都采用了這種Win32的标準調用規則,在講__declspec(nothrow)關鍵字時的例子,其中宏定義就是C++ Builder中winuser.h宏定義。Win32标準調用在C++ Builder中以__stdcall、_stdcall關鍵字來辨別。
在__stdcall、_stdcall調用規則中,函數的參數是按照從右向左的順序依次壓入堆棧,所有的參數均采用堆棧傳遞,而且是被調用函數負責清除堆棧。在參數的傳遞順序上,Win32标準調用規則和C調用規則一樣,從右向左,但和C調用規則不同的是,Win32的标準調用要求調用者必須嚴格按照被調用函數的參數數量及類型進行參數傳遞。
關于C++ Builder中函數調用規則以及命名規則的詳細内容,讀者參考相關書籍,這裡不做過多講解,下表給出了C++ Builder中幾種調用規則的一個簡單比較,具有彙編語言基礎的讀者可以參考本書CD光牒中相關代碼自行分析差別。
關鍵字 調用規則 參數傳遞方向 傳回 參數寄存器 堆棧的清除
__cdecl C調用規則 從右向左 EAX 無 調用者
__fastcall 寄存器 從左向右 EAX EAX、EBX、ECX 被調用者
__stdcall Win32标準 從右向左 EAX 無 被調用者
__pascal Pascal 從左向右 EAX 無 被調用者
__msfastcall Ms寄存器 從右向左 EAX/EDX ECX、EDX 被調用者