C语言函数调用方式的区别 -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

   

    通常在使用VC进行函数定义时会指定该函数调用方式,诸如:

    int __stdcall max(int a, int b)

    {

    return a>b?a:b;

    }

    int __cdecl min(int a, int b)

    {

    return a

    }

    bool __fastcall equal(int a, int b)

    {

    return a=b?true:false;

    }

    首先,让我们来分个类,调用方法分为两大类另加一个较特殊的__thiscall.

    第一类:__stdcall类 别名:WINAPI,CALLBACK,PASCAL,

C语言函数调用方式的区别

。该类特点是:主调函数负责参数入栈,由函数本身负责栈的恢复。

    第二类:__cdecl类 别名:C/C++中默认调用方式,若你定义函数未指定函数调用约定(Calling Conventions),例如在VC6中下面两个函数的调用约定是等价的:

    int max(int a, int b)

    {

    return a>b?a:b;

    }

    int __cdecl min(int a, int b)

    {

    return a

    }

    该类调用约定的特点是:由主调函数负责参数入栈,并由主调函数负责线的恢复。

    第三类:__thiscall 该类比较特殊,只用于类成员函数调用,你甚至不能强制指定这个函数调用约定。它是由C/C++编译器自动添加的。在C/C++中类成员函数会默认传入一个this指针,对于此,在默入情况下,C/C++中类成员函数通过此类调用约定来指定this指针。

    接着介绍一下__thiscall,__thiscall是关于类的一种调用方式。

    它与其他调用方式的最大区别是:

    __thiscall对每个函数都增加了一个类指针参数

    class aa

    {

    void bb(int cc);

    };

    实际上bb的函数原形是void bb(aa &this, int cc);

    __cdecl的调用方式介绍: C和C++缺省调用方式

    例子:

    void Input( int &m,int &n);/*相当于void __cdecl Input(int &m,int &n);*/

    以下是相应的汇编代码:

    00401068 lea eax,[ebp-8] ;取[ebp-8]地址(ebp-8),存到eax

    0040106B push eax ;然后压栈

    0040106C lea ecx,[ebp-4] ;取[ebp-4]地址(ebp-4),存到ecx

    0040106F push ecx ;然后压栈

    00401070 call @ILT+5(Input) (0040100a);然后调用Input函数

    00401075 add esp,8 ;恢复栈

    从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然后压栈ebp-4,然后调用函数Input,最后Input函数调用结束后,利用esp+8恢复栈。由此可见,在C语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复堆栈。

    下面看一下:地址ebp-8和ebp-4是什么? 在VC的VIEW->debug windows->Registers,显示寄存器变量值,然后在选debug windows->Memory,输入ebp-8的值和ebp-4的值(或直接输入ebp-8和-4),看一下这两个地址实际存储的是什么值,实际上是变量"n "的地址(ebp-8),m的地址(ebp-4)。

    由此可以看出:在主调用函数中进行实参的压栈并且顺序是从右到左。另外,由于实参是相应的变量的引用,也证明实际上引用传递的是变量的地址(类似指针),

电脑资料

C语言函数调用方式的区别》(https://www.unjs.com)。

    总结:在C或C++语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复堆栈,实参的压栈顺序是从右到左,最后由主调函数进行堆栈恢复。由于主调用函数管理堆栈,所以可以实现变参函数。另外,命名修饰方法是在函数前加一个下划 线(_)。

    _stdcall调用约定介绍:实际上就是PASCAL,CALLBACK,WINAPI

    例子:

    void WINAPI Input( int &m,int &n);

    看一下相应调用的汇编代码:

    00401068 lea eax,[ebp-8]

    0040106B push eax

    0040106C lea ecx,[ebp-4]

    0040106F push ecx

    00401070 call @ILT+5(Input) (0040100a)

    从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然后压栈ebp-4,然后调用函数Input,在调用函数Input之后,没有相应的堆栈恢复工作(为其它的函数调用,所以我没有列出)下面再列出Input函数本身的汇编代码:(实际此函数不大,但做汇编例子还是大了些,大家可以只看前和后,中间代码与此例子无关)

    39: void WINAPI Input( int &m,int &n)

    40: {

    00401110 push ebp

    00401111 mov ebp,esp

    00401113 sub esp,48h

    00401116 push ebx

    00401117 push esi

    00401118 push edi

    00401119 lea edi,[ebp-48h]

    0040111C mov ecx,12h

    00401121 mov eax,0CCCCCCCCh

    00401126 rep stos dword ptr [edi]

    41: int s,i;

    42:

    43: while(1)

    00401128 mov eax,1

    0040112D test eax,eax

    0040112F je Input+0C1h (004011d1)

    44: {

    45: printf("Please input the first number m:");

    00401135 push offset string " Please input the first number m"… (004260b8)

    0040113A call printf (00401530)

    0040113F add esp,4

    46: scanf("%d",&m);

    00401142 mov ecx,dword ptr [ebp+8]

    00401145 push ecx

    00401146 push offset string "%d" (004260b4)

    0040114B call scanf (004015f0)

    00401150 add esp,8

    47:

    48: if ( m= s )

    004011B3 mov eax,dword ptr [ebp+8]

    004011B6 mov ecx,dword ptr [eax]

    004011B8 cmp ecx,dword ptr [ebp-4]

    004011BB jl Input+0AFh (004011bf)

最新文章