C++对象 内存分布

代码如下:

    class T {
    public :
        T();
        virtual int f(void);
    private :
        int d;
    };

T::T(void)
{
	d = 0x12345678;
}

int T::f(void)
{
	return 12;
}

void x(T& t) {
        t.f();
    }

int main(void)
{
	T t;
	x(t);
	return 0;
}

下面通过汇编来分析类 T 对象的内存分布。

1. 在  main 函数第一行设置断点, 观察执行  T的构造函数之前的情形:

Dump of assembler code for function main():
   0x00010650 <+0>:	push	{r11, lr}
   0x00010654 <+4>:	add	r11, sp, #4
   0x00010658 <+8>:	sub	sp, sp, #8
   0x0001065c <+12>:	sub	r3, r11, #12
   0x00010660 <+16>:	mov	r0, r3
=> 0x00010664 <+20>:	bl	0x105bc <T::T()>
   0x00010668 <+24>:	sub	r3, r11, #12
   0x0001066c <+28>:	mov	r0, r3
   0x00010670 <+32>:	bl	0x10624 <x(T&)>
   0x00010674 <+36>:	mov	r3, #0
   0x00010678 <+40>:	b	0x10680 <main()+48>
   0x0001067c <+44>:	bl	0x10470 <__cxa_end_cleanup@plt>
   0x00010680 <+48>:	mov	r0, r3
   0x00010684 <+52>:	sub	sp, r11, #4
   0x00010688 <+56>:	pop	{r11, pc}

此时的栈结构如下:


(gdb) p /x $r0
$3 = 0xbefffcc0
(gdb) p /x $sp
$4 = 0xbefffcc0
此时, r0指向 对象 t 在栈上的初始地址(对象 t大小为2字节)。

2.下面进入 T的构造函数:

(gdb) disass
Dump of assembler code for function T::T():
   0x000105bc <+0>:	push	{r11}		; (str r11, [sp, #-4]!)
   0x000105c0 <+4>:	add	r11, sp, #0
   0x000105c4 <+8>:	sub	sp, sp, #12
   0x000105c8 <+12>:	str	r0, [r11, #-8]
   0x000105cc <+16>:	ldr	r3, [r11, #-8]
   0x000105d0 <+20>:	ldr	r2, [pc, #36]	; 0x105fc <T::T()+64>
   0x000105d4 <+24>:	str	r2, [r3]
   0x000105d8 <+28>:	ldr	r2, [r11, #-8]
   0x000105dc <+32>:	movw	r3, #22136	; 0x5678
   0x000105e0 <+36>:	movt	r3, #4660	; 0x1234
   0x000105e4 <+40>:	str	r3, [r2, #4]
   0x000105e8 <+44>:	ldr	r3, [r11, #-8]
   0x000105ec <+48>:	mov	r0, r3
=> 0x000105f0 <+52>:	sub	sp, r11, #0
   0x000105f4 <+56>:	pop	{r11}		; (ldr r11, [sp], #4)
   0x000105f8 <+60>:	bx	lr
   0x000105fc <+64>:	andeq	r0, r1, r0, lsl r7
此时,栈上的内容为:


3. 在 x()里面的情形:

Dump of assembler code for function x(T&):
=> 0x00010624 <+0>:	push	{r11, lr}
   0x00010628 <+4>:	add	r11, sp, #4
   0x0001062c <+8>:	sub	sp, sp, #8
   0x00010630 <+12>:	str	r0, [r11, #-8]
   0x00010634 <+16>:	ldr	r3, [r11, #-8]
   0x00010638 <+20>:	ldr	r3, [r3]
   0x0001063c <+24>:	ldr	r3, [r3]
   0x00010640 <+28>:	ldr	r0, [r11, #-8]
   0x00010644 <+32>:	blx	r3
   0x00010648 <+36>:	sub	sp, r11, #4
   0x0001064c <+40>:	pop	{r11, pc}
(gdb) p /x $sp
$20 = 0xbefffcc0
(gdb) p /x $r0
$21 = 0xbefffcc0
(gdb) p /x $r11
$22 = 0xbefffccc

Dump of assembler code for function x(T&):
   0x00010624 <+0>:	push	{r11, lr}
   0x00010628 <+4>:	add	r11, sp, #4
   0x0001062c <+8>:	sub	sp, sp, #8
   0x00010630 <+12>:	str	r0, [r11, #-8]
   0x00010634 <+16>:	ldr	r3, [r11, #-8]
   0x00010638 <+20>:	ldr	r3, [r3]
   0x0001063c <+24>:	ldr	r3, [r3]
   0x00010640 <+28>:	ldr	r0, [r11, #-8]
=> 0x00010644 <+32>:	blx	r3
   0x00010648 <+36>:	sub	sp, r11, #4
   0x0001064c <+40>:	pop	{r11, pc}
End of assembler dump.
(gdb) p /x $r3
$25 = 0x10600
(gdb) x $r3   
0x10600 <T::f()>:	0xe52db004
(gdb) p /x $r0
$26 = 0xbefffcc0
(gdb) x $r0   
0xbefffcc0:	0x00010710
(gdb) x 0x00010710
0x10710 <_ZTV1T+8>:	0x00010600

此时,栈以及调用关系如下:

如前所说, T 占了8个字节,一个4字节是成员变量的,另一个就是vtbl (虚表)指针所占用。这里虚表指向  T::f()的地址。

虚表信息也可以从 gdb观察到:

(gdb) p t
$28 = {_vptr.T = 0x10710 <vtable for T+8>, d = 305419896}
(gdb) info vtbl t
vtable for 'T' @ 0x10710 (subobject @ 0xbefffcc0):
[0]: 0x10600 <T::f()>
(gdb) p &t
$29 = (T *) 0xbefffcc0


reference:

1. http://blog.httrack.com/blog/2014/05/09/a-basic-glance-at-the-virtual-table/

2.http://www.lrdev.com/lr/c/virtual.html

3.https://cloudfundoo.wordpress.com/2012/04/27/deep-c-understanding-c-object-layout/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值