当前位置: 首页 > news >正文

127.【C语言】补充:函数的三种调用约定

目录

0.准备

1._cdecl(全称c declaration)

示例代码1

反汇编示例代码1 后

2._stdcall(全称:standard call)

反汇编后

_stdcall较_cdecall的好处

示例代码2

3._fastcall

示例代码3


0.准备

运行环境:VS2010

1._cdecl(全称c declaration)

示例代码1

int add(int a,int b)
{return a + b;
}int main()
{add(1, 2);
}

反汇编示例代码1 后

int add(int a,int b)
{push        ebp  mov         ebp,esp  sub         esp,40h  push        ebx  push        esi  push        edi  return a + b;mov         eax,dword ptr [a]  add         eax,dword ptr [b]  
}pop         edi  pop         esi  pop         ebx  mov         esp,ebp  pop         ebp  ret  int main()
{push        ebp  mov         ebp,esp  sub         esp,40h  push        ebx  push        esi  push        edi  add(1, 2);push        2  push        1  call        @ILT+130(_add)add         esp,8  
}xor         eax,eax  pop         edi  pop         esi  pop         ebx  mov         esp,ebp  pop         ebp  ret  

问题1:改成int _cdecl add(int a,int b)后观察修改前后的反汇编代码是否有区别?

答:没有区别,可以得出结论:Windows平台下,如果函数省略调用约定,则默认为_cdecl调用约定

2._stdcall(全称:standard call)

问题2:将示例代码1的add函数改成int _stdcall add(int a,int b)后观察修改前后的反汇编代码是否有区别?

反汇编后

int _stdcall add(int a,int b)
{push        ebp  mov         ebp,esp  sub         esp,40h  push        ebx  push        esi  push        edi  return a + b;mov         eax,dword ptr [a]  add         eax,dword ptr [b]  
}pop         edi  pop         esi  pop         ebx  mov         esp,ebp  pop         ebp  ret         8  int main()
{push        ebp  mov         ebp,esp  sub         esp,40h  push        ebx  push        esi  push        edi  add(1, 2);push        2  push        1  call        @ILT+290(_add) (0CA1127h)  
}xor         eax,eax  pop         edi  pop         esi  pop         ebx  mov         esp,ebp  pop         ebp  ret  

答:有区别,区别在下图

71c7e88238f94925a80c1f4f8cfba5ba.png

先引入两个名词:调用者和被调用者,例如本代码的main函数为调用者,其调用add函数,则add函数为被调用者

区别在:清理栈(防止栈溢出)的方式不同,其中add esp,8和ret 8均为清理栈的指令,其中_cdecl调用约定为调用者清栈,而_stdcall为被调用者清栈(即两者清栈的执行对象不同)

_stdcall较_cdecall的好处

示例代码2

int add(int a,int b)
{return a + b;
}int main()
{add(1, 2);add(3, 4);add(5, 6);add(7, 8);add(9, 10);
}

观察反汇编示例代码2后的main函数的汇编代码

9af326d9eacc4aac8ca8a616eb398e64.png

对比_stdcall调用约定,_cdecl在多次调用同一个函数有缺点:add esp,8会多次重复,占用栈帧空间

而_stdcall是被调用者清栈,一劳永逸,不会多次重复

结论:如果多次调用同一个函数,_stdcall较_cdecl节省栈帧空间

3._fastcall

顾名思义,为快速调用

问题3:将示例代码1的add函数改成int _fastcall add(int a,int b)后观察修改前后的反汇编代码是否有区别?

int _fastcall add(int a,int b)
{push        ebp  mov         ebp,esp  sub         esp,48h  push        ebx  push        esi  push        edi  mov         dword ptr [ebp-8],edx  mov         dword ptr [ebp-4],ecx  return a + b;mov         eax,dword ptr [a]  add         eax,dword ptr [b]  
}pop         edi  pop         esi  pop         ebx  mov         esp,ebp  pop         ebp  ret  int main()
{push        ebp  mov         ebp,esp  sub         esp,40h  push        ebx  push        esi  push        edi  add(1, 2);mov         edx,2  mov         ecx,1  call        @ILT+295(_add@8) (0EF112Ch)  
}xor         eax,eax  pop         edi  pop         esi  pop         ebx  mov         esp,ebp  pop         ebp  ret 

答:有区别,区别在下图

0c5a77c508de4dd5932920223791d62f.png

对比后发现:_fastcall在传参时使用寄存器,而_cdecl在传参时使用

之前在98.【C语言】存储体系结构文章讲过,寄存器的速度是快的,因此用寄存器传参比用栈传参要快

108925d2f66b4bf8b0a3afb734483939.png

(注;图来自《深入了解计算机系统》)

示例代码3

int _fastcall add(int a,int b,int c,int d)
{return a + b+c+d;
}int main()
{add(1, 2, 3, 4);
}

问题4:反汇编以上代码,观察传参指令有什么特点

 push        4  push        3  mov         edx,2  mov         ecx,1 

答:显然当参数过多,寄存器不够用时,会用栈来辅助传参,而且一般情况下,最左边的两个参数(add(1,2,3,4)的1和2)交给寄存器去传参,其余交给栈传参


http://www.mrgr.cn/news/83649.html

相关文章:

  • 论文笔记(四十七)Diffusion policy: Visuomotor policy learning via action diffusion(下)
  • vue2修改表单只提交被修改的数据的字段传给后端接口
  • 一个简单的html5导航页面
  • shell脚本回顾1
  • Windows 10 ARM工控主板连接I2S音频芯片
  • 【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解
  • SpringBoot:使用HTTP2+protobuf实现高性能微服务调用
  • 【Linux】Linux开发:GDB调试器与Git版本控制工具指南
  • JVM:ZGC详解(染色指针,内存管理,算法流程,分代ZGC)
  • 拷贝构造函数
  • 基于深度学习的视觉检测小项目(十二) 使用线条边框和渐变颜色美化界面
  • pytest+request+yaml+allure搭建低编码调试门槛的接口自动化框架
  • 指针的进阶
  • 蓝耘:GPU算力云服务的技术探索与AIGC应用支持
  • 渗透Vulnhub-hackme靶机
  • Windows 11更新之后卡顿 (黑神话掉帧严重)问题探索
  • 聊聊AI Agent
  • windows及linux 安装 Yarn 4.x 版本
  • Linux的基础IO
  • SpringBoot之LazyInitializationBeanFactoryPostProcessor类源码学习
  • 金融项目实战 04|JMeter实现自动化脚本接口测试及持续集成
  • LLMBook 大模型数据集下载地址完整收集
  • Github配置ssh key,密钥配对错误怎么解决?
  • Open FPV VTX开源之第一次出图
  • Day05-后端Web基础——TomcatServletHTTP协议SpringBootWeb入门
  • 【AIDD药物研发】张载熙-生成式AI4药物发现