3

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?

9
  • You have too many questions in your post. Which one would like answered here? Commented Dec 5, 2017 at 14:29
  • 2
    And you should definitely get a good beginners book or two if you don't understand the meaning of & in its different forms. Commented Dec 5, 2017 at 14:31
  • 6
    Your question isn't about auto, it's about iterators. Replace auto with int, same results. Commented Dec 5, 2017 at 14:32
  • 2
    The compiler is free to reuse memory for variables inside loops. It would not make sense to allocate new memory for a loop of a few million iterations, for example. Commented Dec 5, 2017 at 14:38
  • 1
    @Shih-ChanHuang in case of copy, the same memory is reused every iteration, hence the same address. In case of reference, the address is the address of the actual memory containing list element. so it is different for every element Commented Dec 5, 2017 at 14:38

3 Answers 3

20
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.

Sign up to request clarification or add additional context in comments.

2 Comments

Plus one for "very typical" and the fact this answer is exceptionally good.
Thanks for your considerate answers, and I wanna give my additional thank to KillZone Kid
7

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)

5 Comments

"the local variable has always the same address (an address in the stack reserved for that variable)" But there isn't just one local variable, there is one for each iteration of the loop. The other answers explain that the compiler is allowed (but not required) to reuse the same memory location for each of those variables, because their lifetime ends before the next iteration starts.
@JonathanWakely my answer justifies the observed behavior of the variable address being the same, in term of what the compiler might have done. Whether relying on the address being the same is wrong from a standard point of view, while may be an interesting discussion, is out of the scope of the question being asked. Indeed the variables are local to the iteration thus there are many local variables, the sentence might need a clarification on this aspect.
Your answer simply states the observed behaviour, it doesn't really explain it. The other answers explain it clearly, and so I'm saying you could improve this answer by explaining the observation, not just observing it :-) (Which I see you've now done, so +1)
@JonathanWakely it does in my opinion. My interpretation of the question is "how can the value be different and the address be the same", which is explained by copy semantic & compiler policy on stack allocation, while in the reference case both the value and the address of the variable are required to be different (by the c++ semantic, since they are reference to different objects whose lifetime exceed the loop iteration)
for a literal explanation of "why the address is the same", the only correct explanation would be "All the local variables have the same address because the compiler decided so" (since he's allowed to do anything). Comparing the address of two objects whose lifetime do not overlap has no standard defined behavior (if the lifetime overlap they are required to be different), hence the only explanation of this observation can be a guess on how the compiler behaved and why.
6

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.