DEV Community

Calin Baenen
Calin Baenen

Posted on

When does a pointer become dangling in C/C++? | Why isn't this example dangling?

All testing has been done on SoloLearn's C++ playground.

So, I was planning on making a post on a custom pointer datatype, and I was playing around with (raw-)pointers, and I wanted to test something, to see if I could (purposefully) get a pointer to dangle, so I tried something I was certain would work:

int main() {
    int* p;
    {
        int i = 10;
        p = &i;
    }
    std::cout << p;
}
Enter fullscreen mode Exit fullscreen mode

and I was baffled when I saw 10 in the output box.
So, I tried (virtually) the same thing in C:

int main() {
    int* p;
    {
        int i = 10;
        p = &i;
    }
    printf("%d", *p);
}
Enter fullscreen mode Exit fullscreen mode

and I got 10 again.

I thought the conditions for a pointer to become dangling was for the referenced value to leave the current scope. But as it's apparent, even though i (and its value) leaves the scope. (Yes, it is leaving the scope, for anyone who wants to object. This is called a guard-scope, and allows temporary variables to exist.)
So, what are the actual conditions for a pointer to become dangling? And/or why is my example NOT dangling?

Thanks!
Cheers!

Discussion (5)

Collapse
deciduously profile image
Ben Lovy • Edited on

I could be wrong, but I think this is dangling, but you're getting lucky - likely because your program doesn't do anything else. This is still undefined behavior. When i goes out of scope, the compiler is free to reuse that memory location for anything else. However, p is still pointing to the same location. If nothing else has happened to that memory, it's still sitting there unchanged, so your program produces the correct output, but there's nothing in the standard mandating this to be true anymore.

You can see this behavior sometimes when trying to print a variable that hasn't been initialized in your program. You might get something random back, which is just whatever that memory was previously used for. Even though the program that allocated it has called all its destructors and released it to the OS, that doesn't mean it's been overwritten yet, just that it's allowed to be reused if needed.

Collapse
thumbone profile image
Bernd Wechner

Sounds right to me. To add to it, I also am far from sure (C goes back some decades in terms of practical use and experience for me) but it could well be that i and p are both living on a stack, and that the embracing scope of the function main() in this instance remains on the stack until that function returns. Seen another way it may be undefined behaviour but also slightly better than mere chance that you experience it in this example (i.e. that it's reliably reproducible) in part because it's a a very terse example but also possibly because the anonymous scope you'd declared i in may (being anonymous) have the stack life that the enclosing function's scope does. That is assuming C puts variables on the stack at all .... my memory on implementation details is, alas, rather distant ;-)

Collapse
baenencalin profile image
Calin Baenen Author

So, the same as I replied to someone on Twitter who had a pretty similar answer; and as you said by saying I'm "getting lucky": The compiler that SoloLearn was just being merciful in its decision not to provide a segfault?

Collapse
deciduously profile image
Ben Lovy • Edited on

A segfault always comes from the OS, not the compiler, when you hit memory that's "not yours". In this case, the compiler did what you asked, it showed you the memory at that location. I believe the other comment is correct about why this is reproducible, both i and p live in the stack frame allocated for main(), which still exists when the printing happens, so nothing has had a chance to interfere even though you're pointing to a variable that isn't in scope. The memory is still "yours", as in, belongs to the program, even though semantically within your program it's out of scope, so the OS doesn't complain. However, it's not guaranteed to be reproducible, and different compilers are free to handle this in totally different ways.

Collapse
pgradot profile image
Pierre Gradot

Just to confirm the other comment: the pointer is dangling.

It points to a variable that no longer exists.

The address of this variable (which is the value of the pointer) hasn't been reused yet so when you read the memory at this adress, you find the value of your dead variable.

This is (almost) pure coincidence.

This is why dangling pointers (or references) are so complicated to debug: you may observe an invalid behavior only once in a while.