3

I hope this is not a duplicate, I've read a number of related questions but no one seemed to cover this case:

 #include <iostream>

int* return_dangling_p()
{
        int x = 1;
        return &x;  // warning: address of local variable 'x' returned
}

void some_func()
{
    int x = 2;
}

int main(int argc, char** argv)
{
    //  UB 1
    int* p = return_dangling_p();
    std::cout << *p;               // 1
    some_func();
    std::cout << *p;               // 2, some_func() wrote over the memory

    // UB 2
    if (true) {
        int x = 3;
        p = &x;     // why does compiler not warn about this?
    }
    std::cout << *p;    // 3
    if (true) {
        int x = 4;    
    }
    std::cout << *p;    // 3, why not 4?

    return 0;
}

I thought these are two cases of the same undefined behaviour. The output is 1233 while I (naively?) expected 1234.

So my question is: why doesn't compiler complain in the second case and why the stack isn't rewritten like in the case of 12? Am I missing something?

(MinGW 4.5.2, -Wall -Wextra -pedantic)

EDIT: I'm aware that it's pointless to discuss outputs of UB. My main concern was if there's any deeper reason to why one is detected by the compiler and the other isn't.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • 1
    I would imagine that even in debug mode the compiler is smart enough to optimize `if (true) {int x=4;}` away to nothing. Note1: you don't need the `if (true)`, just have `{ int x=4;}`. Note2: after `int x=4;` add in `p = p` and see if that "corrects" the behavior. – Mooing Duck Sep 21 '11 at 17:00
  • Also: http://ideone.com/EdNNL results in "1133", with two warnings about unused variables. – Mooing Duck Sep 21 '11 at 17:02
  • 2
    possible duplicate of [Can a local variable's memory be accessed outside its scope?](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) – sbi Sep 21 '11 at 17:03
  • @sbi: I don't think this is a duplicate - this question is mainly about temporaries within a function which go out of scope, rather than local variables in other functions. – Paul R Sep 21 '11 at 17:12
  • @PaulR: To me it seems it asks why the OP sees a certain result after invoking UB by referencing objects that went out of scope. And that is what Eric so figuratively explained. – sbi Sep 21 '11 at 17:17
  • 1
    If you turn on the warnings the compiler will complain. Unfortunately the default warning level on most compilers are too low. Increase the warning level and consider all warnings as logical errors in your code (ie make the compiler stop after detecting a warning and you will be fine). I consider the following the minimum in gcc `-Wall -Wextra -Weffc++ -Wstrict-aliasing -ansi -pedantic -Werror` – Martin York Sep 21 '11 at 18:11

2 Answers2

3

why doesn't compiler complain in the second case

I am not sure. I suppose it could.

why the memory isn't rewritten like in the case of 12

It's undefined behaviour. Anything can happen.

Read on if you're really curious...

When I compile your code as-is, my compiler (g++ 4.4.3) places the two x variables in UB 2 at different locations on the stack (I've verified this by looking at the disassembly). Therefore they don't clash, and your code also prints out 1233 here.

However, the moment I take the address of the second x, the compiler suddenly decides to place it at the same address as the first x, so the output changes to 1234.

if (true) {
    int x = 4;    // 3, why not 4?
    &x;
}

Now, this is what happens when I compile without any optimization options. I haven't experimented with optimizations (in your version of the code, there's no reason why int x = 4 can't be optimized away completely).

The wonders of undefined behaviour...

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • I did try printing out the adresses of x's and they're indeed at different locations. – jrok Sep 21 '11 at 17:04
3

I'm not sure why the compiler doesn't complain. I suppose it's not a very common use-case, so the compiler authors didn't think to add a warning for it.

You can't infer anything useful about behaviour you observe when you are invoking undefined behaviour. The final output could have been 3, it could have been 4, or it could have been something else.

[If you want an explanation, I suggest look at the assembler that the compiler produced. If I had to guess, I'd say that the compiler optimised the final if (true) { ... } away entirely.]

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680