您的位置:首页 > 其它

_stdcall,_cdecl与extern "C"

2011-10-02 20:04 531 查看
原文:/article/4821535.html
调用一个函数时,总是先把参数压入栈,然后通过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的汇编代码

@ILT+0(?swap@@YAXPAH0@Z):

00401005 E9 16 00 00 00 jmp swap (00401020)

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

(2)_stdcall

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@@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语言转换方式),其余与第二种情况相同.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: