_cdel和thiscall调用约定

函数调用约定的作用
(1)规定了参数压栈的顺序;
(2) 规定由谁来清理堆栈
(3)规定函数返回值所放置的地方
1 _cdel调用(它是c语言默认的函数调用方法)
1)它的参数从右到左依次压栈,函数本身并不清理堆栈,这些参数由调用者清理,是C语言缺省的调用约定,它的定义语法是:

  int function (int a ,int b)           // 不加修饰就是C调用约定

  int __cdecl function(int a,int b)     // 明确指出C调用约定

所以C调用约定允许函数的参数的个数是不固定的,这也是C语言的一大特色。对于前面的function函数,使用cdecl后的汇编码变成:

调用处

 push   2       // 第二个参数入栈

  push   1      //  第一个参数入栈

  call   function

  add    esp,8              //注意:这里调用者在恢复堆栈

被调用函数_function处

  push   ebp                 //  保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复

  mov    ebp,esp               //  保存堆栈指针

  mov    eax,[ebp + 8H]     //堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a

  add    eax,[ebp + 0CH]    堆栈中ebp + 12处保存了b

  mov    esp,ebp            //恢复esp

  pop    ebp

  ret                       //注意,这里没有修改堆栈

MSDN中说,该修饰自动在函数名前加前导的下划线,因此函数名在符号表中被记录为_function。 由于参数按照从右向左顺序压栈,因此

最开始的参数在最接近栈顶的位置,因此当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个

后者后续的明确的参数确定下来,就可以使用不定参数,例如对于sprintf函数,定义为:

int sprintf(char* buffer,constchar* format,…)

由于所有的不定参数都可以通过format确定,因此使用不定个数的参数是没有问题的。

具体的执行步骤是:
调用方的函数调用->被调用函数的执行->被调用函数的结果返回->调用方清除调整堆栈

2_thiscall是为了解决类成员调用中this指针传递而规定的。_thiscall要求把this指针放在特定寄存器中,该寄存器由编译器决定。VC使用ecx,Borland的C++编译器使用eax
thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个默认的this指针,因此必须特殊处理,thiscall意味着:

1) 参数从右向左入栈;

2) 如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈;

3) 对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈。

为了说明这个调用约定,定义如下类和使用代码:

class A
{
public:
        int function1(int a,int b);
        int function2(int a,...);
};

int A::function1 (int a,int b)
{
        return a+b;
}

int A::function2(int a,...)
{

        va_list ap;
        va_start(ap,a);
        int i;
        int result = 0;
        for(i = 0 ; i < a ; i ++)
                result += va_arg(ap,int);
        return result;
}

void callee()
{
        A a;
        a.function1(1,2);
        a.function2(3,1,2,3);
}

callee函数被翻译成汇编后就变成:
// 函数function1调用

  0401C1D    push        2
  00401C1F   push        1
  00401C21   lea         ecx,[ebp-8]
  00401C24   call   function1             注意,这里this没有被入栈

// 函数function2调用

  00401C29   push        3
  00401C2B   push        2
  00401C2D   push        1
  00401C2F   push        3
  00401C31   lea         eax,[ebp-8]      这里引入this指针
  00401C34   push        eax
  00401C35   call   function2
  00401C3A   add         esp,14h

所依在参数个数不确定的时候thiscall的调用方式类似于—cdel
注意:
(1)C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。
(2)带有可变参数的函数必须且只能用—cdel的方式

你可能感兴趣的:(c/c++)