使用 'auto' 进行迭代时,'&' 符号有什么作用
What does the '&' sign do when using 'auto' for iteration
最近我在 C++ 中使用 auto 时遇到了非常非常奇怪的问题,只是......看看下面的代码片段:
我的主要功能:
#include <list>
#include <iostream>
#include <stdio.h>
int main(){
int a = 10, b = 20, c = 30;
list<int> what;
what.push_back(a);
what.push_back(b);
what.push_back(c);
read(what);
return 0;
}
这里的函数是:
void read(const list<int>& con){
for (auto it : con){
printf("%p\n", &it);
cout << it << endl;
}
return ;
}
这是输出:
0x7fffefff66a4
10
0x7fffefff66a4
20
0x7fffefff66a4
30
这到底是什么? 相同地址但内容不同 !?
更奇怪的是,如果我通过添加 '&'
来修改 for 循环
即:
for (auto& it : con){
所有输出立即有意义,地址将通过迭代更改
所以我的问题是,
为什么'&'符号在这种情况下会发生变化?
让我们先看看 :
循环语法的扩展版本。
for( auto it: container) {
...
}
在概念上与
相同
for( auto _it = container.begin(); _it != container.end(); it++) {
auto it = *_it;
...
}
而参考表格:
for( auto& it: container)
与
相同
for( auto _it = container.begin(); _it != container.end(); it++) {
auto &it = *_it;
...
}
所以在第一种情况下 it
是容器中项目的 copy,在第二种情况下它是一个(左值)它的引用,因此如果你在第二个循环中修改it
,它会影响容器
中的项目
地址问题也可以这样解释:在copy例子中,局部变量在每次循环迭代中总是有相同的地址(因为它们的生命周期不重叠,编译器没有理由不在堆栈中使用相同的地址),认为如果你分解一个函数中的代码,你可能会观察到它在不同的函数调用中发生变化(因为堆栈大小可能不同),在 reference 示例地址每次都不同,因为获取引用的地址将产生被引用对象的地址(在这种情况下,容器中的项目)
请注意,在您的案例中,auto
代表 int
。所以这是一条红鲱鱼。考虑
for (int i = 0; i < 10; ++i){
int j = i;
cout << (void*)&j << '\n';
}
由于 j
具有自动存储持续时间,它很可能每次都使用相同的地址创建 - 但指向不同的值 - ,j
被压入然后从堆栈弹出每次迭代(让我们搁置编译器优化)。这就是您使用 for (auto it : con){
的情况。 it
具有 自动存储期限。
写的时候
for (auto& it : con){
it
是容器 con
中对 int
的 引用 ,因此其地址在每次迭代中都会不同。
for (auto it : con){
Same address with different content !?
这对于具有自动存储持续时间的变量来说是非常典型的。这与C++中的auto
†无关。如果您使用 int
:
,您将得到相同的结果
for (int it : con){
it
(以及循环中的每个自动变量)的生命周期只是一次迭代。由于上一次迭代it
的生命周期结束,下一次迭代可以重新使用相同的内存,这就是地址相同的原因。
Why does the '&' sign make a change under this circumstance?
因为 T&
声明了一个 引用 来键入 T
。引用变量不同于非引用(对象变量)。不是像对象那样持有一个值,而是 "refers" 对另一个对象的引用。
在引用上使用addressof运算符时,结果将是引用对象的地址;不是引用的地址(它甚至可能没有地址,因为它不是对象)。这就是后一种情况下地址发生变化的原因。在这种情况下,引用将引用存储在 what
节点中的 int
对象(因为 con
本身是一个引用,并引用传递的对象)。
† 我在 C++ 中提到,因为在 C 中 auto
实际上是一个存储 class 修饰符,表示自动存储 class。它在标准 C++ 中从来没有这种含义,它在 C 中的使用也已过时。是B语言残留的关键字。
在 C++ 中,auto
声明一个将从上下文中推导出来的类型。
最近我在 C++ 中使用 auto 时遇到了非常非常奇怪的问题,只是......看看下面的代码片段:
我的主要功能:
#include <list>
#include <iostream>
#include <stdio.h>
int main(){
int a = 10, b = 20, c = 30;
list<int> what;
what.push_back(a);
what.push_back(b);
what.push_back(c);
read(what);
return 0;
}
这里的函数是:
void read(const list<int>& con){
for (auto it : con){
printf("%p\n", &it);
cout << it << endl;
}
return ;
}
这是输出:
0x7fffefff66a4
10
0x7fffefff66a4
20
0x7fffefff66a4
30
这到底是什么? 相同地址但内容不同 !?
更奇怪的是,如果我通过添加 '&'
来修改 for 循环
即:
for (auto& it : con){
所有输出立即有意义,地址将通过迭代更改
所以我的问题是,
为什么'&'符号在这种情况下会发生变化?
让我们先看看 :
循环语法的扩展版本。
for( auto it: container) {
...
}
在概念上与
相同for( auto _it = container.begin(); _it != container.end(); it++) {
auto it = *_it;
...
}
而参考表格:
for( auto& it: container)
与
相同for( auto _it = container.begin(); _it != container.end(); it++) {
auto &it = *_it;
...
}
所以在第一种情况下 it
是容器中项目的 copy,在第二种情况下它是一个(左值)它的引用,因此如果你在第二个循环中修改it
,它会影响容器
地址问题也可以这样解释:在copy例子中,局部变量在每次循环迭代中总是有相同的地址(因为它们的生命周期不重叠,编译器没有理由不在堆栈中使用相同的地址),认为如果你分解一个函数中的代码,你可能会观察到它在不同的函数调用中发生变化(因为堆栈大小可能不同),在 reference 示例地址每次都不同,因为获取引用的地址将产生被引用对象的地址(在这种情况下,容器中的项目)
请注意,在您的案例中,auto
代表 int
。所以这是一条红鲱鱼。考虑
for (int i = 0; i < 10; ++i){
int j = i;
cout << (void*)&j << '\n';
}
由于 j
具有自动存储持续时间,它很可能每次都使用相同的地址创建 - 但指向不同的值 - ,j
被压入然后从堆栈弹出每次迭代(让我们搁置编译器优化)。这就是您使用 for (auto it : con){
的情况。 it
具有 自动存储期限。
写的时候
for (auto& it : con){
it
是容器 con
中对 int
的 引用 ,因此其地址在每次迭代中都会不同。
for (auto it : con){
Same address with different content !?
这对于具有自动存储持续时间的变量来说是非常典型的。这与C++中的auto
†无关。如果您使用 int
:
for (int it : con){
it
(以及循环中的每个自动变量)的生命周期只是一次迭代。由于上一次迭代it
的生命周期结束,下一次迭代可以重新使用相同的内存,这就是地址相同的原因。
Why does the '&' sign make a change under this circumstance?
因为 T&
声明了一个 引用 来键入 T
。引用变量不同于非引用(对象变量)。不是像对象那样持有一个值,而是 "refers" 对另一个对象的引用。
在引用上使用addressof运算符时,结果将是引用对象的地址;不是引用的地址(它甚至可能没有地址,因为它不是对象)。这就是后一种情况下地址发生变化的原因。在这种情况下,引用将引用存储在 what
节点中的 int
对象(因为 con
本身是一个引用,并引用传递的对象)。
† 我在 C++ 中提到,因为在 C 中 auto
实际上是一个存储 class 修饰符,表示自动存储 class。它在标准 C++ 中从来没有这种含义,它在 C 中的使用也已过时。是B语言残留的关键字。
在 C++ 中,auto
声明一个将从上下文中推导出来的类型。