C/C++ 您当前所在位置:首页 > C/C++ > 调用约定是怎么回事

调用约定是怎么回事

陈成 陈成 2020-07-11 11:19:05 C/C++ 177人已围观

简介 介绍调用约定之前先看看下面的情况。

介绍调用约定之前先看看下面的情况。

我们首先定义一个普通函数,调用之后看看反汇编。

int test(int a, int b)
{
    return a + b;
}
int main(int argc, char* argv[])
{
	test(1,2);
	return 0;
}

反汇编。

0040D4E8   push        2
0040D4EA   push        1
0040D4EC   call        @ILT+25(test) (0040101e)
0040D4F1   add         esp,8

我们看到传参数push的时候入栈顺序是先push 2,再push 1,最后面还有一个add esp,8 ,这个esp加8是为了堆栈平衡,在call后面调用我们称为外平栈。但这种情况不是固定的,是可以改的,所以我们要了解调用约定。

常见的几种调用约定

1、__cdecl

这个调用约定入栈的顺序是从右到左,谁调用这个约定的函数谁就清理堆栈。

int __cdecl test1(int a,int b)
{
    return a + b;
}

C或C++中这个调用约定是默认的,这个情况和上面调用test函数一样,就不测试了。

2、__stdcall

这个调用约定入栈的顺序还是从右到左,它自身就可以清理堆栈。

int __stdcall test2(int a, int b)
{
    return a + b;
}
int main(int argc, char* argv[])
{
	test2(1,2);
	return 0;
}

看反汇编

0040D4E8   push        2
0040D4EA   push        1
0040D4EC   call        @ILT+30(Func) (00401023)

还是先push 2,再push 1,最后也没有esp加8的指令,我们再进入call里面看看。

0040104E   pop         edi
0040104F   pop         esi
00401050   pop         ebx
00401051   mov         esp,ebp
00401053   pop         ebp
00401054   ret         8

看到最后一行,ret 8,自身就将堆栈清理了。

3、__fastcall

这种调用约定还是从右到左入栈,它也是自身清理堆栈。不同的是这种调用约定的参数如果小于等于2个,那这个参数就会传到ECX和EDX寄存器,就没有push,没有push也就不用清理堆栈。但如果参数超过两个,就会将多的参数push到堆栈,所以这情况参数小于等于2个的话特点就是快,如果参数大于2个,使用__fastcall意义就不大了。

int __fastcall test3(int a,int b)
{
    return a;
}
int main(int argc, char* argv[])
{
    test3(1,2);
    return 0;
}

看反汇编

00401078   mov         edx,2
0040107D   mov         ecx,1
00401082   call        @ILT+0(test3) (00401005)

再进入call看看ret。

00401043   pop         edi
00401044   pop         esi
00401045   pop         ebx
00401046   mov         esp,ebp
00401048   pop         ebp
00401049   ret

我们可以看到传了两个参数分别放到ecx,edx中。进入call里面,看到ret后面为空,call后面也没有esp加8,所以这个种情况不需要清理堆栈。

最后我们再看看参数2个以上参数。

int __fastcall test3(int a,int b,int c,int d,int e)
{
    return a;
}
int main(int argc, char* argv[])
{
    test3(1,2,3,4,5);
    return 0;
}

反汇编

00401078   push        5
0040107A   push        4
0040107C   push        3
0040107E   mov         edx,2
00401083   mov         ecx,1
00401088   call        @ILT+10(test3) (0040100f)

进入call

00401043   pop         edi
00401044   pop         esi
00401045   pop         ebx
00401046   mov         esp,ebp
00401048   pop         ebp
00401049   ret         0Ch

看出什么来了吗?都是从右往左传参,超过2个参数把第一、二个参数放在ecx,edx,剩余的入栈。最后push了三个参数,就ret 0xc。

文章评论

发送

站点信息

  • 上线时间:2020年05月30日
  • 网站程序:自建框架
  • 博客模板:今夕何夕
  • 文章统计43篇文章
  • 评论统计0条评论
  • 留言统计0条留言
  • 统计数据:百度统计