现在的位置: 首页 > 综合 > 正文

_stdcall,_cdecl与extern “C”

2011年06月16日 ⁄ 综合 ⁄ 共 4947字 ⁄ 字号 评论关闭

调用一个函数时,总是先把参数压入栈,然后通过call指令转移到被调用函数,在完成调用后清除堆栈.这里有两个问题:(1)哪个参数先入栈(2)由谁来清理堆栈.这两个方面的问题称为"调用约定(Calling Conventions)"问题.

这里只讨论_stdcall和_cdecl调用约定,前者是Windows API函数常用的调用约定,后者即是C调用约定.

_stcall:参数按从右向左的顺序入栈,由被调用函数清理堆栈.
_cdecl :参数按从右向左的顺序入栈,由调用函数清理堆栈.

为了防止函数名冲突,或者重载的需要(c++程序),编译器通常都会修改用户定义的函数名.

这样说也许太抽象了,还是来看看具体的例子吧(通常代码最能说明问题,也最能把问题说清楚).

来看一段简单的代码:

Code


很简单,即实现两个数相交换,学过c语言的人对这段代码应该很熟悉.
再来加一段测试代码:

Code


以下的测试基于VC++6.0.
(1)swap函数不加任何修饰(最简单的情况)
来看看它们相应的汇编代码:

//main调用swap的汇编代码

swap(&a,&b);
00401096 8D 45 F8             lea         eax,[ebp-8]
00401099 50                   push        eax
0040109A 8D 4D FC             lea         ecx,[ebp-4]
0040109D 51                   push        ecx
0040109E E8 62 FF FF FF       call        @ILT+0(swap) (00401005)
004010A3 83 C4 08             add         esp,8

//swap的汇编代码

void swap(int *x,int *y)
    {
00401020 55                   push        ebp
00401021 8B EC                mov         ebp,esp
00401023 83 EC 44             sub         esp,44h
00401026 53                   push        ebx
00401027 56                   push        esi
00401028 57                   push        edi
00401029 8D 7D BC             lea         edi,[ebp-44h]
0040102C B9 11 00 00 00       mov         ecx,11h
00401031 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401036 F3 AB                rep stos    dword ptr [edi]
        int temp;
        temp=*x;
00401038 8B 45 08             mov         eax,dword ptr [ebp+8]
0040103B 8B 08                mov         ecx,dword ptr [eax]
0040103D 89 4D FC             mov         dword ptr [ebp-4],ecx
       *x=*y;
00401040 8B 55 08             mov         edx,dword ptr [ebp+8]
00401043 8B 45 0C             mov         eax,dword ptr [ebp+0Ch]
00401046 8B 08                mov         ecx,dword ptr [eax]
00401048 89 0A                mov         dword ptr [edx],ecx
       *y=temp;
0040104A 8B 55 0C             mov         edx,dword ptr [ebp+0Ch]
0040104D 8B 45 FC             mov         eax,dword ptr [ebp-4]
00401050 89 02                mov         dword ptr [edx],eax
   }
00401052 5F                   pop         edi
00401053 5E                   pop         esi
00401054 5B                   pop         ebx
00401055 8B E5                mov         esp,ebp
00401057 5D                   pop         ebp
00401058 C3                   ret

@ILT+0(?swap@@YAXPAH0@Z):
00401005 E9 16 00 00 00       jmp         swap (00401020)

swap被编译器修改为 :?swap@@YAXPAH0@Z,这是c++转换方式.

(2)_stdcall

 

swap(&a,&b);
004010E6 8D 45 F8             lea         eax,[ebp-8]
004010E9 50                   push        eax
004010EA 8D 4D FC             lea         ecx,[ebp-4]
004010ED 51                   push        ecx
004010EE E8 12 FF FF FF       call        @ILT+0(swap) (00401005)

void _stdcall swap(int *x,int *y)
    {
00401030 55                   push        ebp
00401031 8B EC                mov         ebp,esp
00401033 83 EC 44             sub         esp,44h
00401036 53                   push        ebx
00401037 56                   push        esi
00401038 57                   push        edi
00401039 8D 7D BC             lea         edi,[ebp-44h]
0040103C B9 11 00 00 00       mov         ecx,11h
00401041 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401046 F3 AB                rep stos    dword ptr [edi]
        int temp;
        temp=*x;
00401048 8B 45 08             mov         eax,dword ptr [ebp+8]
0040104B 8B 08                mov         ecx,dword ptr [eax]
0040104D 89 4D FC             mov         dword ptr [ebp-4],ecx
       *x=*y;
00401050 8B 55 08             mov         edx,dword ptr [ebp+8]
00401053 8B 45 0C             mov         eax,dword ptr [ebp+0Ch]
00401056 8B 08                mov         ecx,dword ptr [eax]
00401058 89 0A                mov         dword ptr [edx],ecx
       *y=temp;
0040105A 8B 55 0C             mov         edx,dword ptr [ebp+0Ch]
0040105D 8B 45 FC             mov         eax,dword ptr [ebp-4]
00401060 89 02                mov         dword ptr [edx],eax
   }
00401062 5F                   pop         edi
00401063 5E                   pop         esi
00401064 5B                   pop         ebx
00401065 8B E5                mov         esp,ebp
00401067 5D                   pop         ebp
00401068 C2 08 00             ret         8

@ILT+0(?swap@@YGXPAH0@Z):
00401005 E9 16 00 00 00       jmp         swap (00401020)

(3)_cdecl

void _cdecl swap(int *x,int *y)

与第一种情况相同,即这是vc++默认的方式.

(4)extern "C" _cdecl 

void extern "C" _cdecl swap(int *x,int *y)

 

 swap(&a,&b);
00401096 8D 45 F8             lea         eax,[ebp-8]
00401099 50                   push        eax
0040109A 8D 4D FC             lea         ecx,[ebp-4]
0040109D 51                   push        ecx
0040109E E8 67 FF FF FF       call        @ILT+5(_swap) (0040100a)
004010A3 83 C4 08             add         esp,8

@ILT+5(_swap):
0040100A E9 11 00 00 00       jmp         swap (00401020)

swap被编译器修改为_swap,即加一个下划线(c语言转换方式),其余与第三种情况相同.

(5)extern "C" _stdcall

void extern "C" _stdcall swap(int *x,int *y)

 

swap(&a,&b);
00401096 8D 45 F8             lea         eax,[ebp-8]
00401099 50                   push        eax
0040109A 8D 4D FC             lea         ecx,[ebp-4]
0040109D 51                   push        ecx
0040109E E8 62 FF FF FF       call        @ILT+0(_swap@8) (00401005)

swap被修改为_swap@8,即加一个下划线,后面加上参数在堆栈中所占的字节数(c语言转换方式),其余与第二种情况相同.

抱歉!评论已关闭.