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

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

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

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汇编如下:

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验证偏移量:

(gdb) p &(((A*)0)->b)
$1 = (int *) 0x190
(gdb)

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

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

main函数的汇编如下:

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申请的对象,可能会有所不同。

你可能感兴趣的:(c++,汇编,汇编,c++,开发语言)