Reputation: 209
recently I've encountered very very peculiar question when using auto in C++, just ... just look at the following code snippet :
my main function:
#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;
}
And here's function read:
void read(const list<int>& con){
for (auto it : con){
printf("%p\n", &it);
cout << it << endl;
}
return ;
}
And here's is the output :
0x7fffefff66a4
10
0x7fffefff66a4
20
0x7fffefff66a4
30
What the heck is that? Same address with different content !?
And more strange this is, if I modify the for-loop by adding an '&'
that is:
for (auto& it : con){
All the output makes sense immediately, the addresses would change by iteration
So my question is,
Why does the '&' sign make a change under this circumstance?
Upvotes: 3
Views: 1324
Reputation: 6588
let's see the expanded version of the :
loop syntax first.
for( auto it: container) {
...
}
is conceptually the same as
for( auto _it = container.begin(); _it != container.end(); it++) {
auto it = *_it;
...
}
while the reference form:
for( auto& it: container)
is the same as
for( auto _it = container.begin(); _it != container.end(); it++) {
auto &it = *_it;
...
}
So in the first case it
is a copy of the items in the container, in the second case it is a (lvalue) reference of it, hence if you modify it
in the second loop it affects the items in the container
The address issue too can be explained this way: in the copy example the local variable has always the same address in each loop iteration (because their lifetime do not overlap, the compiler has no reason not to use the same address in the stack), thought if you factorize the code inside a function you may observe it changing in different function invocation (because the stack size might be different), in the reference example the address is different every time, because taking the address of a reference will yield the address of the object being referenced (in this case, the item in the container)
Upvotes: 7
Reputation: 238351
for (auto it : con){
Same address with different content !?
This is very typical for variables with automatic storage duration. This has nothing to do with auto
in C++†. You would get the same result if you had used int
:
for (int it : con){
The lifetime of it
(as well as each automatic variable within the loop) is just a single iteration. Since the lifetime of the it
in last iteration was ended, the next iteration can re-use the same memory, and that's why the address is the same.
Why does the '&' sign make a change under this circumstance?
Because T&
declares a reference to type T
. Reference variables are different from non-references (object variables). Instead of holding a value such as an object would, a reference instead "refers" to another object.
When you use the addressof operator on a reference, the result will be the address of the referred object; not the address of the reference (which might not even have an address, since it's not an object). That is why the address changes in the latter case. In this case, the references would refer to the int
objects that are stored in the nodes of what
(because con
itself is a reference, and refers to the passed object).
† I mention in C++, because in C auto
is in fact a storage class modifier that signifies automatic storage class. It has never had that meaning in standard C++, and its use obsolete in C as well. It's a vestigial keyword from the B language.
In C++, auto
declares a type that will be deduced from context.
Upvotes: 20
Reputation: 234715
Note that auto
is standing in for int
in your case. So it's a red herring. Consider
for (int i = 0; i < 10; ++i){
int j = i;
cout << (void*)&j << '\n';
}
Since j
has automatic storage duration, it is most likely created each time with the same address - but points to a different value - , j
is being pushed then popped from a stack on each iteration (let's set aside compiler optimisations). That is what is happening in your case with for (auto it : con){
. it
has automatic storage duration.
When you write
for (auto& it : con){
it
is a reference to an int
within the container con
, so its address will differ on each iteration.
Upvotes: 6