4

I wonder, if I can reuse the pointer to a variable inside a loop block.

int *ptr = nullptr;
for (int i = 0; i < 5; ++i) {
    int j = 5;
    if (!ptr) ptr = &j;
    cout << *ptr << endl;
}

I believe that this code will work on all compilers, but does it conform to standards?

Borisko
  • 358
  • 1
  • 5
  • Why write code that is inefficient and maybe dubious? Just use `&j` instead of `ptr` and then avoid such problems – Ed Heal Nov 11 '15 at 10:17
  • Assuming it's a fictional example (I don't see any reason to use it): no, it **compiles** but it **does not work** on _all compilers_ and platforms. Where j variable is allocated (and when) is implementation defined (it may be on stack and it may be reused, it may be not. according to its type it may be moved to heap, it may be compiled and executed for a platform without stack allocated variables)... – Adriano Repetti Nov 11 '15 at 10:19
  • @AdrianoRepetti, well, actually, it works in ideone: http://ideone.com/gCCXjn – alexeykuzmin0 Nov 11 '15 at 10:23
  • It _works_ and it's _correct_ are pretty different things. In that example (maybe for O3) j won't even ever stay in memory and it will live in a register. Loop itself may also be simply unrolled...the point is that C++ standard doesn't say anything about this (it's unspecified) then each implementation is free to do all assumptions it wants. It may work because _by case_ your assumption matches what compiler did **in this case**. It may be broken with another compiler, another version, another platform or another optimizations set... – Adriano Repetti Nov 11 '15 at 10:28
  • Compiler (with optimizations) will probably first translate to something like: `int j = 5; int* ptr = &j; cout << *ptr << endl;` (`cout` repeated 5 times). Then if `ptr` isn't used in any other place...it'll simply generate `cout << 5 << endl;` (repeated five times). See also [this post](http://stackoverflow.com/a/2354049/1207195) but especially **read comments** in [this post](http://stackoverflow.com/a/2354088/1207195). Your assumption is broken (in that case). – Adriano Repetti Nov 11 '15 at 10:36
  • I enjoyed reading [this answer](http://stackoverflow.com/a/6445794/238902), which gives a nice anology regarding "it works" and "it is correct" being two different things. – default Nov 11 '15 at 10:42
  • The example would be better if `j` were computed from `i` and `*ptr` were used without a function call. Then a sane optimizer might unwind the loop, and might overlap the computations of `j` (and/or throw some away as never used) but in any case might not put them all in the same place. – JSF Nov 11 '15 at 10:47
  • To not "work" the code must not only put another iteration of `j` in a different place, but also destroy the `5` put in the stale location of the first `j`. In theory that `5` wouldn't still be there, but in practice it will be. So even if the compiler unwound the loop, and overlapped work such that it couldn't put all `j` in the same place, the stale `5` still "works" in place of each valid `5`. No matter how robust, it is still **undefined**. But a less robust version would give more insight into the problem. – JSF Nov 11 '15 at 10:59

1 Answers1

6

No, it doesn't work.

On the second iteration, ptr is pointing to the j of the first iteration, which doesn't exist anymore. Dereferencing ptr at that point is undefined behavior. Ditto for all iterations after the first one.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • 1
    Probably, the new iteration `j` will be placed exactly at the same memory address as the previous iteration `j`, so, I think this actually will work in most modern compilers. But yes, this is undefined behavior and does not conform to standards. – alexeykuzmin0 Nov 11 '15 at 10:20
  • @alexeykuzmin0 don't make assumption about such subtle and unspecified details. A _modern_ compiler may do whatever it wants for this case. If I'd have to write assembly by hand I wouldn't even put j in memory and compiler will do better than me... – Adriano Repetti Nov 11 '15 at 10:31
  • @alexeykuzmin0: _Probably_, the new `j` will be in the same memory address than the old one. _Probably_ your compiler will not notice that you are dereferencing a pointer to a out-of-scope variable and will not do some crazy optimization. So _probably_ your code will work most of the time, but that doesn't mean that it is correct or that it even is a good idea. – rodrigo Nov 11 '15 at 10:36
  • @rodrigo: I never meant this is correct - I will never write code like this or accept it in code review. Just probably this will work in most cases. – alexeykuzmin0 Nov 11 '15 at 10:41
  • I think you should have just focused on **undefined behavior** because this simple example does work in any compiler you've ever heard of. It would take a lot of effort to construct a more elaborate example of the same undefined behavior that would actually fail. (Get the optimizer to place a local whose address is taken at least once in a different place on a later iteration of the loop). – JSF Nov 11 '15 at 10:42
  • @JSF not so impossible to build [such example](http://stackoverflow.com/a/2354088/1207195) – Adriano Repetti Nov 11 '15 at 10:44
  • @Adriano Repetti, that is not the same thing at all. That is a local in a different place during a different call to the function containing the local. No one would expect the location to be stable in that situation. Also, I only said it would take a lot of effort to construct the example. That implies I know it is not "impossible". – JSF Nov 11 '15 at 10:53
  • Not the different function call but where c and d variables are stored (see comments) anyway what I tried to highlight it's that it's not so _hard_ to break that assumption. – Adriano Repetti Nov 11 '15 at 11:09