从汇编层面理解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申请的对象,可能会有所不同。

相关推荐
郝学胜_神的一滴3 小时前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
见过夏天18 小时前
C++ 基础入门完全指南
c++
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK3 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境3 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境3 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴4 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境6 天前
C++ 的Eigen 库全解析
c++
卷无止境6 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴6 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake