关于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 ...
随机推荐
- less简述
什么是less? 一种 动态 样式 语言. LESS 将 CSS 赋予了动态语言的特性,如 变量, 继承, 运算, 函数. LESS 既可以在 客户端 上运行 (支持IE 6+, Webkit, ...
- RibbonForm使用技巧
Ribbon右侧显示Logo 方法 重写RibbonControl的Paint事件 效果 代码 private void _ribbonControl_Paint(object sender, Pai ...
- Class 与 Style 绑定
将 v-bind 用于 class 和 style 时,Vue.js 做了专门的增强.表达式结果的类型除了字符串之外,还可以是对象或数组. 绑定 HTML Class 对象语法 <div cla ...
- python网络编程之单线程之间的并发
单线程之间的并发就是利用一个线程实现并发的效果,也就是利用了cup遇到阻塞的那段时间去做别的事情,从而提高了cup的利用率,使之在单个线程中就实现了并发的效果. 下面就是一个简单的服务端单个线程实现并 ...
- VMware中克隆虚拟机出现eth0改变为eth1情况
解决如下: 查看复制虚拟机网卡信息如下: root@jcfx-2 ~]# ifconfig eth1 Link encap:Ethernet HWaddr 00:0C:29:CC:32:63 inet ...
- 前端测试框架Jest系列教程 -- Global Functions(全局函数)
写在前面: Jest中定义了很多全局性的Function供我们使用,我们不必再去引用别的包来去实现类似的功能,下面将列举Jest中实现的全局函数. Jest Global Functions afte ...
- 高效管理http连接
1.Http连接基础 Http协议承载了互联网上的主要流量,然而说到传输,还要回归到最基本的网络分层模型TCP/IP.TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交互网络分层协议集.客户 ...
- 好好写代码吧,没事别瞎B去创业!
知乎上看到这个问题 正好最近想写篇关于此的文章,于是就回答了一波. 也贴到这里来,回答如下 : 本问题简直为我量身定制,做为一个正在创业中的苦逼少年,说说我是如何从鼓吹怂恿身边人创业转换成反对创业的. ...
- Navicat for Mysql 暴力破解教程
关于破解Navicat for MySQL的教程有很多 ,但是比较繁琐, 这里推荐一种比较简单的办法~ 网盘地址:链接: https://pan.baidu.com/s/1kVHyShL 密码: ws ...
- 【前端】Util.js-ES6实现的常用100多个javaScript简短函数封装合集(持续更新中)
Util.js (持续更新中...) 项目地址: https://github.com/dragonir/Util.js 项目描述 Util.js 是对常用函数的封装,方便在实际项目中使用,主要内容包 ...