在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. vue2.x封装svg组件并使用

    https://blog.csdn.net/ChickenBro_/article/details/134027803

  2. uni-app阿里图标引用

    @font-face { font-family: "iconfont"; /* Project id 2566540 */ src: url('~@/static/fonts/i ...

  3. uni-app打包到安卓步骤

    1.打包到安卓 https://www.bilibili.com/video/BV1BJ411W7pX?p=56 2.打包到IOS https://www.jianshu.com/p/ef6e6e01 ...

  4. C# 用树形列表 动态 显示菜单

    如图 页面加载 private void Form1_Load(object sender, EventArgs e) { GetMenu(treeView1, menuStrip1);//将menu ...

  5. python之logging日志

    一.logging介绍: 使用 logging.debug(text)来打印信息,info等的使用方法与debug一致,都只有一个位置参数 默认日志界别为:会输出warning以上的信息,代码示例: ...

  6. Angular系列教程之DOM操作

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  7. 03-ASIC和FPGA中的时钟结构

    1 ASIC 中时钟的结构 ASIC电路中的时钟的结构.这是一个非常典型的MCU的时钟结构图.它的时钟结构和功能的划分.首先,我们通过外部振荡器发送了一个8MHz的时钟给PLL,经过分分频和倍频产生更 ...

  8. 【面试题精讲】什么是websocket?如何与前端通信?

    > 有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准`https://blog.zysicyj.top` [首发博客地址](https://blog.zysicyj.t ...

  9. [转帖]Linux cache参数调优

    https://zhuanlan.zhihu.com/p/136237953 缓存机制(cache)是保证Linux环境下对硬盘/flash操作效率的有效方式.cache建立在内存中,它缓存了硬盘/f ...

  10. [转帖]Linux进程的Uninterruptible sleep(D)状态

    https://cloud.tencent.com/developer/article/1581022 Linux系统进程状态 PROCESS STATE CODES Here are the dif ...