stdcall很多時候被稱為pascal調(diào)用約定,因為pascal是早期很常見的一種教學(xué)用計算機程序設(shè)計語言,其語法嚴謹,使用的函數(shù)調(diào)用約定就是
stdcall。在Microsoft C++系列的C/C++編譯器中,常常用PASCAL宏來聲明這個調(diào)用約定,類似的宏還有WINAPI和
CALLBACK。
stdcall調(diào)用約定聲明的語法為(以前文的那個函數(shù)為例):
int __stdcall function(int a,int b)
stdcall的調(diào)用約定意味著:1)參數(shù)從右向左壓入堆棧,2)函數(shù)自身修改堆棧 3)函數(shù)名自動加前導(dǎo)的下劃線,后面緊跟一個@符號,其后緊跟著參數(shù)的尺寸
以上述這個函數(shù)為例,參數(shù)b首先被壓棧,然后是參數(shù)a,函數(shù)調(diào)用function(1,2)調(diào)用處翻譯成匯編語言將變成:
push 2 第二個參數(shù)入棧
push 1 第一個參數(shù)入棧
call function 調(diào)用參數(shù),注意此時自動把cs:eip入棧
而對于函數(shù)自身,則可以翻譯為:
push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數(shù)退出時恢復(fù)
mov ebp,esp 保存堆棧指針
mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a
add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b
mov esp,ebp 恢復(fù)esp
pop ebp
ret 8
而在編譯時,這個函數(shù)的名字被翻譯成_function@8
注意不同編譯器會插入自己的匯編代碼以提供編譯的通用性,但是大體代碼如此。其中在函數(shù)開始處保留esp到ebp中,在函數(shù)結(jié)束恢復(fù)是編譯器常用的方法。
從函數(shù)調(diào)用看,2和1依次被push進堆棧,而在函數(shù)中又通過相對于ebp(即剛進函數(shù)時的堆棧指針)的偏移量存取參數(shù)。函數(shù)結(jié)束后,ret 8表示清理8個字節(jié)的堆棧,函數(shù)自己恢復(fù)了堆棧。
c程序默認的是__cdecl
__stdcall時輸入?yún)?shù)的棧空間有被調(diào)用函數(shù)恢復(fù)
__cdecl時由調(diào)用函數(shù)恢復(fù)
和參數(shù)如棧順序不同,記不清楚了
是編譯器調(diào)用約定的一種
調(diào)用約定分為_cdecl _stdcall _fastcall thiscall naked call等等 網(wǎng)絡(luò)上很多調(diào)用約定的介紹
_stdcall將參數(shù)壓棧是按C語言的順序(從右到左),但與C 語言不同的是它是由被調(diào)用者將參數(shù)從棧中清除的,所以它的編譯文件比_cdecl?gt;>stdcall是Windows API函數(shù)中默認的調(diào)用約定,VB、VFP等也采用這個約定。
_cdecl是C語言采用的默認調(diào)用方法,它的優(yōu)點是支持printf這樣的可變參數(shù)調(diào)用。
另外,VC++對于兩種調(diào)用方法的名稱轉(zhuǎn)換方法也不同
++++++++++++++++++++++++++++++++++++++++++++++++++++
_stdcall
按
從右至左的順序壓參數(shù)入棧,由被調(diào)用者把參數(shù)彈出棧。對于“C”函數(shù)或者變量,修飾名以下劃線為前綴,然后是函數(shù)名,然后是符號“@”及參數(shù)的字節(jié)數(shù),如
函數(shù)int func(int a, double b)的修飾名是_func@12。對于“C++”函數(shù),則有所不同。
所有的Win32 API函數(shù)都遵循該約定。
+++++++++++++++++++++++++++++++++++++++++++++++++++++
函數(shù)調(diào)用約定有多種,這里簡單說一下:
1、__stdcall調(diào)用約定相當(dāng)于16位動態(tài)庫中經(jīng)常使用的PASCAL調(diào)用約定。在32
位的VC++5.0中PASCAL調(diào)用約定不再被支持(實際上它已被定義為__stdcall。除了__pascal外,__fortran和
__syscall也不被支持),取而代之的是__stdcall調(diào)用約定。兩者實質(zhì)上是一致的,即函數(shù)的參數(shù)自右向左通過棧傳遞,被調(diào)用的函數(shù)在返回前
清理傳送參數(shù)的內(nèi)存棧,但不同的是函數(shù)名的修飾部分(關(guān)于函數(shù)名的修飾部分在后面將詳細說明)。
_stdcall是Pascal程序的缺省調(diào)用方式,通常用于Win32 Api中,函數(shù)采用從右到左的壓棧方式,自己在退出時清空堆棧。VC將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴,在函數(shù)名后加上"@"和參數(shù)的字節(jié)數(shù)。
2、C調(diào)用約定(即用__cdecl關(guān)鍵字說明)按從右至左的順序壓參數(shù)入棧,由調(diào)用者把參數(shù)彈出棧。對于傳送參數(shù)的內(nèi)存棧是由調(diào)用者來維護的(正因為如此,實現(xiàn)可變參數(shù)的函數(shù)只能使用該調(diào)用約定)。另外,在函數(shù)名修飾約定方面也有所不同。
_cdecl是C和C++程序的缺省調(diào)用方式。每一個調(diào)用它的函數(shù)都包含清空堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會比調(diào)用_stdcall函數(shù)的大。函數(shù)采用從右到左的壓棧方式。VC將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴。是MFC缺省調(diào)用約定。
3、
__fastcall調(diào)用約定是"人"如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數(shù)的(實際上,它用ECX和EDX傳送前兩個雙字
(DWORD)或更小的參數(shù),剩下的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的內(nèi)存棧),在函數(shù)名修飾約定方面,它和前兩者均不同。
_fastcall方式的函數(shù)采用寄存器傳遞參數(shù),VC將函數(shù)編譯后會在函數(shù)名前面加上"@"前綴,在函數(shù)名后加上"@"和參數(shù)的字節(jié)數(shù)。
4、thiscall僅僅應(yīng)用于"C++"成員函數(shù)。this指針存放于CX寄存器,參數(shù)從右到左壓。thiscall不是關(guān)鍵詞,因此不能被程序員指定。
5、naked call采用1-4的調(diào)用約定時,如果必要的話,進入函數(shù)時編譯器會產(chǎn)生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數(shù)時則產(chǎn)生代碼恢復(fù)這些寄存器的內(nèi)容。
naked call不產(chǎn)生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用。
關(guān)
鍵字 __stdcall、__cdecl和__fastcall可以直接加在要輸出的函數(shù)前,也可以在編譯環(huán)境的Setting...\C/C++ \
Code Generation項選擇。當(dāng)加在輸出函數(shù)前的關(guān)鍵字與編譯環(huán)境中的選擇不同時,直接加在輸出函數(shù)前的關(guān)鍵字有效。它們對應(yīng)的命令行參數(shù)分別
為/Gz、/Gd和/Gr。缺省狀態(tài)為/Gd,即__cdecl。
要完全模仿PASCAL調(diào)用約定首先必須使用__stdcall調(diào)用約定,至于
函數(shù)名修飾約定,可以通過其它方法模仿。還有一個值得一提的是WINAPI宏,Windows.h支持該宏,它可以將出函數(shù)翻譯成適當(dāng)?shù)恼{(diào)用約定,在
WIN32中,它被定義為__stdcall。使用WINAPI宏可以創(chuàng)建自己的APIs。