1. cdecl: C的函数默认调用方式, 适用可变参数,这种方式由于是 函数调用者清理堆栈,所以又导致了生成代码增长(因为每次函数调用之后都是实现清理功能的代码,如果是函数自己清理,则只需要一份清理代码就可以了)
2.
stdcall:
函数清理堆栈,不可用于可变参数。
3.
fastcall: 类似stdcall, 但把因为它是通过寄存器
来传送参数的(实际上,它用ECX和EDX传送前两个单字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈 )。
基于在不同函数调用方式下的汇编生成代码作比较:(用VS反汇编生成,看标红代码即可)
int func(int val1,int val2,int val3) { int a; a=val1; return a; } b=func(1,2,3); //调用语句
--------cdecl------------
func:
.........
004113BE mov eax,dword ptr [val1]
004113C1 mov dword ptr [a],eax
004113C4 mov eax,dword ptr [a]
.........
004113CD
ret
b=func(1,2,3):
004113FE push 3
00411400 push 2
00411402 push 1
00411404 call func (411028h)
00411409
add esp,0Ch
0041140C mov dword ptr [b],eax
说明:
cdecl是函数调用者清理参数(add esp,0Ch,把栈指针加3个int长度的偏移,实现参数的清理,如果是类对象还要析构)
--------cdecl------------
--------stdcall------------
func:
.........
004113BE mov eax,dword ptr [val1]
004113C1 mov dword ptr [a],eax
004113C4 mov eax,dword ptr [a]
.........
004113CD
ret 0Ch
b=func(1,2,3):
004113FE push 3
00411400 push 2
00411402 push 1
00411404 call func (411028h)
00411409 mov dword ptr [b],eax
说明:
stdcall是函数自己清理参数(注意此时堆栈指针esp是指向返回地址的,返回地址下面是参数,指令
ret OCh使
函数返回再给
esp加上3个int长度的偏移来清理参数
)
--------stdcall------------
--------fastcall------------
func:
.........
004113BF
pop ecx
004113C0
mov dword ptr [ebp-14h],edx
004113C3
mov dword ptr [ebp-8],ecx
004113C6 mov eax,dword ptr [val1] //这里val1==ebp-8
004113C9 mov dword ptr [a],eax
004113CC mov eax,dword ptr [a]
.........
004113D5
ret 4
b=func(1,2,3):
0041140E push 3
00411410
mov edx,2
00411415
mov ecx,1
0041141A call func (4110B4h)
0041141F mov dword ptr [b],eax
说明:
fastcall是通过ecx和edx来传递前
两个单字(DWORD)或更小的参数 ,如这里用ecx传递1( mov ecx,1 ),edx传递2(mov edx,2 ), 它的栈中参数清理工作是跟stdcall一样的,只是这里只需要清理一个参数就可以了(ret 4 )
--------fastcall------------
总结:
1.cdecl和stdcall好理解,如果不需要可变参数的话,stdcall是好的选择。
2.fastcall的作用有点不好理解,从上面的代码看,看不出有多么快速。。
待续。。