关于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;
}
关于C++函数返回局部对象的详细分析的更多相关文章
- 条款31: 千万不要返回局部对象的引用,也不要返回函数内部用new初始化的指针的引用
先看第一种情况:返回一个局部对象的引用.它的问题在于,局部对象 ----- 顾名思义 ---- 仅仅是局部的.也就是说,局部对象是在被定义时创建,在离开生命空间时被销毁的.所谓生命空间,是指它们所在的 ...
- C++ //拷贝构造函数调用时机//1.使用一个已经创建完毕的对象来初始化一个新对象 //2.值传递的方式给函数参数传值 //3.值方式返回局部对象
1 //拷贝构造函数调用时机 2 3 4 #include <iostream> 5 using namespace std; 6 7 //1.使用一个已经创建完毕的对象来初始化一个新对象 ...
- c/c++不能返回局部对象和局部变量的指针或引用解释
在编写c/c++代码时,调用函数的书写让程序变得整洁易读,但是调用函数的返回值(局部变量的返回值,变量,结构体,数组等)也有注意事项.c/c++严禁返回局部变量的指针或引用. 其实函数的返回值的规则非 ...
- C++函数返回局部指针变量
遇到过好几次关于函数返回指针变量问题,有时候是可以的,有时候是不可以的,然后就混乱了.今天研究了下,结果发现原来和内存分配有关. 用下面的例子分析下吧: char * test() { char a[ ...
- 【C++对象模型】函数返回C++对象的问题
在深入C++对象模型中,对于形如 CObj obj1 = Get(obj2); 的形式,编译器会在将其改变为如下 Get(obj, CObj& obj1); 将赋值操作符左边的变量作为函数的 ...
- 类1(this指针/const成员函数/类作用域/外部成员函数/返回this对象的函数)
假设我们要设计一个包含以下操作的 Sales_data 类: 1.一个 isbn 成员函数,用于返回对象的 book_no 成员变量 2.一个 combine 成员函数,用于将一个 Sales_dat ...
- 【Vue】定义组件 data 必须是一个函数返回的对象
Vue 实例的数据对象.Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化.对象必须是纯粹的对象 (含有零个或多个的 key/value ...
- Python3基础 Python的函数都有返回值 无指定返回值的函数 返回NONE对象
镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...
- 函数返回new对象
#include <iostream> using namespace std; // foo()函数本质上没什么问题,但建议你不要这样写代码 string &foo() { st ...
随机推荐
- mysql插入测试数据
使用php生成sql文件,然后再倒入mysql. 1.编写php代码 <?php set_time_limit(0); ini_set("memory_limit", &qu ...
- windows下python3.6 32bit 安装django
在Windows下python3.6安装Django1.11.3 1.首先下载地址:https://pypi.python.org/pypi/Django/1.11.3 pip install dja ...
- 推荐一款基于bootstrap的漂亮的前端模板—inspinia_admin
首先给出Demo网址:http://cn.inspinia.cn inspinia admin 最新版 bootstrap 完全响应式后台管理模板,采用扁平化设计.使用Bootstrap 3+ Fra ...
- mvc约定
mvc是围绕一些基于约定的默认项,这写默认项在必要时可以覆盖,这个概念通常成为"约定优于配置". 比如没必要在web.config中设置views,models等文件夹的名称,已经 ...
- php intval()和floatval()
intval -- 获取变量的整数值 floatval -- 获取变量的浮点值 <?php $a = 26; $b = 4; $a/=$b; echo intval($a);//6 echo f ...
- Protocol Buffer序列化对比Java序列化.
初识 Protocol Buff是谷歌推出的一种序列化协议. 而Java序列化协议也是一种协议. 两者的目的是, 将对象序列化成字节数组, 或者说是二进制数据, 那么他们之间有什么差异呢. proto ...
- calc() ---一个会计算的css属性
最近这个月一直在赶项目开发,遇到的问题和学到的前端知识没有更新到博客园,现在闲了下来,就整理一下前端知识. 在项目开发中,在样式这方面花费的时间较多,因为针对于数字的变化特别多,本人不爱记数字,在看设 ...
- 浏览器抓包(post)
谁能想到我写的第一个wp竟然是个web题??? 以Geek2017_Buy me a Tesla 为例 来看看题目 emmmm有理想还是很好的,火狐打开(事实证明FF对web题是最友好的) 能点的地方 ...
- java学习笔记IO之File类
File类总结 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Times } p.p2 { margin: 0.0px 0.0px 0.0p ...
- tensorflow 自定义损失函数示例
这个自定义损失函数的背景:(一般回归用的损失函数是MSE, 但要看实际遇到的情况而有所改变) 我们现在想要做一个回归,来预估某个商品的销量,现在我们知道,一件商品的成本是1元,售价是10元. 如果我们 ...