关于C++函数返回局部对象的详细分析
来源:互联网 发布:上海德颐网络正规吗 编辑:程序博客网 时间:2024/05/17 21:43
以前一直挺好奇的,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的地址存放到esilea 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;}
1 0
- 关于C++函数返回局部对象的详细分析
- 关于函数不能返回局部对象的引用或者局部对象的指针
- c++返回函数局部对象的引用
- c++返回函数局部对象的引用
- 关于函数返回局部变量的问题
- C语言中关于函数的局部变量和地址返回
- 关于函数返回局部变量
- C函数返回局部变量
- C 函数返回局部变量
- C函数返回局部变量
- c 函数 返回局部变量
- C函数返回局部变量
- 关于函数返回的局部数组的问题
- 关于C++中的函数返回局部string的问题
- 关于局部函数返回值的一个有趣问题
- 关于 C++ 函数返回局部变量的警告
- 关于函数返回局部变量的内存细节知识
- c语言中关于局部变量的返回值
- Poj1847(floyd)
- spring MVC 传入 json 数组
- shell学习笔记(二):shell 语法
- MySQL元数据库——information_schema
- Response And Request
- 关于C++函数返回局部对象的详细分析
- 应用最广的模式--单例模式
- stm32 FPU 注意事项
- HTML5中标签之间尽量不要加enter或者空格
- 从网站首页看BAT的发展历史(待续....)
- Android消息机制源码分析
- 正向代理和反向代理
- 【机器学习trick】Batch-Normalization的理解和研究
- 2014 蓝桥杯C/C++B组省赛 六角填数