当前位置: 首页 > news >正文

返回值的理解

前言

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

文章汇编代码:采用 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 %rsi
  4004d2:   mov    %rdi,%rax	# 把 a 移动到 %rax
  4004d5:   imul   %rsi,%rax	# 此时 %rax 保存的是参数 a,再将 %rsi 保存的参数 b 乘到 %rax
  4004d9:   retq				# 这时 %rax 保存的是 a * b

00000000004004da <mult_store>:
  # x in %rdi,y in %rsi,dest in %rdx
  4004da:   push   %rbx
  4004db:   mov    %rdx,%rbx		# 保存 dest,采取调用方保存
  4004de:   callq  4004d2 <mult2>	# 调用 mult2
  4004e3:   mov    %rax,(%rbx)		# 将 %rax 保存的 a * b
  									# 移动到 %rbx 保存的 dest 指针指向的内存处
  									# t 并没有实际的作用,编译器将其创建优化掉了
  4004e6:   pop    %rbx
  4004e7:   retq					

编译器优化

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

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

class qgw {
    // 有默认构造函数、拷贝构造函数、析构函数等
    long a1;
    long a2;
    long a3;
} qgw;

qgw fun() {
	qgw q;
    // 处理 q
    return 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;
    // 处理 q
    return q;
}

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

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

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

相关文章:

  • 前同事居然因为 Pycharm 的这个功能,即使离职三年也依然经常被请去喝茶~
  • IPV4地址详解
  • ubuntu 22.04学习笔记
  • 【蓝桥杯-筑基篇】基础数学思维与技巧(1)
  • 图论(入门版)
  • 使用bindgen将C语言头文件转换为Rust接口代码
  • 第九层(2):STL之string类
  • Allegro如何自动做差分对内等长操作指导
  • 搜索引擎位置跟踪应用SerpBear
  • 浅析一条SQL在mysql中是如何执行的
  • 前端艺术之毛玻璃-倾斜-日历
  • Python-Flask-2023.1.24-Review
  • SpringBoot 统一功能处理
  • 3. Python列表简介
  • sidebar(侧边栏原理vue admin)
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • AWS实战 - 利用IAM对S3做访问控制
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • CSS实用技巧
  • JavaScript-Array类型
  • JavaScript实现分页效果
  • leetcode98. Validate Binary Search Tree
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 构建工具 - 收藏集 - 掘金
  • 算法-插入排序
  • 学习Vue.js的五个小例子
  • 一加3T解锁OEM、刷入TWRP、第三方ROM以及ROOT
  • 移动端唤起键盘时取消position:fixed定位
  • 组复制官方翻译九、Group Replication Technical Details
  • (pytorch进阶之路)扩散概率模型
  • (八)c52学习之旅-中断实验
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (论文阅读30/100)Convolutional Pose Machines
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (译)2019年前端性能优化清单 — 下篇
  • (转)Sublime Text3配置Lua运行环境
  • .Net 垃圾回收机制原理(二)
  • .NET导入Excel数据
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • :O)修改linux硬件时间
  • @font-face 用字体画图标
  • @Transient注解
  • [Android]Android开发入门之HelloWorld
  • [android学习笔记]学习jni编程
  • [C# WPF] 如何给控件添加边框(Border)?
  • [Django 0-1] Core.Handlers 模块
  • [Editor]Unity Editor类常用方法
  • [English]英语积累本
  • [JS] 常用正则表达式集(一)
  • [JS]变量
  • [Linux]history 显示命令的运行时间
  • [MFC] VS2013版本MFC工程移植到VC6.0上