从汇编层面理解C++ this指针

c++中,调用对象的非静态成员函数过程中,编译器会自动添加this指针作为第一个参数。我们从汇编层面看看this指针是如何传进来的。

使用如下简单的程序来演示:

cpp 复制代码
class A
{
private:
  int a[100];
  int b;
public:
  void SetB(int b)
  { this->b = b;}
  int GetB() { return this->b;}
};


int main()
{
  A oa ;
  oa.SetB(0x888);

  int b = oa.GetB();
  return 0;
}

编译后反汇编A::SetB汇编如下:

shell 复制代码
0000000000400536 <_ZN1A4SetBEi>:
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
  40053a:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  40053e:       89 75 f4                mov    %esi,-0xc(%rbp)
  400541:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  400545:       8b 55 f4                mov    -0xc(%rbp),%edx
  400548:       89 90 90 01 00 00       mov    %edx,0x190(%rax)
  40054e:       5d                      pop    %rbp
  40054f:       c3                      retq

可以看到,寄存器rdiesi分别表示第一个参数this和第二个参数int b

最后通过mov %edx,0x190(%rax) 指令实现this->b = b;

类内变量b相对于对象起始地址this的偏移是0x190,可以通过gdb验证偏移量:

shell 复制代码
(gdb) p &(((A*)0)->b)
$1 = (int *) 0x190
(gdb)

可以看到, 编译器确实将this指针作为第一个参数传到非静态成员函数中了。

看了成员函数的汇编, 再看看main函数中是如何调用的。

main函数的汇编如下:

shell 复制代码
00000000004004fd <main>:
  4004fd:       55                      push   %rbp
  4004fe:       48 89 e5                mov    %rsp,%rbp
  400501:       48 81 ec a0 01 00 00    sub    $0x1a0,%rsp
  400508:       48 8d 85 60 fe ff ff    lea    -0x1a0(%rbp),%rax
  40050f:       be 88 08 00 00          mov    $0x888,%esi
  400514:       48 89 c7                mov    %rax,%rdi
  400517:       e8 1a 00 00 00          callq  400536 <_ZN1A4SetBEi>
  40051c:       48 8d 85 60 fe ff ff    lea    -0x1a0(%rbp),%rax
  400523:       48 89 c7                mov    %rax,%rdi
  400526:       e8 25 00 00 00          callq  400550 <_ZN1A4GetBEv>
  40052b:       89 45 fc                mov    %eax,-0x4(%rbp)
  40052e:       b8 00 00 00 00          mov    $0x0,%eax
  400533:       c9                      leaveq
  400534:       c3                      retq
  400535:       90                      nop

main函数中定义了局部变量oa,在栈上申请内存。

第三条指令sub $0x1a0,%rsp, 表示在栈上申请了0x1a0个字节,此时%rsp=-0x1a0(%rbp)-0x1a0(%rbp)其实就是栈顶,也是对象oa的地址,即this指针。

第四条指令lea -0x1a0(%rbp),%rax执行后,this指针保存在%rax中。

第6/7条指令,分别将this指针及变量int b(0X888)放入rdirsi表示第一/二个参数, 然后调用A::SetB

上述程序,对象在栈上。 如果对象在堆上的情况,即通过new申请的对象,可能会有所不同。

相关推荐
Lccee23 分钟前
Windows安装 PHP 8 和mysql9,win下使用phpcustom安装php8.4.5和mysql9
开发语言·php
Yang-Never30 分钟前
Open GL ES ->GLSurfaceView在正交投影下的图片旋转、缩放、位移
android·开发语言·kotlin·android studio·贴图
niuniu_6661 小时前
针对 Python 3.7.0,以下是 Selenium 版本的兼容性建议和安装步骤
开发语言·chrome·python·selenium·测试工具
zyx没烦恼1 小时前
Linux 下 日志系统搭建全攻略
linux·服务器·开发语言·c++
苏卫苏卫苏卫1 小时前
【Python】数据结构练习
开发语言·数据结构·笔记·python·numpy·pandas
审计侠1 小时前
Go语言-初学者日记(四):包管理
开发语言·后端·golang
辰辰大美女呀1 小时前
C 语言高级编程指南:回调函数与设计模式
c语言·开发语言·设计模式
冰红茶兑滴水2 小时前
Qt 音乐播放器项目
开发语言·qt
MCYH02062 小时前
C++抽卡模拟器
java·c++·算法·概率·原神
pystraf2 小时前
P10587 「ALFR Round 2」C 小 Y 的数 Solution
数据结构·c++·算法·线段树·洛谷