【C++语法糖】范围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. 遍历数组的时候

    // 范围for
    	for (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)
    
    // 普通for
    	for (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循环语句来遍历数组。

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值