编译器原理-函数调用约定/调用规范/传参方式

2024-06-13 13:18

本文主要是介绍编译器原理-函数调用约定/调用规范/传参方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

cdecl:使用栈传参,通常使用ax寄存器存放返回值,由调用方重置sp
stdcall:使用栈传参,通常使用ax寄存器存放返回值,由被调用方重置sp
fastcall:约定优先使用寄存器传递参数,其次使用栈,由不同的编译器实现,咱自己也可以实现一个
thiscall:入参的时候多了一个当前对象(this)指针

本文的示例代码在visual studio 2019下写的,下面是一段最简单不过的C代码

int add_function(int a,int b,int c) {return a + b + c;
}int main()
{int aa=add_function(1, 2, 3);
}

cdecl(C Declaration) 约定
将被调用的函数需要的参数,压栈,当被调用的函数执行完毕,调用方负责重置SP的高度
本例中,main方法调用add_function之前,先push 1,2,3,然后调用add_function,add_function执行完毕,由main负责重置SP
下面的代码是mian函数调用add_function前后的一波操作

push        3  
push        2  
push        1  
call        add_function(0371037h)  
add         esp,0Ch
mov        dword ptr [aa],eax  

下面的代码是add_function函数执行前后的一波操作

// 函数序言
push        ebp  
mov         ebp,esp  
sub         esp,0C0h  
// 开始执行a + b + c,并把结果放到eax中
mov         eax,dword ptr [ebp+8]  
add         eax,dword ptr [ebp+0Ch]  
add         eax,dword ptr [ebp+10h] 
// 函数尾声
mov         esp,ebp  
pop         ebp  
ret  

下面把上述两段代码合并到一起
在这里插入图片描述

在这里插入图片描述

以上就是cdecl约定,需要记住的是调用方负责重置SP高度,在本例的代码是main中的add esp,0Ch

stdcall(Standard Call) 约定

ret x指令:将SP-x,逻辑代码为:sp=sp-x

将add_function函数前面加上_stdcall ,编译器会按照stdcall约定编译

int _stdcall add_function(int a,int b,int c) {return a + b + c;
}

编译之后,add_function函数的尾声部分汇编代码如下

mov         esp,ebp  
pop         ebp  
ret         0Ch			 ;此处和cdecl约定不同,cdecl直接ret,而此处ret 0Ch

main函数汇编代码片段如下

push        3  
push        2  
push        1  
call        add_function(07113CAh)  ;此处call之后和cdecl约定不同,cdecl有个add esp,0Ch操作,而此处没有
mov        dword ptr [aa],eax  

通过上述代码已经发现,stdcall约定中重置SP操作是在被调用函数(本例add_function)中做的,这是和cdecl约定不同的地方

fastcall
约定优先使用寄存器传递参数,如果无法通过寄存器,则使用栈传递参数,没有统一实现方式,不同的编译器有不同的策略

thiscall
为面向对象语言设计的调用规范,传参的时候,多出了一个this引用,按照本例的add_function函数来说,例子中是传递3个int,而如果这个函数在一个C++对象中,那么实际传递的参数是4个,多出了一个当前对象的指针,在GCC编译器中使用栈传递这个指针,MSVC中使用ECX寄存器,那么很明显,Java中就是thiscall调用约定,至于重置sp是同cdecl还是stdcall,在vs中是同stdcall的,而java中的ret指令也同样是stdcall

这篇关于编译器原理-函数调用约定/调用规范/传参方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057411

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

Vue3视频播放组件 vue3-video-play使用方式

《Vue3视频播放组件vue3-video-play使用方式》vue3-video-play是Vue3的视频播放组件,基于原生video标签开发,支持MP4和HLS流,提供全局/局部引入方式,可监听... 目录一、安装二、全局引入三、局部引入四、基本使用五、事件监听六、播放 HLS 流七、更多功能总结在 v