废话不多说,请直接看代码:
#include <stdio.h>
int _stdcall Add(int a, int b)
{
return a+b;
}
void main()
{
int c = Add(3,4);
printf("c = %d\n", c);
}
我们现在就取main函数中,两个语句之间的汇编代码,从而来实质性的区别两种调用规则。
int c = Add(3,4);
0041141E push 4
00411420 push 3
00411422 call Add (4111DBh)
00411427 mov dword ptr [c],eax
printf("c = %d\n", c);
现在,我们将Add前的_stdcall换成_cdcel看看,结果有什么不同。
#include <stdio.h>
int _cdecl Add(int a, int b)
{
return a+b;
}
void main()
{
int c = Add(3,4);
printf("c = %d\n", c);
}
新的结果是:
int c = Add(3,4);
0041141E push 4
00411420 push 3
00411422 call Add (4111E0h)
00411427 add esp,8
0041142A mov dword ptr [c],eax
printf("c = %d\n", c);
这两种汇编结果的不同之处相信大家一眼可以看出来。后者在函数返回值被赋给c之前,有主调用者(main)来清理了Add函数的参数
栈空间,而前者是由Add函数自身来清理的。入参的顺序都是一样的,这个没有区别,关键在与清理参数的执行者不同。为此,我还是将_stdcall调用的清理过程列出来比较好,大家可以一下子有清新的认识。
int _stdcall Add(int a, int b)
{
004113C0 push ebp
004113C1 mov ebp,esp
004113C3 sub esp,0C0h
004113C9 push ebx
004113CA push esi
004113CB push edi
004113CC lea edi,[ebp-0C0h]
004113D2 mov ecx,30h
004113D7 mov eax,0CCCCCCCCh
004113DC rep stos dword ptr es:[edi]
return a+b;
004113DE mov eax,dword ptr [a]
004113E1 add eax,dword ptr [b]
}
004113E4 pop edi
004113E5 pop esi
004113E6 pop ebx
004113E7 mov esp,ebp
004113E9 pop ebp
004113EA ret 8
看到最后一句:ret 8就是清理过程。这下子,明白了吧。