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

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

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

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循环主要是为了防止报错,为了突出主题,关于设置栈帧和循环的部分就省略了

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函数

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 可以改出一段伪代码模拟这段汇编:

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;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【编程基础】C语言指针、引用和取值

什么是指针?什么是内存地址?什么叫做指针的取值?指针是一个存储计算机内存地址的变量。“引用”表示计算机内存地址。从指针指向的内存读取数据称作指针的取值。指针可以...

3687
来自专栏抠抠空间

集合 (set) 的增删改查及 copy()方法

简介: 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的。以下是集合最重要的两点: 1、...

30411
来自专栏从流域到海域

《笨办法学Python》 第32课手记

《笨办法学Python》 第32课手记 本节课讲for循环和list,list里类似于c中的数组,但有区别很大。C语言中的数组是数据类型相同的值的集合,list...

2059
来自专栏nummy

python operator模块学习

operator模块是python中内置的操作符函数接口,它定义了一些算术和比较内置操作的函数。operator模块是用c实现的,所以执行速度比python代码...

752
来自专栏一“技”之长

Swift专题讲解十八——异常处理 原

        代码的运行很多时候并不会完全按照程序员的设想进行,编写代码时进行可控的异常处理机制是十分必要的。通常,对于一个特定的操作,程序员可以定义一个继承...

722
来自专栏程序员互动联盟

【编程基础】C语言位

位制就是为了减小存储大小,把一个char, unsigned int, int的存储空间进行拆分后,对每个进行操作。 给出一段代码: #include <st...

3237
来自专栏五分钟学算法

每天一算:Intersection of Two Arrays II

leetcode上第350号问题:Intersection of Two Arrays II

992
来自专栏技术博文

php http_build_query 拼接数组 可以用这个函数

http_build_query (PHP 5) http_build_query — 生成 URL-encode 之后的请求字符串 说明 string htt...

3454
来自专栏蜕变

Python 数据类型

Python主要数据类型包括list(列表)、tuple(元组)、dict(字典)和set(集合)等对象,下面逐一介绍这些Python数据类型。

820
来自专栏决胜机器学习

PHP面向对象核心(二)——继承、多态、接口

PHP面向对象核心(二) (原创内容,转载请注明来源,谢谢) 三、继承与多态 3.1 继承 1、继承是类级别的复用,关键词为extends;多态是方法级别的复用...

37312

扫码关注云+社区

领取腾讯云代金券