栈与调用惯例

来源:互联网 发布:java ssh框架教程 pdf 编辑:程序博客网 时间:2024/06/16 04:40

调用惯例

在讲调用惯例之前我们先说明一个问题。通过上一篇栈与栈帧的文章,我们可以发现一个现象:函数的调用者和被调用者对函数如何调用有着统一的理解。例如,它们双方都一致地认同函数的参数是按照某个固定的方式入栈,否则函数将无法正确运行。

例如1,函数:

int  foo(int a,int b)

{

int  n = 0;

````````````

}

如果函数的调用者在传递参数时先压入参数a,再压入参数b,而foo函数却认为其调用者先压入b再压入a,那么不难理解foo内部的a和b的值将会被调换。这样必然很引起很多问题导致程序无法正确执行。

例如2,调用参数传递途径:

如果函数的调用者是使用寄存器传递参数,而函数本身却认为是以参数通过栈传递,这样显然函数无法得到正确的参数。

既然函数调用会出现例子所描述的问题,但是我们平时写程序都能够正常运行,这就是因为存在一个调用惯例的约定。

调用惯例:函数的调用者与被调用者对于函数如何调用须要有一个明确的约定,只有双方都遵循同样的约定,函数才能被正确地调用。这样的约定成为调用惯例。

调用惯例一般约定的内容如下:

1、函数参数的传递顺序和方式

函数的调用方式有使用寄存器传递和通过栈传递之分。为了提高参数传递性能可以使用寄存器传递方式,但是参数传递最常见的是通过栈传递。对于一个参数的时候比较容易理解,只是对参数简单的压栈和出栈。但是如果是两个参数以上的时候,调用惯例必须规定函数调用者将参数压栈的顺序,分为从左只右还是从右到左。

2、栈的维护方式

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

3、名字修饰策略

为了链接是对调用惯例进行区分,调用管理需要对函数本身的名字进行修饰。因为不同的调用惯例有不同的函数名字修饰策略。在C语言里面有多个调用惯例而默认的调用惯例是cdecl。在没有任何指定调用惯例的函数都是默认cdecl惯例。对于上例的函数foo的声明的完整形式为:int  _cdecl  foo(int  a,int  b)。


cdecl这个调用惯例是C语言默认的调用惯例,内容如下:

参数传递:从右到左的顺序压入参数进栈,例如上例子先压b再压a。

栈的维护方式:函数调用者来完成

名字修饰:直接在函数名称前加一个下划线,例如上例子的函数foo被修饰后变成_foo。

另外还介绍其他三种调用惯例,如下图:










0 0
原创粉丝点击