函数调用惯例

函数的调用方与被调用方对于函数如何调用须要一个明确的约定,这样的约定就叫作调用惯例。

一个调用惯例一般会规定如下几方面的内容:

1. 函数参数的传递顺序及方式

函数参数的传递有多种方式,常见的是通过栈传递。函数的调用方将参数压入栈中,函数自己再从栈中取出参数。对于有多个参数的函数,调用惯例需要约定函数调用方将参数压入栈的顺序:是从左到右,还是从右到左。还有些调用惯例也支持使用寄存器来传递参数。

2. 栈的维护方式

在参数被压入栈后,函数体被调用执行,此后需要将被压入栈的参数再全部弹出,以使得栈在函数调用前后保持一致。这个弹出工作可以由调用方完成,也可以由被调用函数来完成。

3. 名字修饰的策略

为了链接的时候能够对调用惯例进行区分,调用管理要对函数名进行修饰。不同的调用惯例也有不同的修饰策略。

下表列出几个常见的调用惯例:

调用惯例

出栈方

参数传递

名字修饰

cdecl 调用方 从右至左的顺序压参数入栈 下划线+函数名
stdcall 函数自身 从右至左的顺序压参数入栈 下划线+函数名+@+参数的字节数,如函数int func(int a, double b)的修饰名是_func@12
fastcall 函数自身 头两个DWORD(4字节)类型或者占更少字节的参数被放入寄存器,其他剩下的参数按从右到左的顺序入栈 @+函数名+@+参数的字节数
pascal 函数自身 从左至右的顺序入栈 较复杂,参见pascal 文档

其中cdecl 是c 语言默认的调用惯例。

 

此外,C 语言的诸如printf() 这样支持可变参数的函数,也是依赖于cdecl 的从右向左传递参数从而得到实现的。

你可能感兴趣的:(技术杂谈)