天天看點

【C/C++學習筆記】C++ 調用約定一、函數調用約定

一、函數調用約定

  首先,我們由函數的調用約定說起,microsoft的vc預設的是__cdecl方式,而windows API則是__stdcall,如果用vc開發dll給其他語言用,則應該指定__stdcall方式。堆棧由誰清除這個很重要,如果是要寫彙編函數給C調用,一定要小心堆棧的清除工作,如果是__cdecl方式的函數,則函數本身(如果不用彙編寫)則不需要關心儲存參數的堆棧的清除,但是如果是__stdcall的規則,一定要在函數退出(ret)前恢複堆棧。

windows有如下五種調用約定:

1.__cdecl

       所謂的C調用規則。按從右至左的順序壓參數入棧,由調用者把參數彈出棧。切記:對于傳送參數的記憶體棧是由調用者來維護的。傳回值在EAX中是以,對于象printf這樣變參數的函數必須用這種規則。編譯器在編譯的時候對這種調用規則的函數生成修飾名的餓時候,僅在輸出函數名前加上一個下劃線字首,格式為_functionname。

2.__stdcall 

        按從右至左的順序壓參數入棧,由被調用者把參數彈出棧。_stdcall是Pascal程式的預設調用方式,通常用于Win32 Api中,切記:函數自己在退出時清空堆棧,傳回值在EAX中。  __stdcall調用約定在輸出函數名前加上一個下劃線字首,後面加上一個“@”符号和其參數的位元組數,格式為[email protected]。如函數int func(int a, double b)的修飾名是[email protected]。

3.__fastcall

       __fastcall調用的主要特點就是快,因為它是通過寄存器來傳送參數的(實際上,它用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在傳回前清理傳送參數的記憶體棧)。__fastcall調用約定在輸出函數名前加上一個“@”符号,後面也是一個“@”符号和其參數的位元組數,格式為@[email protected]。這個和__stdcall很象,唯一差别就是頭兩個參數通過寄存器傳送。注意通過寄存器傳送的兩個參數是從左向右的,即第一個參數進ECX,第2個進EDX,其他參數是從右向左的入stack。傳回仍然通過EAX。

4.__pascal

       這種規則從左向右傳遞參數,通過EAX傳回,堆棧由被調用者清除。

5.__thiscall

僅僅應用于"C++"成員函數。this指針存放于CX寄存器,參數從右到左壓。thiscall不是關鍵詞,是以不能被程式員指定。

二、__cdecl,__stdcall(WINAPI),__fastcall 的差別

(1) __cdecl:C/C++預設方式,參數從右向左入棧,主調函數負責棧平衡。

(2) __stdcall:windows API預設方式,參數從右向左入棧,被調函數負責棧平衡。

(3) __fastcall:快速調用方式。數優先從寄存器傳入(ECX和EDX),剩下的參數再從右向左從棧傳入。

三、最為關鍵的原則

(1) 導出的函數明确指定調用約定,一般為__stdcall

(2) 導出函數,跟調用者保持一緻調用約定

(3) 調用約定錯誤如下(發生如下錯誤時檢查調用約定):

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

繼續閱讀