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 lt = { 1,2,3,4,5,6 };
list::iterator lit = lt.begin();
while (lit != lt.end())
{
cout << *lit << "->";
++lit;
}
cout << endl;
我们可以看到这段代码还是相对比较麻烦的,C++11以后引入了范围for的语法糖就大大简化了上面的代码。
list lt = { 1,2,3,4,5 };
for (auto& k : lt)
{
cout << k << "->";
}
cout << endl;
for (auto 迭代的变量 : 迭代的范围)
{
循环体
}
for (auto& 迭代的变量 : 迭代的范围)
{
循环体
}
下面这两段虽然输出结果一样,但是有着本质上的区别。
list lt = { 1,2,3,4,5 };
for (auto k : lt)
{
k += 2;
cout << k << "->";
}
cout << endl;
完成上面的操作后,lt中的值没有发生变化
list lt = { 1,2,3,4,5 };
for (auto& k : lt)
{
k += 2;
cout << k << "->";
}
cout << endl;
完成上面的操作后,lt中每个值都+=2
从语法上理解:
如果直接采用第一种方法,就是将lt中每个值依次拷贝给k,然后完成循环体中的操作。
如果采用第二种方法,k依次是lt中每个值的别名,然后进行循环体中的操作。
但是实际上,两种方案在内存上没有区别,因为两者底层都是采用迭代器,只是两种语法对迭代器变量的操作不同而已。
范围for有两种情况:
遍历容器的时候
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 > >::__autoclassinit2 (07FF6A66413E8h)
00007FF6A6645826 lea rdx,[rbp+58h]
00007FF6A664582A mov rcx,qword ptr [rbp+38h]
00007FF6A664582E call std::list >::_Unchecked_begin (07FF6A66414F1h)
00007FF6A6645833 mov edx,8
00007FF6A6645838 lea rcx,[rbp+78h]
00007FF6A664583C call std::_List_unchecked_iterator > >::__autoclassinit2 (07FF6A66413E8h)
00007FF6A6645841 lea rdx,[rbp+78h]
00007FF6A6645845 mov rcx,qword ptr [rbp+38h]
00007FF6A6645849 call std::list >::_Unchecked_end (07FF6A66413A2h)
00007FF6A664584E jmp __$EncStackInitStart+0F9h (07FF6A6645859h)
00007FF6A6645850 lea rcx,[rbp+58h]
00007FF6A6645854 call std::_List_unchecked_iterator > >::operator++ (07FF6A66411FEh)
00007FF6A6645859 lea rdx,[rbp+78h]
00007FF6A664585D lea rcx,[rbp+58h]
00007FF6A6645861 call std::_List_unchecked_const_iterator >,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 > >::operator* (07FF6A664157Dh)
00007FF6A6645876 mov qword ptr [rbp+98h],rax
我们可以看到,在遍历容器时,编译器会将范围for转换成迭代器去遍历容器。
遍历数组的时候
// 范围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循环语句来遍历数组。