面试中被问到函数调用规则,不懂,所以查了资料,总结如下:
调用规则 | __cdecl | __stdcall | __fastcall | __pascal | __thiscall |
参数压栈顺序 | 从右到左 | 从右到左 | 用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送 | 从左到右 | 从右到左 |
栈维护 | 函数调用者 | 函数本身 | 函数本身 | 函数本身 | 函数本身 |
返回值存放 | EAX | EAX | EAX | EAX | EAX |
C编译器名称修饰 | 函数名前加上一个下划线前缀 | 函数名前加上一个下划线前缀 | 函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数 |
|
|
C++编译器名称修饰 | 规则与_stdcall调用约定相同,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。 | 见表下面 | 规则与_stdcall调用约定相同,只是参数表的开始标识由上面的"@@YG"变为"@@YI" |
|
|
VC++ __stdcall调用约定名称修饰规则
1)、以"?"标识函数名的开始,后跟函数名;
2)、函数名后面以"@@YG"标识参数表的开始,后跟参数表;
3)、参数表以代号表示:
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"代表一次重复;
4)、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
5)、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
说明一下:之所以搞清楚栈的维护者很重要,因为有的约定需要函数自己来清除堆栈,如果都是使用c的话,倒是没太大问题,因为编译器都会帮你搞定,但是如果编写汇编代码提供给c使用,那汇编代码就要注意了,如果所使用的调用约定需要自己清除堆栈空间,则一定要清楚,否则后面的代码都会认为该函数已经清空了堆栈,后果当然很严重,大家都懂得:)