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

从汇编的角度揭开C++ this指针的神秘面纱(上)

C++中的this指针一直比较神秘。任何类的对象,都有一个this指针,无处不在。那么this指针的本质究竟是什么?this指针什么时候会被用到?今天通过几段简单的代码,来揭秘一下。

要先揭秘this指针,先来说一下函数调用时参数的传递过程。考虑以下代码:

int sum(int i, int j, int k)
{return i + j + k;
}int main()
{int a, b, c;a = 1;b = 2;c = 3; sum(a,b,c);return 0;
}

这是一段非常简单的函数调用代码。我们生成其汇编代码(x86-64),如下所示:

sum(int, int, int):pushq   %rbpmovq    %rsp, %rbpmovl    %edi, -4(%rbp)movl    %esi, -8(%rbp)movl    %edx, -12(%rbp)movl    -4(%rbp), %edxmovl    -8(%rbp), %eaxaddl    %eax, %edxmovl    -12(%rbp), %eaxaddl    %edx, %eaxpopq    %rbpret
main:pushq   %rbpmovq    %rsp, %rbpsubq    $16, %rspmovl    $1, -4(%rbp)movl    $2, -8(%rbp)movl    $3, -12(%rbp)movl    -12(%rbp), %edxmovl    -8(%rbp), %ecxmovl    -4(%rbp), %eaxmovl    %ecx, %esimovl    %eax, %edicall    sum(int, int, int)movl    $0, %eaxleaveret

我们重点来关注一下函数参数的传递过程。通过分析main函数的汇编函数, 我用类似于C语言的伪代码解释了一下每一行的意思,辅助理解,如下所示。

main        pushq   %rbpmovq    %rsp, %rbp      //rbp = rspsubq    $16, %rsp       //rsp -= 16movl    $1, -4(%rbp)    //*(rbp-4) = 1movl    $2, -8(%rbp)    //*(rbp-8) = 2 movl    $3, -12(%rbp)   //*(rbp-12) = 3movl    -12(%rbp), %edx //edx =*(rbp-12)movl    -8(%rbp), %ecx   //ecx =*(rbp-8) movl    -4(%rbp), %eax   //eax = *(rbp-4)movl    %ecx, %esi    //esi = ecx movl    %eax, %edi   //edi = eaxcall    sum(int, int, int)movl    $0, %eaxleaveret

在执行这条指令(call sum(int, int, int))前,main函数的栈空间分布如下:

即main函数会存储三个变量: a, b, c. 同时会将其值分别赋值给edi, esi、edx寄存器。那么我们很好奇,将a, b, c三个变量的值赋值给edi, esi、edx寄存器会有什么用呢?我们先来看一下sum函数,我用类似于C语言的伪代码解释了一下每一行的意思,辅助理解,如下所示。

sum(int, int, int):pushq   %rbpmovq    %rsp, %rbp         //rbp = rspmovl    %edi, -4(%rbp)     //*(rbp-4) = edimovl    %esi, -8(%rbp)     //*(rbp-8) = esimovl    %edx, -12(%rbp)    //*(rbp-12) = edxmovl    -4(%rbp), %edx     //edx = *(rbp-4)movl    -8(%rbp), %eax     //eax = *(rbp-8)addl    %eax, %edx         //edx += eax  movl    -12(%rbp), %eax    //eax = *(rbp-12) addl    %edx, %eax         //eax += edx popq    %rbpret

sum函数的栈空间分布如下:

 

我们重点关注一下这几条指令:

 movl    %edi, -4(%rbp)     //*(rbp-4) = edi
 movl    %esi, -8(%rbp)     //*(rbp-8) = esi
 movl    %edx, -12(%rbp)  //*(rbp-12) = edx

可以看到,在sum函数的栈空间中,其会分配三个存储单元,rbp-4, rbp-8, rbp-12存储1,2,3。而1,2,3这三个值分别又是从edi,  esi、edx三个寄存器中拷贝过来的。而这三个寄存器的值又是来自main函数中a, b, c三个变量的赋值。也就是说,这里edi,  esi、edx三个寄存器,在函数调用时,完成了参数的传递。那么这种参数传递的现像是不是有什么约定呢? 答案是有的!

在Linux/macOS 等 Unix-like系统中,函数的调用约定标准为System V AMD64 ABI,其参数传递机制:

参数位置整数/指针寄存器浮点寄存器
第 1 个RDIXMM0
第 2 个RSIXMM1
第 3 个RDXXMM2
第 4 个RCXXMM3
第 5 个R8XMM4
第 6 个R9XMM5
第 7+ 个栈(右→左)XMM6-7

从这个约定中得知,在传递整数时,第一个参数用的是RDI寄存器,第二个参数用的是RSI寄存器,第三个参数用的是RDX寄存器。上面函数调用的例子中正好符合此调用约定(例子中用的是edi,  esi、edx三个寄存器传递第1,第2,第3个参数,而edi,  esi、edx正好是RDI、RSI、RDX三个寄存器的低32位)。

<this指针揭秘继续...>

 

 

 

http://www.xdnf.cn/news/1032301.html

相关文章:

  • Excel大厂自动化报表实战(互联网金融-数据分析周报制作下)
  • Python的“Rstudio“——Pycharm
  • Vue3中v-bind指令用法详解
  • 电脑出问题了,无网络环境下一键快速重装系统
  • 阿尔茨海默病谱系中APOE-ε4携带者的性别特异性超兴奋性:一项纵向fMRI与DTI研究
  • 【消息队列】——消息队列的高可用与容灾设计
  • docker 安装jobe服务器配置Moodle的coderunner插件
  • .net5中用Redis存放session
  • Flask入门:从零搭建Python Web应用
  • JavaScript基础-事件委托(代理、委派)
  • JavaEE初阶第一期:计算机是如何 “思考” 的(上)
  • TerraFE 脚手架开发实战系列(一):项目架构设计与技术选型
  • 基于“数智立体化三维架构”框架的医疗数智化机制研究
  • 【算法篇】逐步理解动态规划模型7(两个数组dp问题)
  • 禾川伺服驱动器与EtherCAT主站转Profinet网关的双向数据交换
  • 【Redis】Redis的启航之路:Ubantu操作系统下安装Redis
  • 计算机网络-自顶向下—第三章运输层重点复习笔记
  • DataHub 架构设计与核心工作原理
  • Hive SQL:一小时快速入门指南
  • 第五章 决策树
  • 关于凸轮的相位角计算
  • 16 celery集成其他工具
  • Blender 案例及基础知识点
  • LIN通信错误 CSError( invalid checksum)
  • Element Plus 去除下拉菜单周黑边
  • 34-Oracle 23 ai 示例数据库部署指南、脚本获取、验证与实操(兼容19c)
  • 嵌入式开发中fmacro-prefix-map选项解析
  • evo工具
  • linux驱动开发(9)- 信号量
  • 《Elasticsearch 分布式搜索在聊天记录检索中的深度优化》