- 在计算机科学中, 调用约定是一种定义子过程从调用处接受参数以及返回结果的方法的约定。不同调用约定的区别在于:
- 参数和返回值放置的位置(在寄存器中;在调用栈中;两者混合)
- 参数传递的顺序(或者单个参数不同部分的顺序)
- 调用前设置和调用后清理的工作,在调用者和被调用者之间如何分配
- 被调用者可以直接使用哪一个寄存器有时也包括在内。(否则的话被当成ABI的细节)
- 哪一个寄存器被当作volatile的或者非volatile的,并且如果是volatile的,不需要被调用者恢复
- _cdcel是C/C++默认的函数调用约定
- 函数实参在线程栈上按照从右至左的顺序依次压栈。
- 函数结果保存在寄存器EAX/AX/AL中
- 浮点型结果存放在寄存器ST0中
- 编译后的函数名前缀以一个下划线字符
- 调用者负责从线程栈中弹出实参(即清栈)
- 8比特或者16比特长的整形实参提升为32比特长。
- 受到函数调用影响的寄存器(volatile registers):EAX, ECX, EDX, ST0 - ST7, ES, GS
- 不受函数调用影响的寄存器: EBX, EBP, ESP, EDI, ESI, CS, DS
- RET指令从函数被调用者返回到调用者(实质上是读取寄存器EBP所指的线程栈之处保存的函数返回地址并加载到IP寄存器)
- stdcall是由微软创建的调用约定,是Windows API的标准调用约定。非微软的编译器并不总是支持该调用协议。
- 被调用者负责清理线程栈,参数从右往左入栈。
- 其他各方面基本与cdecl相同
- fastcall的处理速度快,直接使用寄存器传参
- 实际上__fastcall用ECX和EDX传送前两个DWORD或更小的参数,剩下的参数仍自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈。
- __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@number,如double multi(double a, double b)的修饰名是@multi@16。
- __fastcall和__stdcall很象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,即第1个参数进ECX,第2个进EDX,其他参数是从右
- _thiscall是C++类成员函数缺省的调用约定,但它没有显示的声明形式
- 参数入栈:参数从右向左入栈
- this指针入栈:如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入栈。
- 栈恢复:对参数个数不定的,调用者清理栈,否则函数自己清理栈。
- pascal
- 基于Pascal语言的调用约定,参数从左至右入栈(与cdecl相反)。被调用者负责在返回前清理堆栈。