__stdcall和__cdecl的区别

  • __cdecl
#include 
using namespace std;
int __cdecl add(int a,int b)
{
	return 0;
}
int main()
{
	int a=1;
	int b=2;
	add(a,b);
	int c=3;
	return 0;
}
_main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$32, %esp
	call	___main
	movl	$1, 28(%esp)
	movl	$2, 24(%esp)
	movl	24(%esp), %eax
	movl	%eax, 4(%esp)
	movl	28(%esp), %eax
	movl	%eax, (%esp)
	call	__Z3addii
	movl	$3, 20(%esp)
	movl	$0, %eax
	leave
	ret
  • __stdcall
```cpp
#include 
using namespace std;
int __stdcall add(int a,int b)
{
	return 0;
}
int main()
{
	int a=1;
	int b=2;
	add(a,b);
	int c=3;
	return 0;
}
_main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ecx
	subl	$36, %esp
	call	___main
	movl	$1, -12(%ebp)
	movl	$2, -16(%ebp)
	movl	-16(%ebp), %eax
	movl	%eax, 4(%esp)
	movl	-12(%ebp), %eax
	movl	%eax, (%esp)
	call	__Z3addii@8
	subl	$8, %esp
	movl	$3, -20(%ebp)
	movl	$0, %eax
	movl	-4(%ebp), %ecx
	leave
	leal	-4(%ecx), %esp
	ret
  1.  _cdecl
    

(1). 是C Declaration的缩写,表示C语言默认的函数调用方法,实际上也是C++的默认的函数调用方法。

(2). 所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。具体所示:调用方的函数调用->被调用函数的执行->被调用函数的结果返回->调用方清除调整堆栈。

(3). 被调用函数无需要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。总的来说函数的参数个数可变的(就像printf函数一样),因为只有调用者才知道它传给被调用函数几个参数,才能在调用结束时适当地调整堆栈。

(4). 因为每个调用的地方都需要生成一段调整堆栈的代码,所以最后生成的文件较大。

  1.  _stdcall(CALLBACK/WINAPI)
    

(1). 是Standard Call的缩写,要想函数按照此调用方式必须在函数名加入_stdcall,通常_ win32 api应该是_stdcall调用规则。通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为_stdcall 方式,WINAPI都采用这种方式。

(2). 所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。具体所示:调用方的函数调用->被调用函数的执行->被调用方清除调整堆栈->被调用函数的结果返回。

(3). 这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retn X,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。

(4). 函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。总的来说,就是函数的参数个数不能是可变的。是从 _cdecl 修改而来,_stdcall 不支持可变参数,并且清栈由被调用者负责,其他的都一样

(5). 因为只需在被调用函数的地方生成一段调整堆栈的代码,所以最后生成的文件较小。

参考:_stdcall与_cdel

你可能感兴趣的:(C++(accumulate))