返回值的理解

news/2024/11/17 7:34:56/

前言

我们写的函数是怎么返回的,该如何返回一个临时变量,临时变量不是出栈就销毁了吗,为什么可以传递给调用方?返回对象的大小对使用的方式有影响吗?本文将带你探究这些问题,阅读本文需要对函数栈帧有一定的理解,并了解基本的汇编指令。

文章汇编代码:采用 GCC 8.3.1,对 C 代码使用 -Og 优化级别生成的可执行程序,再用 objdump -d 反汇编的结果。

寄存器保存

如果返回的对象比较小,寄存器可以放得下,返回值将被放到 %rax 中。%rax 只能存放整数数据和指针,浮点数使用另外一组单独的寄存器。

返回寄存器

来看一段简单的代码:

// 一段简单让两数相乘的代码,写成这种形式,主要是尽量减少编译器优化
long mult2(long a, long b) {long t = a * b;return t;
}void mult_store(long x, long y, long* dest) {long t = mult2(x, y);*dest = t;
}
00000000004004d2 <mult2>:# a in %rdi,b in %rsi4004d2:   mov    %rdi,%rax	# 把 a 移动到 %rax4004d5:   imul   %rsi,%rax	# 此时 %rax 保存的是参数 a,再将 %rsi 保存的参数 b 乘到 %rax4004d9:   retq				# 这时 %rax 保存的是 a * b00000000004004da <mult_store>:# x in %rdi,y in %rsi,dest in %rdx4004da:   push   %rbx4004db:   mov    %rdx,%rbx		# 保存 dest,采取调用方保存4004de:   callq  4004d2 <mult2>	# 调用 mult24004e3:   mov    %rax,(%rbx)		# 将 %rax 保存的 a * b# 移动到 %rbx 保存的 dest 指针指向的内存处# t 并没有实际的作用,编译器将其创建优化掉了4004e6:   pop    %rbx4004e7:   retq					

编译器优化

上面所说的都是比较小的内置类型,那假如返回的对象很大,%rax 放不下该怎么办?

下面介绍一种 C++ 对返回值的优化方式,实际编译器并不一定会使用该方式。

class qgw {// 有默认构造函数、拷贝构造函数、析构函数等long a1;long a2;long a3;
} qgw;qgw fun() {qgw q;// 处理 qreturn q;
}

如果返回值很小,我们可以使用寄存器取到返回值,现在又该怎么办呢?Stroustrup 在 cfront 中的解决方案是一个双阶段优化:

  1. 加上一个额外参数,类型是 class object 的一个 reference
    • 这个参数在最后用 “返回值” 构建
  2. 在 return 指令之前插入一个拷贝构造调用操作,以便将想要返回的 object 的内容当做上述新增参数的初值

上述代码经转化后如下:

void fun(qgw& __result) {qgw q;// 编译器产生的默认构造函数调用操作q.qgw::qgw();// 处理 q// 编译器产生的拷贝构造调用操作__result.qgw::qgw(q);return;
}
qgw qin = fun();
// 转化为
// 不必为 qin 调用默认构造函数
qgw qin;
fun(qin);// fun() 函数返回值调用 test 函数
fun().test();
// 转化为
// 编译器生成的临时变量
qgw __temp0;
(fun(__temp0), __temp0).test();

还有一种被称为 Named Return Value(NRL)优化,被视为标准 C++ 编译器的一个义不容辞的优化操作。

qgw fun() {qgw q;// 处理 qreturn q;
}// 直接优化为
void fun(qgw& __result) {// 默认构造被调用__result.gqw::qgw();// 直接处理 __resultreturn;
}

经上述处理后,函数没有真正意义上的返回值了,也就不需要处理大对象的情况了。

对于传参请参考:传参的理解


http://www.ppmy.cn/news/19602.html

相关文章

如何好好说话第11章 攀登抽象之梯

在心里面放一把抽象之梯我们要时刻去概括。从更宏观的角度去理解我们当前所处的事情。抽上之梯的这个概念&#xff0c;在写作教材中常常出现。他指引我们写作的时候&#xff0c;不要站在梯子的中间。要么具体详实&#xff0c;要么抽象而精简短小。为什么不要站在梯子中间呢&…

JVM快速入门学习笔记(四)

15.GC &#xff1a;垃圾回收机制 垃圾回收的区域只有在堆里面&#xff08;方法区在堆里面&#xff09; 15.1 垃圾回收 GC JVM 在进行垃圾回收&#xff08;GC&#xff09;时&#xff0c;并不是堆这三个区域统一回收。大部分时候&#xff0c;回收都是新生代~   1.新生代   …

java-List

java-List1. 预备知识-泛型(Generic)1.1 泛型的引入1.2 泛型的分类1.3 泛型类的定义的简单演示1.4 泛型背后作用时期和背后的简单原理1.5 泛型类的使用1.6 泛型总结2. 预备知识-包装类(Wrapper Class)2.1 基本数据类型和包装类直接的对应关系2.2 包装类的使用&#xff0c;装箱(…

【前端面试】http面试整理

"一问一答"模型的协议 客户端通过http请求&#xff1b;服务器端根据请求返回客户想要的资源&#xff1b;客户端接收到资源&#xff1b;http是什么 HTTP是超文本传输协议&#xff0c;是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约…

STM32+python产生三角波

目录任务目标实现方法python制作数表由于项目需要&#xff0c;需要产生一个三角波&#xff0c;需要覆盖4000个点的一个数组&#xff0c;这样的数组点数太多了&#xff0c;肯定不能自己一个一个手写了。最简单的一个方法是在嵌入式程序中用C写一个函数&#xff0c;对一个数组&am…

报错整理(1.25)

报错1&#xff1a; 报错&#xff1a;gcc: error trying to exec cc1plus: execvp: No such file or directory error: command /usr/bin/nvcc failed with exit status 1解决&#xff1a;apt-get install g报错2&#xff1a; 报错&#xff1a;add-apt-repository&#xff1a;找…

背景颜色和背景图片

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <!--这行代码是告诉浏览器需要使用"utf-8"字符集打开 因为HBuilder工具是采用utf-8编码的 注意&#xff1a;并不是设置当前页面的字符集编码方式--> …

基于微信小程序的新生自助报到系统小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…