【C++语法糖】范围for

ops/2024/12/22 15:00:25/

文章目录

  • 【C++语法糖】:范围for
    • 范围for
      • 1. 范围for的模板
      • 2. &的使用
      • 3. 范围for的底层

【C++语法糖】:范围for

C++11标准后引入了范围for,这个范围for是一种语法糖,来简化代码书写。

下面是简单遍历数组的一段简单代码

int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
{cout << arr[i] << " ";
}
cout << endl;

但是如果将数组换成不连续存储的链表list,就不能使用上面的方式,得用迭代器。

list<int> lt = { 1,2,3,4,5,6 };
list<int>::iterator lit = lt.begin();
while (lit != lt.end())
{cout << *lit << "->";++lit;
}
cout << endl;

我们可以看到这段代码还是相对比较麻烦的,C++11以后引入了范围for的语法糖就大大简化了上面的代码。

list<int> lt = { 1,2,3,4,5 };
for (auto& k : lt)
{cout << k << "->";
}
cout << endl;

范围for

  • 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号 " : " 分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
  • 范围for可以作用到数组和容器对象上进行遍历
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到

1. 范围for的模板

for (auto 迭代的变量 : 迭代的范围)
{循环体
}
for (auto& 迭代的变量 : 迭代的范围)
{循环体
}

2. &的使用

下面这两段虽然输出结果一样,但是有着本质上的区别。

list<int> lt = { 1,2,3,4,5 };
for (auto k : lt)
{k += 2;cout << k << "->";
}
cout << endl;

完成上面的操作后,lt中的值没有发生变化

list<int> lt = { 1,2,3,4,5 };
for (auto& k : lt)
{k += 2;cout << k << "->";
}
cout << endl;

完成上面的操作后,lt中每个值都+=2

从语法上理解:

如果直接采用第一种方法,就是将lt中每个值依次拷贝给k,然后完成循环体中的操作。

如果采用第二种方法,k依次是lt中每个值的别名,然后进行循环体中的操作。

但是实际上,两种方案在内存上没有区别,因为两者底层都是采用迭代器,只是两种语法对迭代器变量的操作不同而已。

3. 范围for的底层

范围for有两种情况:

  1. 遍历容器的时候

    	for (auto& k : lt)
    00007FF6A6645810  lea         rax,[lt]  
    00007FF6A6645814  mov         qword ptr [rbp+38h],rax  
    00007FF6A6645818  mov         edx,8  
    00007FF6A664581D  lea         rcx,[rbp+58h]  
    00007FF6A6645821  call        std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<int> > >::__autoclassinit2 (07FF6A66413E8h)  
    00007FF6A6645826  lea         rdx,[rbp+58h]  
    00007FF6A664582A  mov         rcx,qword ptr [rbp+38h]  
    00007FF6A664582E  call        std::list<int,std::allocator<int> >::_Unchecked_begin (07FF6A66414F1h)  
    00007FF6A6645833  mov         edx,8  
    00007FF6A6645838  lea         rcx,[rbp+78h]  
    00007FF6A664583C  call        std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<int> > >::__autoclassinit2 (07FF6A66413E8h)  
    00007FF6A6645841  lea         rdx,[rbp+78h]  
    00007FF6A6645845  mov         rcx,qword ptr [rbp+38h]  
    00007FF6A6645849  call        std::list<int,std::allocator<int> >::_Unchecked_end (07FF6A66413A2h)  
    00007FF6A664584E  jmp         __$EncStackInitStart+0F9h (07FF6A6645859h)  
    00007FF6A6645850  lea         rcx,[rbp+58h]  
    00007FF6A6645854  call        std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<int> > >::operator++ (07FF6A66411FEh)  
    00007FF6A6645859  lea         rdx,[rbp+78h]  
    00007FF6A664585D  lea         rcx,[rbp+58h]  
    00007FF6A6645861  call        std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<int> >,std::_Iterator_base0>::operator!= (07FF6A66412F8h)  
    00007FF6A6645866  movzx       eax,al  
    00007FF6A6645869  test        eax,eax  
    00007FF6A664586B  je          __$EncStackInitStart+144h (07FF6A66458A4h)  
    00007FF6A664586D  lea         rcx,[rbp+58h]  
    00007FF6A6645871  call        std::_List_unchecked_iterator<std::_List_val<std::_List_simple_types<int> > >::operator* (07FF6A664157Dh)  
    00007FF6A6645876  mov         qword ptr [rbp+98h],rax  
    

    我们可以看到,在遍历容器时,编译器会将范围for转换成迭代器去遍历容器。

  2. 遍历数组的时候

    // 范围forfor (auto k : arr)
    00007FF6D28C1973  lea         rax,[arr]  
    00007FF6D28C1977  mov         qword ptr [rbp+48h],rax  
    00007FF6D28C197B  mov         rax,qword ptr [rbp+48h]  
    00007FF6D28C197F  mov         qword ptr [rbp+68h],rax  
    00007FF6D28C1983  mov         rax,qword ptr [rbp+48h]  
    00007FF6D28C1987  add         rax,28h  
    00007FF6D28C198B  mov         qword ptr [rbp+88h],rax  
    00007FF6D28C1992  jmp         __$EncStackInitStart+0A1h (07FF6D28C19A0h)  
    00007FF6D28C1994  mov         rax,qword ptr [rbp+68h]  
    00007FF6D28C1998  add         rax,4  
    00007FF6D28C199C  mov         qword ptr [rbp+68h],rax  
    00007FF6D28C19A0  mov         rax,qword ptr [rbp+88h]  
    00007FF6D28C19A7  cmp         qword ptr [rbp+68h],rax  
    00007FF6D28C19AB  je          __$EncStackInitStart+0BCh (07FF6D28C19BBh)  
    00007FF6D28C19AD  mov         rax,qword ptr [rbp+68h]  
    00007FF6D28C19B1  mov         eax,dword ptr [rax]  
    00007FF6D28C19B3  mov         dword ptr [rbp+0A4h],eax  {}
    00007FF6D28C19B9  jmp         __$EncStackInitStart+95h (07FF6D28C1994h)
    
    // 普通forfor (int i = 0; i < sizeof(arr) / sizeof(int); ++i)
    00007FF6D28C19BB  mov         dword ptr [rbp+0C4h],0  
    00007FF6D28C19C5  jmp         __$EncStackInitStart+0D6h (07FF6D28C19D5h)  
    00007FF6D28C19C7  mov         eax,dword ptr [rbp+0C4h]  
    00007FF6D28C19CD  inc         eax  
    00007FF6D28C19CF  mov         dword ptr [rbp+0C4h],eax  
    00007FF6D28C19D5  movsxd      rax,dword ptr [rbp+0C4h]  
    00007FF6D28C19DC  cmp         rax,0Ah  
    00007FF6D28C19E0  jae         __$EncStackInitStart+0E5h (07FF6D28C19E4h)  {}
    00007FF6D28C19E2  jmp         __$EncStackInitStart+0C8h (07FF6D28C19C7h)
    

    由此我们可以看到,在遍历数组时,编译器会将范围for转换成普通的for循环语句来遍历数组。


http://www.ppmy.cn/ops/91984.html

相关文章

【C++】C++11的新特性 — 线程库 ,原子操作 , 条件变量

勇敢就是接受发生在你身上的事&#xff0c;并把它尽力做到最好。 -- 约翰・欧文 -- C11的新特性 1 线程1.1 线程概念1.2 C中的线程1.3 线程并行1.4 锁 2 原子操作3 条件变量Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;&#xff01;&#xff01;下…

Linux查看系统线程数

Linux查看系统线程数 查看线程数查看进程内的线程统计线程数 查看线程数 想要查看Linux操作系统允许的最大线程数&#xff0c;可以通过命令 ulimit -a返回配置项的详细说明&#xff1a; # core文件的最大值为100blocks core file size (blocks, -c) 0# 进程的数…

[激光原理与应用-122]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 23 - BSB环形光斑激光焊接的特殊检测手段

目录 前言&#xff1a; 一、双光束&#xff08;环形光斑&#xff09;激光焊接的方案 1.1 概述 1.2 应用案例 1.3激光器技术参数&#xff1a; 1.4 应用数据 二、双光束&#xff08;环形光斑&#xff09; 2.1 优点&#xff1a;可以解决激光焊接如下常见问题 1、焊接飞溅…

AWS DMS遇到 Error : no handler found for uri

问题描述&#xff1a; 当我按照文档[1]配置AWS DMS 目标端为OpenSearch, 并进行数据迁移的时候&#xff0c;我遇到了如下报错&#xff1a; 00015696: 2024-07-31T03:26:57 [TARGET_LOAD ]E: Elasticsearch:FAILED SourceTable:test TargetIndex:test Operation:INSERT_E…

学习笔记第二十二天

1. time 函数 time_t time(time_t *t); 功能&#xff1a;获取当前时间&#xff08;自1970年1月1日00:00:00 UTC以来的秒数&#xff09;。 参数&#xff1a;t 是一个指向 time_t 类型的指针&#xff0c;用于存放返回的时间值。如果传递 NULL&#xff0c;函数将不 会使用此参数…

MongoDB学习记录

1、初识Mongo 概述&#xff1a;与关系型数据库不同&#xff0c;MongoDB 的数据以类似于 JSON 格式的二进制文档存储&#xff0c;通常称这种格式为Bson&#xff0c;Bson不仅支持JSON中已有的数据类型&#xff0c;还增加了一些额外的数据类型&#xff0c;例如日期和二进制数据&a…

python 中的 write() 和 writelines()

python 中的 write() 和 writelines() 文章目录 python 中的 write() 和 writelines()1. write() 方法2. writelines() 方法注意事项:综合示例 在Python中&#xff0c;write() 和 writelines() 是两种常用的方法&#xff0c;用于向文件中写入数据。下面是这两种方法的详细解释和…

React 条件判断

在 React 中&#xff0c;可以通过 JavaScript 的条件语句来动态渲染组件或元素。 以下是几种常用的在 React 中处理条件渲染的方法&#xff1a; 1. 使用 if 语句 在 render 方法或函数组件的返回值中使用 if 语句来决定渲染内容。 实例 import React from react; import R…