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

相关推荐
李白同学12 分钟前
【C语言】结构体内存对齐问题
c语言·开发语言
黑子哥呢?1 小时前
安装Bash completion解决tab不能补全问题
开发语言·bash
青龙小码农2 小时前
yum报错:bash: /usr/bin/yum: /usr/bin/python: 坏的解释器:没有那个文件或目录
开发语言·python·bash·liunx
大数据追光猿2 小时前
Python应用算法之贪心算法理解和实践
大数据·开发语言·人工智能·python·深度学习·算法·贪心算法
Dream it possible!2 小时前
LeetCode 热题 100_在排序数组中查找元素的第一个和最后一个位置(65_34_中等_C++)(二分查找)(一次二分查找+挨个搜索;两次二分查找)
c++·算法·leetcode
柠石榴2 小时前
【练习】【回溯No.1】力扣 77. 组合
c++·算法·leetcode·回溯
王老师青少年编程2 小时前
【GESP C++八级考试考点详细解读】
数据结构·c++·算法·gesp·csp·信奥赛
彳卸风2 小时前
Unable to parse timestamp value: “20250220135445“, expected format is
开发语言
dorabighead3 小时前
JavaScript 高级程序设计 读书笔记(第三章)
开发语言·javascript·ecmascript
风与沙的较量丶3 小时前
Java中的局部变量和成员变量在内存中的位置
java·开发语言