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

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编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

springboot security使用jwt认证方式

《springbootsecurity使用jwt认证方式》:本文主要介绍springbootsecurity使用jwt认证方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录前言代码示例依赖定义mapper定义用户信息的实体beansecurity相关的类提供登录接口测试提供一

springboot security之前后端分离配置方式

《springbootsecurity之前后端分离配置方式》:本文主要介绍springbootsecurity之前后端分离配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的... 目录前言自定义配置认证失败自定义处理登录相关接口匿名访问前置文章总结前言spring boot secu

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

SpringBoot中封装Cors自动配置方式

《SpringBoot中封装Cors自动配置方式》:本文主要介绍SpringBoot中封装Cors自动配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot封装Cors自动配置背景实现步骤1. 创建 GlobalCorsProperties

Flutter打包APK的几种方式小结

《Flutter打包APK的几种方式小结》Flutter打包不同于RN,Flutter可以在AndroidStudio里编写Flutter代码并最终打包为APK,本篇主要阐述涉及到的几种打包方式,通... 目录前言1. android原生打包APK方式2. Flutter通过原生工程打包方式3. Futte