函数调用规范的一些知识

2007.1.9 09:42 作者:roloo | 评论:0 | 阅读:391

 

调用规范是指进行一次函数调用所采用的传递参数的方法,返回值的处理以及堆栈的清理等等。常见的调用规范有:

stdcall

cdecl

fastcall

thiscall

nakedcall

 

 

一、stdcall调用规范

stdcall很多时候被称为pascal调用规范,因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定是 stdcall。在Microsoft C++系列的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK。 

 

stdcall调用规范声明的语法为:

int __stdcall fun( int a, int b );

 

stdcall的调用约定:

(1) 参数从右向左压入堆栈。

(2) 函数自身调整堆栈。

(3) 返回函数调用结果。

 

stdcall的其它说明:

由于函数自身调整堆栈,调整堆栈代码包含再被调函数中,所以最后生成的代码文件相对较小;

由于函数自身调整堆栈,必须预先知道函数参数个数,所以参数个数不能是可变的;

在c和c++中输出时函数名都会被修饰,通常是再函数名前加下划线,后面紧跟“@”符号,其后紧跟参数的尺寸。可以通过.def文件去除函数名修饰;

 

stdcall实例分析:

int __stdcall fun( int, int );

int main()

{

    int nRet = fun ( 1, 2 );

    printf( "%d/n", nRet );

    return 0;

}

int __stdcall fun( int a, int b )

{

    return a + b;

}

编译成汇编代码如下:

8:    int main()

9:    {

00401020   push        ebp        // 系统代码

00401021   mov         ebp,esp

00401023   sub         esp,44h

00401026   push        ebx

00401027   push        esi

00401028   push        edi

00401029   lea         edi,[ebp-44h]

0040102C   mov         ecx,11h

00401031   mov         eax,0CCCCCCCCh

00401036   rep stos    dword ptr [edi]

10:       int nRet = Fun( 1 , 2 );

00401038   push        2    // 将第2个参数压入栈中

0040103A   push        1    // 将第1个参数压入栈中

0040103C   call        @ILT+0(Fun) (00401005)   // 跳到Fun函数的入口

// 00401005   jmp         Fun (00401080)

00401041   mov         dword ptr [ebp-4],eax  // 保存返回值

11:       printf( "%d/n", nRet );

00401044   mov         eax,dword ptr [ebp-4]

00401047   push        eax

00401048   push        offset string "%d/n" (0042201c)

0040104D   call        printf (004010b0)

00401052   add         esp,8

12:       return 0;

00401055   xor         eax,eax

13:   }

 

// 省略其它汇编代码

......

15:   int Fun( int a, int b )

16:   {

00401080   push        ebp  // 将当前栈顶地址压入栈中,用于函数退出时回复堆栈指针

00401081   mov         ebp,esp

00401083   sub         esp,40h

00401086   push        ebx

00401087   push        esi

00401088   push        edi

00401089   lea         edi,[ebp-40h]

0040108C   mov         ecx,10h

00401091   mov         eax,0CCCCCCCCh

00401096   rep stos    dword ptr [edi]

17:       return a + b;

00401098   mov         eax,dword ptr [ebp+8]

0040109B   add         eax,dword ptr [ebp+0Ch]

18:   }

0040109E   pop         edi

0040109F   pop         esi

004010A0   pop         ebx

004010A1   mov         esp,ebp

004010A3   pop         ebp

004010A4   ret         8        // 返回并恢复堆栈,两个int参数大小为8

 

 

二、cdecl调用规范

cdecl调用约定又称为C调用约定,是C语言缺省的调用约定。在c中函数不加调用规范修饰符时默认为此调用规范。

 

cdecl调用规范声明的语法为:

int __cdecl fun( int a, int b );

 

cdecl的调用约定:

(1) 参数从右向左压入堆栈。

(2) 返回函数调用结果。

(3) 函数调用者调整堆栈。

 

cdecl的其它说明:

由于函数调用者需要调整堆栈,每次调用都要生成调整堆栈代码,所以最后生成的代码文件相对要大一些;

由于函数调用者调整堆栈,调用者知道函数参数个数并在函数运行完后清理,所以函数参数个数可变;

在c中输出时函数名不会被修饰。在c++中输出时函数名会被修饰,可以通过在前面添加extern "C"或.def文件去掉函数名修饰;

 

cdecl实例分析:

int fun( int, int );

int main()

{

    int nRet = fun ( 1, 2 );

    printf( "%d/n", nRet );

    return 0;

}

int fun( int a, int b )

{

    return a + b;

}

编译成汇编代码如下:

8:    int main()

9:    {

00401020   push        ebp        // 系统代码

00401021   mov         ebp,esp

00401023   sub         esp,44h

00401026   push        ebx

00401027   push        esi

00401028   push        edi

00401029   lea         edi,[ebp-44h]

0040102C   mov         ecx,11h

00401031   mov         eax,0CCCCCCCCh

00401036   rep stos    dword ptr [edi]

10:       int nRet = Fun( 1 , 2 );

00401038   push        2    // 将第2个参数压入栈中

0040103A   push        1    // 将第1个参数压入栈中

0040103C   call        @ILT+0(Fun) (00401005)   // 跳到Fun函数的入口

// 00401005   jmp         Fun (00401080)

00401041   add         esp,8   // 恢复堆栈,两个int参数大小为8

00401044   mov         dword ptr [ebp-4],eax  // 保存返回值

11:       printf( "%d/n", nRet );

00401047   mov         eax,dword ptr [ebp-4]

0040104A   push        eax

0040104B   push        offset string "%d/n" (0042201c)

00401050   call        printf (004010b0)

00401055   add         esp,8

12:       return 0;

00401058   xor         eax,eax

13:   }

// 省略其它汇编代码

......

15:   int Fun( int a, int b )

16:   {

00401080   push        ebp  //

00401081   mov         ebp,esp

00401083   sub         esp,40h

00401086   push        ebx

00401087   push        esi

00401088   push        edi

00401089   lea         edi,[ebp-40h]

0040108C   mov         ecx,10h

00401091   mov         eax,0CCCCCCCCh

00401096   rep stos    dword ptr [edi]

17:       return a + b;

00401098   mov         eax,dword ptr [ebp+8]

0040109B   add         eax,dword ptr [ebp+0Ch]

18:   }

0040109E   pop         edi

0040109F   pop         esi

004010A0   pop         ebx

004010A1   mov         esp,ebp

004010A3   pop         ebp

004010A4   ret

 

 

三、fastcall调用规范

fastcall调用约定和stdcall类似

 

fastcall调用规范声明的语法为:

int __fastcall fun( int a, int b );

 

fastcall的调用约定:

(1) 函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其它参数从右向左压入堆栈。

(2) 函数自身调整堆栈。

(3) 返回函数调用结果。

 

fastcall的其它说明:

 

与stdcall雷同; 

 

 

fastcall实例分析:

int __fastcall fun( int, int, int, int );

int main()

{

    int nRet = fun ( 1, 2, 3, 4 );

    printf( "%d/n", nRet );

    return 0;

}

int __fastcall fun( int a, int b, int c, int d )

{

    return a + b + c + d;

}

编译成汇编代码如下:

8:    int main()
9:    {

00401020   push        ebp        // 系统代码

00401021   mov         ebp,esp

00401023   sub         esp,44h

00401026   push        ebx

00401027   push        esi

00401028   push        edi

00401029   lea         edi,[ebp-44h]

0040102C   mov         ecx,11h

00401031   mov         eax,0CCCCCCCCh

00401036   rep stos    dword ptr [edi]

10:       int nRet = Fun( 1, 2, 3, 4 );

00401038   push        4    // 将第4个参数压入栈中

0040103A   push        3    // 将第3个参数压入栈中

0040103C   mov         edx,2    // 将第2个参数存入edx中

00401041   mov         ecx,1    // 将第1个参数存入ecx中

00401046   call        @ILT+0(Fun) (00401005)   // 跳到Fun函数的入口

// 00401005   jmp         Fun (00401090)

0040104B   mov         dword ptr [ebp-4],eax

11:       printf( "%d/n", nRet );

0040104E   mov         eax,dword ptr [ebp-4]

00401051   push        eax

00401052   push        offset string "%d/n" (0042201c)

00401057   call        printf (004010e0)

0040105C   add         esp,8

12:       return 0;

0040105F   xor         eax,eax

13:   }

// 省略其它汇编代码

......

15:   int Fun( int a, int b, int c, int d )

16:   {

00401090   push        ebp  //

00401091   mov         ebp,esp

00401093   sub         esp,48h

00401096   push        ebx

00401097   push        esi

00401098   push        edi

00401099   push        ecx

0040109A   lea         edi,[ebp-48h]

0040109D   mov         ecx,12h

004010A2   mov         eax,0CCCCCCCCh

004010A7   rep stos    dword ptr [edi]

004010A9   pop         ecx

004010AA   mov         dword ptr [ebp-8],edx

004010AD   mov         dword ptr [ebp-4],ecx

17:       return a + b + c + d;

004010B0   mov         eax,dword ptr [ebp-4]

004010B3   add         eax,dword ptr [ebp-8]

004010B6   add         eax,dword ptr [ebp+8]

004010B9   add         eax,dword ptr [ebp+0Ch]

18:   }

004010BC   pop         edi

004010BD   pop         esi

004010BE   pop         ebx

004010BF   mov         esp,ebp

004010C1   pop         ebp

004010C2   ret         8    // 返回并恢复堆栈,第3和第4个参数大小为8

 

 

四、thiscall调用规范

thiscall是唯一一个不能明确指明的函数修饰,因为thiscall不是关键字。它是C++类成员函数缺省的调用约定。由于成员函数调用还有一个this指针,因此必须特殊处理。

 

thiscall调用规范声明的语法为:

不需要明确修饰,在类声明中自动采用改调用规范;

 

thiscall的调用约定:

(1) 参数从右向左压入堆栈。如果参数个数确定,this指针ü齟cx传递给被调用者;如果参数个数不确定,this指针在所有参数入栈后被压入堆栈。

(2) 如果参数个数确定,函数自身调整堆栈再返回函数调用结果。

(3) 如果参数个数不确定,则先返回函数调用结果并由函数调用者调整堆栈。

 

thiscall的其它说明:

一种特别的调用规范,函数参数个数确定的情况下类似于stdcall,函数参数个数不确定的情况下类似于cdecl; 

 

thiscall实例分析:

class CFun

{

public:

    int Fun1( int, int );

    int Fun2( int, ... );

};

int main()

{

    CFun c;

    int nRet1 = c.Fun1( 1, 2 );

    int nRet2 = c.Fun2( 3, 1, 2, 3 );

    printf( "%d/n%d/n", nRet1, nRet2 );

    return 0;

}

int CFun::Fun1( int a, int b )

{

    return a + b;

}

int CFun::Fun2( int a, ... )

{

    va_list plst;

    va_start( plst, a );

    int nRet = 0;

    for ( int i = 0; i < a; i++ )

    {

        nRet += va_arg( plst, int );

    }

    return nRet;

}

编译成汇编代码如下:

17:   int main()

18:   {

00401030   push        ebp

00401031   mov         ebp,esp

00401033   sub         esp,4Ch

00401036   push        ebx

00401037   push        esi

00401038   push        edi

00401039   lea         edi,[ebp-4Ch]

0040103C   mov         ecx,13h

00401041   mov         eax,0CCCCCCCCh

00401046   rep stos    dword ptr [edi]

19:       CFun c;

20:       int nRet1 = c.Fun1( 1, 2 );

00401048   push        2

0040104A   push        1

0040104C   lea         ecx,[ebp-4]

0040104F   call        @ILT+10(CFun::Fun1) (0040100f)

00401054   mov         dword ptr [ebp-8],eax

21:       int nRet2 = c.Fun2( 3, 1, 2, 3 );

00401057   push        3

00401059   push        2

0040105B   push        1

0040105D   push        3

0040105F   lea         eax,[ebp-4]

00401062   push        eax

00401063   call        @ILT+0(CFun::Fun2) (00401005)

00401068   add         esp,14h

0040106B   mov         dword ptr [ebp-0Ch],eax

22:       printf( "%d/n%d/n", nRet1, nRet2 );

0040106E   mov         ecx,dword ptr [ebp-0Ch]

00401071   push        ecx

00401072   mov         edx,dword ptr [ebp-8]

00401075   push        edx

00401076   push        offset string "%d/n%d/n" (0042201c)

0040107B   call        printf (00401170)

00401080   add         esp,0Ch

23:       return 0;

00401083   xor         eax,eax

24:   }

// 省略其它汇编代码

......

26:   int CFun::Fun1( int a, int b )

27:   {

004010B0   push        ebp

004010B1   mov         ebp,esp

004010B3   sub         esp,44h

004010B6   push        ebx

004010B7   push        esi

004010B8   push        edi

004010B9   push        ecx

004010BA   lea         edi,[ebp-44h]

004010BD   mov         ecx,11h

004010C2   mov         eax,0CCCCCCCCh

004010C7   rep stos    dword ptr [edi]

004010C9   pop         ecx

004010CA   mov         dword ptr [ebp-4],ecx

28:       return a + b;

004010CD   mov         eax,dword ptr [ebp+8]

004010D0   add         eax,dword ptr [ebp+0Ch]

29:   }

004010D3   pop         edi

004010D4   pop         esi

004010D5   pop         ebx

004010D6   mov         esp,ebp

004010D8   pop         ebp

004010D9   ret         8

//

31:   int CFun::Fun2( int a, ... )

32:   {

004010F0   push        ebp

004010F1   mov         ebp,esp

004010F3   sub         esp,4Ch

004010F6   push        ebx

004010F7   push        esi

004010F8   push        edi

004010F9   lea         edi,[ebp-4Ch]

004010FC   mov         ecx,13h

00401101   mov         eax,0CCCCCCCCh

00401106   rep stos    dword ptr [edi]

33:       va_list plst;

34:       va_start( plst, a );

00401108   lea         eax,[ebp+10h]

0040110B   mov         dword ptr [ebp-4],eax

35:       int nRet = 0;

0040110E   mov         dword ptr [ebp-8],0

36:       for ( int i = 0; i < a; i++ )

00401115   mov         dword ptr [ebp-0Ch],0

0040111C   jmp         CFun::Fun2+37h (00401127)

0040111E   mov         ecx,dword ptr [ebp-0Ch]

00401121   add         ecx,1

00401124   mov         dword ptr [ebp-0Ch],ecx

00401127   mov         edx,dword ptr [ebp-0Ch]

0040112A   cmp         edx,dword ptr [ebp+0Ch]

0040112D   jge         CFun::Fun2+56h (00401146)

37:       {

38:           nRet += va_arg( plst, int );

0040112F   mov         eax,dword ptr [ebp-4]

00401132   add         eax,4

00401135   mov         dword ptr [ebp-4],eax

00401138   mov         ecx,dword ptr [ebp-4]

0040113B   mov         edx,dword ptr [ebp-8]

0040113E   add         edx,dword ptr [ecx-4]

00401141   mov         dword ptr [ebp-8],edx

39:       }

00401144   jmp         CFun::Fun2+2Eh (0040111e)

40:       return nRet;

00401146   mov         eax,dword ptr [ebp-8]

41:   }

00401149   pop         edi

0040114A   pop         esi

0040114B   pop         ebx

0040114C   mov         esp,ebp

0040114E   pop         ebp

0040114F   ret

 

 

五、naked call调用规范

naked call这是一个很少见的调用约定,一般程序设计者建议不要使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,不能用return返回返回值,只能用插入汇编返回结果。这一般用于实模式驱动程序设计。这个修饰是和stdcall及cdecl结合使用的。

 

naked call调用规范声明的语法为:

__declspec(naked) int fun( int a, int b);    // 默认为cdecl

__declspec(naked) int __stdcall fun( int a, int b );

 

naked call的调用约定:

(1) 视函数前面的修饰符而定。

 

naked call的其它说明:

一种特别的调用规范,在函数中使用汇编代码,如下:

 __declspec(naked) int __stdcall fun( int a, int b )

{

    __asm mov eax,a

    __asm add eax,b

    __asm ret 8        // 注意值8

}

 

 

参考 http://blogger.org.cn/blog/more.asp?name=oceanblue&id=19832

http://blog.bcchinese.net/happyjet/archive/2005/05/24/22545.aspx

http://www.vckbase.com/document/viewdoc/?id=1438

http://www.czvc.com/view.asp?id=70

http://www.turbozv.com/read.php?78

 

你可以通过这个链接引用该篇文章:      http://roloo.bokee.com/viewdiary.14395212.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值