函数命名规则及调用约定(__cdecl,__stdcall,__fastcall)
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。__cdecl可以写成_cdecl。
__stdcall调用约定用于调用Win32 API函数。采用__stdcall约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。__stdcall可以写成_stdcall。
__fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall可以写成_fastcall
函数返回值传递方式
其实,返回值的传递从处理上也可以想象为函数调用的一个out形参数; 函数返回值传递方式也是函数调用约定的一部分;
有返回值的函数返回时:一般int、指针等32bit数据值(包括32bit结构)通过eax传递,(bool,char通过al传递,short通过ax传递),特别的__int64等64bit结构(struct) 通过edx,eax两个寄存器来传递(同理:32bit整形在16bit环境中通过dx,ax传递); 其他大小的结构(struct)返回时把其地址通过eax返回;(所以返回值类型不是1,2,4,8byte时,效率可能比较差)
参数和返回值传递中,引用方式的类型可以看作与传递指针方式相同;
float\double(包括Delphi中的extended)都是通过浮点寄存器st(0)返回;
下面是几个例子:
global _GetPowerAsm@8
_GetPowerAsm@8:
PUSH EBP ; Save EBP
MOV EBP, ESP ; Move ESP into EBP so we can refer
; to arguments on the stack
MOV EAX, [EBP+4] ; Get first argument
MOV ECX, [EBP+6] ; Get second argument
SHL EAX, CL ; EAX = EAX * (2 ^ CL)
POP EBP ; Restore EBP
RET 8 ;被调用者平衡堆栈
global _GetPowerAsm
_GetPowerAsm:
PUSH EBP ; Save EBP
MOV EBP, ESP ; Move ESP into EBP so we can refer
; to arguments on the stack
MOV EAX, [EBP+4] ; Get first argument
MOV ECX, [EBP+6] ; Get second argument
SHL EAX, CL ; EAX = EAX * (2 ^ CL)
POP EBP ; Restore EBP
RET ;调用者平衡堆栈
global _sums@12 ;全局函数声明,表示这个函数要被外部文件调用
_sums@12:
push ebp ;保护ebp指针
mov ebp,esp
mov eax,[ebp+8] ;第一个入口参数int VAR1
add eax,[ebp+12];第二个入口参数相加int VAR2
add eax,[ebp+16];第三个入口参数相加int VAR3
pop ebp ;恢复ebp指针
ret 12 ;被调用者平衡堆栈
global _sums ;全局函数声明,表示这个函数要被外部文件调用
_sums:
push ebp ;保护ebp指针
mov ebp,esp
mov eax,[ebp+8] ;第一个入口参数int VAR1
add eax,[ebp+12];第二个入口参数相加int VAR2
add eax,[ebp+16];第三个入口参数相加int VAR3
pop ebp ;恢复ebp指针
ret ;调用者平衡堆栈