在C++中,可以使用以下代码计算vector<int>中所有元素的和:

vector<int> v = {1, 3, 7, 9};
sums = 0; for (int i = 0; i < v.size(); i++) {
sums += v[i];
}

  这是一段很普通的代码,问题在于:在这段代码中,v.size()会在循环开始前仅计算一次?还是会在每次循环中都计算一次?以前我总觉得,这段代码没有改变v的大小,既然我都能看出来,编译器一定也知道,从而会在循环开始前就把v.size()计算出来,之后的每次循环中,直接使用计算出的值,以减少函数调用的开销。但事实果真如此吗?

  使用g++编译上面这段代码,得到的结果如下:

  可以看到,每次循环时会调用v.size()获取vector大小。使用clang编译后的代码也大致类似,也会在每次循环调用v.size()获取大小:

  但这是否就意味着我想错了呢?并非如此。事实上,大家可能忽略了编译器有不同的优化等级,默认情况下,编译器会忠实地按照源代码的描述编译代码。当我们开启更高级别的优化时,就会得到“源代码来了也认不出”的编译结果:

  这里使用clang编译器编译并启用了-O2级别的优化。是的,你没有看错,启用此级别的优化后,循环直接被优化掉了。因为这里的vector<int>中只有4个固定的元素,因此编译器直接使用xmm(一种宽度为128位的加宽寄存器)替代了求和的循环。

  

  为了搞清楚启用优化后.size()是否会被提前计算出,我们创建一个更大的vector:

vector<int> v {};
for (int i = 0; i < 500; i++) {
v.push_back(i);
} int sums = 0;
for (std::size_t i = 0; i < v.size(); i++) {
sums += v[i];
}

  启用-O2级别的优化后,与求和循环相关的代码如下图所示:

  可以看到:v.size()会在循环开始前计算出,并保存到寄存器rdx中。在每次循环中,vector的索引(保存在r8中)直接与寄存器rdx中的值进行比较。

由于优化后使用的是128位的xmm寄存器,因此每次循环时r8会加上8.(一个xmm可以保存4个int,以上循环中每轮读取2次数据到xmm2中,因此共4x2=8个int数据)

  因此,在上述代码中,编译器是否会提前计算出.size()取决于启用优化的等级。

如果在循环中不改变vector的大小,C++编译器是否会将.size()优化为常数?的更多相关文章

  1. bash的循环中无法保存变量

    在bash中,如果循环在一个子shell中运行,那么在循环中对循环外面的变量的更改将在循环退出后不可见.像下面的例子: #!/bin/sh python run.py | while read lin ...

  2. Java循环中标签的作用(转)

    转自:http://lihengzkj.iteye.com/blog/1090034 以前不知道在循环中可以使用标签.最近遇到后,举得还是有其独特的用处的.我这么说的意思是说标签在循环中可以改变循环执 ...

  3. C#不允许在foreach循环中改变数组或集合中元素的值(注:成员的值不受影响)

    C#不允许在foreach循环中改变数组或集合中元素的值(注:成员的值不受影响),如以下代码将无法通过编译. foreach (int x in myArray) { x++; //错误代码,因为改变 ...

  4. ruby for in 循环中改变i的值无效

    ruby for in 循环中改变i的值无效 for j in 1..5 puts "#{j}hehe" j = j + 2 #break end 在循环中,使用j = j + 2 ...

  5. C++中数组和vector

    本文基于邓俊辉编著<数据结构(C++语言版)(第3版)>.<C++ Primer(第5版)>以及网上的相关博文而写,博主水平有限,若有不妥处,欢迎指出. 一.数组 C++中数组 ...

  6. 混编用到 C++中数组和vector 复习下大学课本

    本文基于邓俊辉编著<数据结构(C++语言版)(第3版)>.<C++ Primer(第5版)>以及网上的相关博文而写,博主水平有限,若有不妥处,欢迎指出. 一.数组 C++中数组 ...

  7. Java 循环中标签的作用

    continue和break可以改变循环的执行流程,但在多重循环中,这两条语句无法直接从内层循环跳转到外层循环.在C语言中,可以通过goto语句实现多重循环的跳转,但在非循环结构中使用goto语句会使 ...

  8. C#在foreach循环中修改字典等集合出错的处理

    C#在foreach循环中修改字典等集合出错:System.InvalidOperationException: Collection was modified; enumeration operat ...

  9. let 和 const 在for 循环中的使用

    在ES6 的规范中,多了两个声明变量的关键字: let 和const.初次学习的时候,只记住了 let 声明的变量只在for 的循环体中有效,循环结束后 变量就消失了, 同时const 也可以在for ...

  10. JS的splice()方法在for循环中使用可能会遇到的坑

    在写JS代码时,我们常常使用 splice 函数来删除数组中的元素,因为 splice 函数会直接对数组进行修改,从而不需再自己写一个算法来移动数组中的其他元素填补到被删除的位置.splice 功能十 ...

随机推荐

  1. 技术文档丨 OpenSCA技术原理之npm依赖解析

    本文主要介绍基于npm包管理器的组件成分解析原理. npm介绍 npm(全称Node Package Manager)是Node.js标准的软件包管理器. npm的依赖管理文件是package.jso ...

  2. Bash 常用命令总结

    基础常用命令 某个命令 --h,对这个命令进行解释 某个命令 --help,解释这个命令(更详细) man某个命令,文档式解释这个命令(更更详细)(执行该命令后,还可以按/+关键字进行查询结果的搜索) ...

  3. .NET静态代码织入——肉夹馍(Rougamo)发布2.2

    肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应 ...

  4. C#对象二进制序列化优化:位域技术实现极限压缩

    目录 1. 引言 2. 优化过程 2.1. 进程对象定义与初步分析 2.2. 排除Json序列化 2.3. 使用BinaryWriter进行二进制序列化 2.4. 数据类型调整 2.5. 再次数据类型 ...

  5. 配置Maven项目的pom.xml时遇到input contained no data问题

    1.问题 2.解决 我发现每次当我修改一些部分之后,就会遇到这个问题 我遵循了他的建议重新排列标记属性,然后重新加载就解决了报错 个人怀疑是修改后并没有识别之类的? 最终原因:是我在Maven项目中的 ...

  6. DEV-C++调试报错

    1.报错信息如下: 2.原因 SIGSEGV是是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号. 意思是程序接受一个无效的指针地址,Segmentation fault即是提示我们去 ...

  7. SpringBoot利用自定义注解实现多数据源

    自定义多数据源 SpringBoot利用自定义注解实现多数据源,前置知识:注解.Aop.SpringBoot整合Mybaits 1.搭建工程 创建一个SpringBoot工程,并引入依赖 <de ...

  8. [转帖]离线部署单机kubenetes-1.28.4

    系统版本: openEuler 22.03 (LTS-SP2) docker版本:24.0.7 kubenetes版本: 1.28.4 虚机IP: 192.168.177.138 基于 https:/ ...

  9. Oracle 专用模式与共享模式的学习与思考

    Oracle 专用模式与共享模式的学习与思考 说明 Oracle数据库中的专用模式和共享模式是两种不同的数据库运行模式,它们在应用场景和权限管理上有所不同. 专用模式(Dedicated Mode): ...

  10. [转帖]Nginx 反向代理解决跨域问题

    https://juejin.cn/post/6995374680114741279 编写代码两分钟,解决跨域两小时,我吐了. 如果对跨域还不了解的朋友,可以看这篇:[基础]HTTP.TCP/IP 协 ...