读书人

学习札记:调用约定整理

发布时间: 2012-09-18 16:21:42 作者: rapoo

学习笔记:调用约定整理

=========调用约定整理总=================================

调用约定可以在Project->Setting...->C/C++->Code Generation中的

Calling convention中进行设置,缺省状态为__cdecl

?

?

例如对于函数:

void func(int a,int b,int c,int d)??

{?

?? ///////////

}

?

int main()

{

?? func(1,?? 2,?? 3,?? 4);

}

?

1.如果函数func是__cdecl,调用时情况如下

int main()

{

?? //参数从右到左压栈

?? push?? 4

?? push?? 3

?? push?? 2

?? push?? 1

?? call?? func

?? add? ??esp?? 0x10?? //调用者恢复堆栈指针esp,4个参数的大小是0x10(4x4)

}

C编译时函数名的转换:_function

其中function为函数名

例:int MyFunc(int a, int b)

??? _MyFucn

C++编译时函数名的转换:同__stdcall,把YG改为YA

注意:对于可变参数的成员函数,始终使用__cdecl的转换方式

?

========================================================

?

2.如果函数func是__stdcall,调用时情况如下

int?? main()

{

?? //参数从右到左压栈

?? push?? 4

?? push?? 3

?? push?? 2

?? push?? 1

?? call?? func???????? //恢复堆栈指针:由被调用函数func负责,方法是 "ret 0x10 "

}

C编译时函数名的转换:_function@number

其中function为函数名,number为参数的字节数

例:int MyFunc(int a, int b)

??? _MyFucn@8

??

C++编译时函数名的转换:?function@@YG****@Z或者?function@@YG*XZ

若函数有参数,以@Z结束;若函数无参数,则以Z结束

其中function为函数名,*代表参数表,为下列值:

????? X--void ,

????? D--char,

????? E--unsigned char,

????? F--short,

????? H--int,

????? I--unsigned int,

????? J--long,

????? K--unsigned long,

????? M--float,

????? N--double,

???? _N--bool,

???? PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,

以"0"代替,一个"0"代表一次重复

参数表第一项为返回类型,其后跟参数的类型,指针标识在其所指数据类型前

例:int MyFunc1(unsigned char *arg1, unsigned long arg2)

??? ?MyFunc1@@YGHPAEK@Z

??? void MyFunc2(char *arg1, char *arg2, char *arg3)

??? ?MyFunc2@@YGXPAD00@Z

??? void MyFunc3()

??? ?MyFunc3@@YGXXZ

C++编译器转换函数名时更多的考虑了参数,主要是为了方便函数重载,而C语言则不存在函数重载问题

========================================================

3.如果函数func是__pascal,调用情况如下

int?? main()

{

?? //参数从左到右压栈

?? push?? 1

?? push?? 2

?? push?? 3

?? push?? 4

?? call?? func? //恢复堆栈指针:由被调用函数func负责,方法是 "ret?? 0x10 "

}

========================================================

?

4.如果函数func是__fastcall,调用情况如下

int?? main()

{

  //规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过堆栈传递(从右到左)。

  //不同编译器编译的程序规定的寄存器不同。在Intel 386平台上,使用ECX和EDX寄存器。

  //使用__fastcall方式无法用作跨编译器的接口。

?? //如果以2个参数进栈为例

?? //使用ECX传递第一个参数,EDX传递第二个参数,其余2个参数自右向左压栈

?? push?? 4

?? push?? 3

?? mov?? edx 2

?? mov?? ecx 1

?? call?? func //恢复堆栈指针:由被调用函数func负责,方法是 "ret 0x08 ",

?????????? //因为只进栈一个参数,其余用寄存器传递,所以用ret 0x08恢复

}

C编译时函数名的转换:@function@number

其中function为函数名,number为参数的字节数

例:int MyFunc(int a, int b)

??? @MyFucn@8

C++编译时函数名的转换:同__stdcall,把YG改为YI

?

========================================================

5.thiscall

thiscall不是一个关键字,因此不能在程序中明确指定,它是C++类成员函数缺省的调用约定。

由于成员函数调用涉及到一个this指针,因此必须进行特殊处理。

参数自右向左压栈

如果参数个数确定,this指针通过ECX传递给被调用者,???? 由被调用函数func清理入栈参数

如果参数个数不定,this指针在所有参数压栈后被压入堆栈,由调用者清理堆栈

?

可见,对于参数个数固定情况下,它类似于__stdcall,不定时则类似__cdecl

?

6.naked call

使用前四种调用约定时,在进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,

退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。更特殊的是,

不能用return返回返回值,返回值存放在eax中,

?? ?__asm ret

00401026?? ret 只能用插入汇编返回结果。

naked call必须和__declspec连用,即__declspec(naked),naked call还可以和其他调用约定联用,如:

??? __declspec(naked) int __stdcall function(int a, int b)

?

使用场合:

1 _beginthread需要__cdecl的线程函数地址,_beginthreadex和CreateThread需要__stdcall的线程函数地址。

2 main函数必须是__cdecl,一般的WIN32函数和回调函数都是__stdcall,并且专门定义了宏来标识:

? #define CALLBACK __stdcall

? #define WINAPI  __stdcall

3.如果某函数在C语言编译器中编译,而在C++文件使用,由于两种编译器对函数名的解析不一样,

因此需要在C++文件中使用extern "C"进行声明,否则会发生链接错误:

#ifdef _cplusplus

extern "C"{

#endif

int func(int a, int b);

#ifdef _cplusplus

}

#endif

========================================================

其他:

注意: 在Win32 API中,很多函数都有WINAPI修饰符,

但WINAPI实际上不是调用约定,而是使用默认调用约定。

例如: 在Windows上默认为__stdcall,在WinCE.NET上默认为__cdecl。

========================================================

?

?

?

读书人网 >编程

热点推荐