从汇编看c++对象在栈空间内的构造

  我们知道,类对象是c++中很重要的一部分,那么它的大小以及构造方式在汇编代码中是如何实现的呢?在一个程序的虚拟进程空间中,类对象的分布是怎样的呢?它们的构造方式和普通的内置类型有什么区别?下面我们就一起从汇编的角度来看看编译器是如何实现类对象的构造的,本文的所有系统环境环境为ubuntu16.04,G++编译。

  1.让我们来看看最简单的情况:

#include<stdio.h>
#include<iostream>
using namespace std;
class cy{
public:
	int a;
	int b;
	int add(int x,int y){
		int sum;
		sum=a+b;
		return sum;
	}
	cy(int m,int n):a(m),b(n){}
};

int main(){
	int sum;
	cy aa(1,2);
	printf("%d\n",aa.a);
	return 0;
}
让我们来看看这个简单对象构造的汇编代码:

080485fb <main>:
 80485fb:	8d 4c 24 04          	lea    0x4(%esp),%ecx
 80485ff:	83 e4 f0             	and    $0xfffffff0,%esp
 8048602:	ff 71 fc             	pushl  -0x4(%ecx)
 8048605:	55                   	push   %ebp
 8048606:	89 e5                	mov    %esp,%ebp //main函数的堆栈开始;
 8048608:	51                   	push   %ecx //累加寄存器
 8048609:	83 ec 14             	sub    $0x14,%esp //预留20字节空间
 804860c:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax //这条指令暂时没懂
 8048612:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048615:	31 c0                	xor    %eax,%eax  //寄存器清零
 8048617:	83 ec 04             	sub    $0x4,%esp  //调整栈顶指针
 804861a:    6a 02                	push   $0x2  //因为要调用构造函数,这里将构造函数的参数2入栈,从右向左
 804861c:	6a 01                	push   $0x1  //参数1入栈
 804861e:	8d 45 ec             	lea    -0x14(%ebp),%eax  //对象aa的地址,位于ebp寄存器的下方第20字节的地方
 8048621:	50                   	push   %eax  //将对象aa的地址入栈
 8048622:	e8 91 00 00 00       	call   80486b8 <_ZN2cyC1Eii>  //调用类cy的构造函数来初始化对象aa
 8048627:	83 c4 10             	add    $0x10,%esp //函数返回,调整栈顶指针 
 804862a:	8b 45 ec             	mov    -0x14(%ebp),%eax  //将对象aa的地址放入到eax寄存器
 804862d:	83 ec 08             	sub    $0x8,%esp
 8048630:	50                   	push   %eax  //将对象aa的地址入栈
 8048631:	68 50 87 04 08       	push   $0x8048750 //将字符串常量%d\n的地址入栈
 8048636:	e8 95 fe ff ff       	call   80484d0 <printf@plt> //调用printf函数,这里是动态链接
 804863b:	83 c4 10             	add    $0x10,%esp  //调整栈顶指针
 804863e:	b8 00 00 00 00       	mov    $0x0,%eax
 8048643:	8b 55 f4             	mov    -0xc(%ebp),%edx
 8048646:	65 33 15 14 00 00 00 	xor    %gs:0x14,%edx
 804864d: 	74 05                	je     8048654 <main+0x59>
 804864f: 	e8 8c fe ff ff       	call   80484e0 <__stack_chk_fail@plt>
 8048654:	8b 4d fc             	mov    -0x4(%ebp),%ecx
 8048657:	c9                   	leave  
 8048658:	8d 61 fc             	lea    -0x4(%ecx),%esp
 804865b:	c3                   	ret 
以及cy对象的构造函数:

080486b8 <_ZN2cyC1Eii>:
 80486b8:	55                   	push   %ebp  //上一级函数的栈底地址入栈
 80486b9:	89 e5                	mov    %esp,%ebp //将栈底指针移到当前栈顶指针
 80486bb:	8b 45 08             	mov    0x8(%ebp),%eax //将相对与ebp指针向上偏移8个单位地址的值传送到eax寄存器,因为此时ebp之上的内容
//分别为oldebp(函数的返回地址上一级栈帧的ebp,-0x0(%ebp)),ret(函数的返回地址,-0x04(%ebp),aa对象的地址(-0x08(%ebp)),所以这条指令的意思就是将aa对
//象的地址放到eax寄存器                                                           
 80486be:	8b 55 0c             	mov    0xc(%ebp),%edx //将入栈的1的值传送到edx寄存器
 80486c1:	89 10                	mov    %edx,(%eax)  //将1存放到aa对象的前4个字节中
 80486c3:	8b 45 08             	mov    0x8(%ebp),%eax //将aa对象地址再次放到eax寄存器
 80486c6:	8b 55 10             	mov    0x10(%ebp),%edx //将2的值放到edx寄存器
 80486c9:	89 50 04             	mov    %edx,0x4(%eax) //将寄存器edx的值放到相对域aa地址向高地址方向偏移4个字节的地址中(就是aa对象
                                     //成员b的地址)
 80486cc:	90                   	nop
 80486cd:	5d                   	pop    %ebp
 80486ce:	c3                   	ret    
 80486cf:	90                   	nop
2.以对象为参数,返回一个整形值:

class cy{
public:
	int a;
	int b;
	int add(int x,int y){
		int sum;
		sum=a+b;
		return sum;
	}
	cy(int m,int n):a(m),b(n){}
};

int add(cy tmp){
	int sum=0;
	sum=tmp.a+tmp.b;
	return sum;
}

int main(){
	int sum;
	cy aa(1,2);
	sum=add(aa);
	printf("%d\n",sum);
	printf("%p\n",&aa);
	return 0;
}
主要汇编码如下:

 8048622:	55                   	push   %ebp
 8048623:	89 e5                	mov    %esp,%ebp
 8048625:	51                   	push   %ecx
 8048626:	83 ec 14             	sub    $0x14,%esp
 8048629:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 804862f:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048632:	31 c0                	xor    %eax,%eax
 8048634:	83 ec 04             	sub    $0x4,%esp
 8048637:	6a 02                	push   $0x2
 8048639:	6a 01                	push   $0x1
 804863b:	8d 45 ec             	lea    -0x14(%ebp),%eax //类对象aa的地址
 804863e:	50                   	push   %eax
 804863f:	e8 b8 00 00 00       	call   80486fc <_ZN2cyC1Eii> //调用类cy构造函数
 8048644:	83 c4 10             	add    $0x10,%esp 
 8048647:	83 ec 08             	sub    $0x8,%esp
 804864a:	ff 75 f0             	pushl  -0x10(%ebp)  //将2的值入栈
 804864d:	ff 75 ec             	pushl  -0x14(%ebp)   //将1的值入栈,那么相当与对象aa入栈
 8048650:	e8 a6 ff ff ff       	call   80485fb <_Z3add2cy>
 8048655:	83 c4 10             	add    $0x10,%esp
 8048658:	89 45 e8             	mov    %eax,-0x18(%ebp) //eax保存返回值,然后将返回值赋值sum,课件变量sum的地址为-0x18(%ebp)
我们可以看到类对象作参数时,将对象的每个变量的值依次入栈。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值