__stdcall和__cdecl是兩種函數名字修飾。(注意是連續的兩個下划線)
Windows上
windows上不管是C還是C++,默認使用的都是__stdcall方式。
不論__stdcall還是__cdecl函數參數都是從可向左入棧的,並且由調用者完成入棧操作。對於__stdcall方式被調用者自身在函數返回前清空堆棧;而__cdecl則由調用者維護內存堆棧,所以調用者函數生成的匯編代碼比前一種方式長。
由__cdecl約定的函數只能被C/C++調用。
Windows上使用dumpbin工具查看函數名字修飾。
C語言
__stdcall方式:_FuncName@sizeofParameters
例如:int __stdcall test(int a,double b)編譯之后完整的函數名為_test@12
__cdecl方式:_FuncName
例如:int __stdcall test(int a,double b)編譯之后完整的函數名為_test
由於C++允許重載函數,所以函數的名字修飾就不能像C這么簡單,C++中的函數名字修飾應該包含返回類型,各參數類型等信息,如果是類成員函數,還應該包含類名、訪問級別、是否為const函數等等信息。
C++語言
不管__cdecl,__fastcall還是__stdcall調用方式,函數修飾都是以一個“?”開始,后面緊跟函數的名字,再后面是參數表的開始標識和按照參數類型代號拼出的參數表。對於__stdcall方式,參數表的開始標識是“@@YG”,對於__cdecl方式則是“@@YA”。
X--void
D--char
E--unsigned char
F--short
H--int
I--unsigned int
J--long
K--unsigned long(DWORD)
M--float
N--double
_N--bool
U--struct
PA--指針
PB--const類型的指針
如果相同類型的指針連續出現,以“0”代替,一個“0”代表一次重復;
U表示結構類型,通常后跟結構體的類型名,用“@@”表示結構類型名的結束;
函數參數表的第一項實際上是表示函數的返回值類型;
參數表后以“@Z”標識整個名字的結束,如果該函數無參數,則以“Z”標識結束。
舉例:
int Function1 (char *var1,unsigned long);其函數修飾名為“?Function1@@YGHPADK@Z”
void Function2();其函數修飾名則為“?Function2@@YGXXZ”
C++中調用由C編譯器生成的函數
extern "C" {
long func(int a);
char* strcat(char*,const char*);
}
extern "C" {
#include<string.h>
}
要想在編譯階段就知道使用的編譯器類型,可以使用:
#ifdef __cplusplus cout<<"C++"; #else printf("C"); #endif
通常應該這樣聲明頭文件:
#ifdef _cplusplus extern "C" { #endif long MakeFun(long lFun); #ifdef _cplusplus } #endif
Linux上
Linux上使用__stdcall和__cdecl的方式比較麻煩一些。
int __attribute__((cdecl)) test();
Linux上使用nm工具查看函數名字修飾。
__stdcall和__cdecl沒有區別,有區別的是編程語言。
C++語言
char test(); ----- _Z4testv _Z表示C++,4代表函數名有4個字節,test是函數名,v代表參數為空
double func(unsigned int a,double *b,char c); ----- _Z4funcjPdc j代表int,Pd代表double型指針,c代表char
C語言
只是簡單一個函數名,沒有其他修飾信息。
char test(); ----- test
double func(unsigned int a,double *b,char c); ----- func
附:
Linux上的反匯編工具:objdump -x exefile
查看二進制文件:hexdump -C biFile
編輯二進制文件:hexedit biFile