您的位置:首页 > 文旅 > 旅游 > 【C++语法糖】范围for

【C++语法糖】范围for

2025/7/13 0:39:36 来源:https://blog.csdn.net/Alenenen/article/details/141003210  浏览:    关键词:【C++语法糖】范围for

文章目录

  • 【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循环语句来遍历数组。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com