前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于C++函数返回局部对象的详细分析

关于C++函数返回局部对象的详细分析

作者头像
racaljk
发布2018-08-31 11:17:54
3.4K0
发布2018-08-31 11:17:54
举报
文章被收录于专栏:racaljkracaljk

以前一直挺好奇的,C++是怎么在函数内返回一个局部对象的。因为按照我之前的想法,函数返回一个基本类型的值是通过存放到ecx实现的(关于浮点不了解),但是局部对象又是比较大的,很明显不能使用寄存器作为通用解决方案,虽然也能猜想到可能是用函数栈实现的,但是具体如何没了解过,今天偶有闲时兴趣正浓仔细看了一遍汇编大概了解了 VS编译器对于函数返回局部对象的处理方法, 这里分享出来与君共勉。

代码非常简单,首先定义一个对象,然后定义一个函数返回一个局部对象,最后主函数调用该函数

代码语言:javascript
复制
class ReturnAnObject {
public:
	int arr[10];
	int num;
};


ReturnAnObject returnAnObjectFunc() {
	ReturnAnObject obj;
	obj.num = 0x12345678;
	for (int i = 0; i < 10; i++) {
		obj.arr[i] = i + 3;
	}
	return obj;
}


int main() {
	ReturnAnObject obj;
	obj = returnAnObjectFunc();
	return 0;
}

函数中for循环主要是为了防止报错,为了突出主题,关于设置栈帧和循环的部分就省略了

代码语言:javascript
复制
returnAnObjectFunc:
	ReturnAnObject obj;
	obj.num = 0x12345678;
	for (int i = 0; i < 10; i++) {
		obj.arr[i] = i + 3;
	}
	return obj;
;;;;这里开始正题,首先设置ecx为0Bh用以后面的循环
mov         ecx,0Bh  
;;;;然后获取obj的地址存放到esi
lea         esi,[obj]
;;;;注意这里[ebp+8],很熟悉吧,以前实际参数也是这样获取的
;;;;这里一样,所以我们可以猜想这个函数把局部对象存放到参数上面
;;;;后面如果我们能看到调用这个函数如果还压入其他数据就能肯定我们的猜想
;;;;(因为这个函数是没有参数的)  
mov         edi,dword ptr [ebp+8]
;;;;重复把esi的数据复制到es:[edi],esi会自动向前移动,重复次数为ecx值
rep movs    dword ptr es:[edi],dword ptr [esi]
;;;;顺着我们之前的猜想我们可以进一步认为这个函数已经把局部变量复制到了参数上
;;;;然后返回参数地址相当于返回变量
mov         eax,dword ptr [ebp+8]  

然后看main函数

代码语言:javascript
复制
main:
 push        ebp  
 mov         ebp,esp  
 sub         esp,15Ch  
 push        ebx  
 push        esi  
 push        edi  
 lea         edi,[ebp-15Ch]  
 mov         ecx,57h  
 mov         eax,0CCCCCCCCh  
 rep stos    dword ptr es:[edi]  
	ReturnAnObject obj;
	obj = returnAnObjectFunc();
;;;;注意下面三行代码,这里取main函数栈里面一块很大空间(不全是用于存放函数返回的局部变量)的首地址作为参数压栈
;;;;但是returnAnObjectFunc是没有参数的,而且也不是类成员函数,不存在this的可能
;;;;很明显我们的猜想是正确的,这块内存就用来存放返回的局部变量
 lea         eax,[ebp-158h]  
 push        eax  
 call        returnAnObjectFunc (039B16Fh)
;;;;平衡栈
 add         esp,4  
;;;;这里首先设置循环次数
 mov         ecx,0Bh
;;;;然后把之前returnAnObjectFunc参数地址复制到esi
 mov         esi,eax
;;;;[ebp-124h]就是当前main函数中局部临时变量的地址
 lea         edi,[ebp-124h] 
;;;;重复执行复制之前保存的局部变量到当前局部临时变量
 rep movs    dword ptr es:[edi],dword ptr [esi]
;;;;最后下面这些操作把当前局部临时变量复制到当前局部变量obj出  
 mov         ecx,0Bh  
 lea         esi,[ebp-124h]  
 lea         edi,[obj]  
 rep movs    dword ptr es:[edi],dword ptr [esi]  
	return 0;
;;;;eax清零
 xor         eax,eax  

至此在Debug模式下返回被掉函数局部对象然后赋值给当前调用函数局部变量就完成了,我们可以总结一下: 首先调用函数会在栈内开辟一段内存用来保存被调函数的局部变量,然后把这段内存的首地址压栈并调用函数, 进入被调函数,被调函数会将局部变量复制到压入的参数的那片内存,然后再返回那片内存的首地址 其实到这里局部变量的返回已经结束了,为了加深印象我们在main创建obj然后调用returnAnObjectFunc给它赋值,具体体现到汇编代码就是 在main函数栈中创建一个临时变量然后把returnAnObjectFunc返回的那片内存(通过返回的首地址访问)复制到这个临时变量,再把临时变量复制给当前的局部变量obj 可以改出一段伪代码模拟这段汇编:

代码语言:javascript
复制
void* returnAnObjectFunc(void * address) {
	ReturnAnObject obj;
	obj.num = 0x12345678;
	for (int i = 0; i < 10; i++) {
		obj.arr[i] = i + 3;
	}


	copyObjectData(address,&obj);
	return address;
}


int main() {
	void* newMem = mallocSuitableMemory();
	ReturnAnObject obj;
	newMem = returnAnObjectFunc(newMem);
	ReturnAnObject temp;
	copyObjectData(&temp,newMem);
	obj=temp;
	return 0;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-11-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档