函数调用约定_stdcall和_cdecl
2012-06-25 18:55
344 查看
_cdecl 是C Declaration的缩写,表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。
_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
我这里是C++代码,函数调用的方式应该是 _stdcall(默认的)。用void _cdecl
foo(int p1,int p2,int p3)和void
_stdcall foo(int p1,int p2,int p3)的结果都是一样的,如上图所示。_stdcall 和 _cdecl函数调用都是参数从右到左依次压栈。但在参数里面出现++i和i++时,有几点需要说明的是:
1、有++i和i++时,先计算++操作,再进行压栈操作。
2、++i返回的i的引用(加1)相当于{i = i + 1; return &i},i++返回的是一个临时变量(没有加1)相当于{int temp = i; i = i + 1;return temp}。压栈的时候++i使用的i本身的值,i++压栈使用的这个临时变量值temp。
下面是visual studio 2008的反汇编代
foo(++i,++i,++i)的反汇编代码:
foo(++i,i++,i++)的反汇编代码
自习看上面的两段汇编代码,就可以明白函数调用是参数的压栈顺序,特别是从第二段汇编代码可以看出,函数压栈顺序是从有到左,先i++,i++,在++i。在压栈之前就已经计算好了各自的值。
参考资料:
1、百度百科,_cdecl,http://baike.baidu.com/view/1280676.htm
2、代码疑云(2)-c函数调用约定,http://blog.csdn.net/zhanxinhang/article/details/6586962
_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
#include<iostream> using namespace std; void foo(int p1,int p2,int p3) { cout<<"p1="<<p1<<endl <<"p2="<<p2<<endl <<"p3="<<p3<<endl; } int main() { int i; cout<<"first call:"<<endl; i=0; foo(++i,++i,++i); cout<<"second call:"<<endl; i=0; foo(++i,i++,i++); cout<<"third call:"<<endl; i=0; foo(++i,i, i++); cout<<"fourth call:"<<endl; i=0; foo(++i,i++,++i); system("PAUSE"); return 0; }结果为:
我这里是C++代码,函数调用的方式应该是 _stdcall(默认的)。用void _cdecl
foo(int p1,int p2,int p3)和void
_stdcall foo(int p1,int p2,int p3)的结果都是一样的,如上图所示。_stdcall 和 _cdecl函数调用都是参数从右到左依次压栈。但在参数里面出现++i和i++时,有几点需要说明的是:
1、有++i和i++时,先计算++操作,再进行压栈操作。
2、++i返回的i的引用(加1)相当于{i = i + 1; return &i},i++返回的是一个临时变量(没有加1)相当于{int temp = i; i = i + 1;return temp}。压栈的时候++i使用的i本身的值,i++压栈使用的这个临时变量值temp。
下面是visual studio 2008的反汇编代
foo(++i,++i,++i)的反汇编代码:
i=0; 00411669 mov dword ptr [i],0 // 初始化i的内存单元的值 foo(++i,++i,++i); 00411670 mov eax,dword ptr [i] // 把i放到eax寄存器 00411673 add eax,1 // 把eax的值加1 00411676 mov dword ptr [i],eax // 用eax值修改i的内存单元 00411679 mov ecx,dword ptr [i] 0041167C add ecx,1 0041167F mov dword ptr [i],ecx 00411682 mov edx,dword ptr [i] 00411685 add edx,1 00411688 mov dword ptr [i],edx 0041168B mov eax,dword ptr [i] // 因为++i返回的是i的引用,直接用i的内存单元的值压栈 0041168E push eax 0041168F mov ecx,dword ptr [i] // 因为++i返回的是i的引用,直接用i的内存单元的值压栈 00411692 push ecx 00411693 mov edx,dword ptr [i] // 因为++i返回的是i的引用,直接用i的内存单元的值压栈 00411696 push edx 00411697 call foo (411096h) // 调用foo函数 0041169C add esp,0Ch // 弹栈,3个int变量的大小,即清空函数栈
foo(++i,i++,i++)的反汇编代码
i=0; 004116CA mov dword ptr [i],0 foo(++i,i++,i++); 004116D1 mov eax,dword ptr [i] 004116D4 mov dword ptr [ebp-0D0h],eax // ebp是栈底指针,[ebp-0D0h](++i使用的临时变量地址)的值是0 004116DA mov ecx,dword ptr [i] 004116DD add ecx,1 004116E0 mov dword ptr [i],ecx // 先计算i的值,从左到右,修改i的值,i==1 004116E3 mov edx,dword ptr [i] 004116E6 mov dword ptr [ebp-0D4h],edx // [ebp-0D4h]的值为1 004116EC mov eax,dword ptr [i] 004116EF add eax,1 004116F2 mov dword ptr [i],eax // 修改i的值,i==2 004116F5 mov ecx,dword ptr [i] 004116F8 add ecx,1 004116FB mov dword ptr [i],ecx // 修改i的值,i==3 004116FE mov edx,dword ptr [ebp-0D0h] // 压栈的是i++的临时变量,顺序是从右到左 00411704 push edx 00411705 mov eax,dword ptr [ebp-0D4h] // 压栈的是i++的临时变量 0041170B push eax 0041170C mov ecx,dword ptr [i] // 压栈的是i++的临时变量 0041170F push ecx 00411710 call foo (411096h) 00411715 add esp,0Ch // 弹栈,3个int变量的大小,即清空函数栈
自习看上面的两段汇编代码,就可以明白函数调用是参数的压栈顺序,特别是从第二段汇编代码可以看出,函数压栈顺序是从有到左,先i++,i++,在++i。在压栈之前就已经计算好了各自的值。
参考资料:
1、百度百科,_cdecl,http://baike.baidu.com/view/1280676.htm
2、代码疑云(2)-c函数调用约定,http://blog.csdn.net/zhanxinhang/article/details/6586962
相关文章推荐
- 函数调用约定:__cdecl,__stdcall,__fastcall,__thiscall
- 函数的调用约定(_cdecl,_stdcall,_fastcall,_pascal)
- C/C++中函数调用规则(约定)__cdecl __stdcall __thiscall __vectorcall __fastcall
- 函数调用约定 : _stdcall _cdecl fastcall 调用方式详解
- 函数命名规则及调用约定:__stdcall,__cdecl,__fastcall,thiscall,naked call
- 函数调用约定__stdcall与__cdecl
- 函数调用约定 stdcall,cdecl,fastcall,thiscall,naked call
- 函数调用约定:_stdcall,_cdecl,_fastcall
- 函数调用约定,_cdecl,_stdcall
- 函数调用约定 stdcall cdecl fastcall thiscall naked call
- 函数命名规则及调用约定(__cdecl,__stdcall,__fastcall)
- __cdecl __stdcall __fastcall之函数调用约定讲解
- 函数命名规则及调用约定:__stdcall,__cdecl,__fastcall,thiscall,naked call
- 函数的调用约定——__thiscall,__cdecl,__stdcall
- C/C++中函数的调用约定(__cdecl, __stdcall)以及extern, extern “C"的详细解释
- 汇编学习第五课之函数调用约定:cdecl,stdcall,fastcall
- DLL 导出函数 _stdcall 和 _cdecl调用约定
- 函数调用约定详解:_stdcall,_cdecl,_fastcall
- 函数的调用约定 __stdcall __cdecl __fastcall
- 函数调用约定 __cdecl __fastcall __stdcall