Question
Can You Access a Local Variable Outside Its Scope in C++? Dangling Pointers Explained
Question
I have the following C++ code:
#include <iostream>
int* foo()
{
int a = 5;
return &a;
}
int main()
{
int* p = foo();
std::cout << *p;
*p = 8;
std::cout << *p;
}
This program runs without any runtime exception, and the output is:
58
How is that possible? I thought a local variable's memory becomes inaccessible after the function returns. Why does dereferencing the pointer still appear to work?
Short Answer
By the end of this page, you will understand why returning a pointer to a local variable in C++ is unsafe, what a dangling pointer is, why code with undefined behavior can still appear to work, and how to return data safely instead.
Concept
In C++, a local variable such as a inside foo() has automatic storage duration. That usually means it is created when the function begins and destroyed when the function ends.
When you write:
int* foo()
{
int a = 5;
return &a;
}
a stops existing as soon as foo() returns. The address &a may still look like a valid memory address, but the object that used to live there is gone. That means the returned pointer is a dangling pointer.
A dangling pointer is a pointer that points to memory that is no longer valid for the original object.
Using a dangling pointer causes undefined behavior. In C++, undefined behavior means:
- the program may appear to work
- it may print unexpected values
- it may crash
- it may behave differently on another compiler or another run
- the compiler may optimize the code in surprising ways
So the important rule is not "the program always crashes". The rule is: once behavior is undefined, C++ gives no guarantees at all.
Why this matters in real programming:
- It can create hidden bugs that only appear sometimes.
- It can lead to corrupted data.
- It can create security issues.
Mental Model
Think of a local variable like a hotel room booked for a guest during a short stay.
- While the guest is checked in, the room belongs to them.
- When they check out, the room is no longer theirs.
- You may still remember the room number, but that does not mean you are allowed to use the room as if the guest were still there.
A pointer to a local variable is like keeping the room number after checkout.
Sometimes the room may still look unchanged for a while, so it seems usable. But the hotel is free to clean it, reassign it, or use it for something else at any moment.
That is what happens with a dangling pointer: you still have an address, but the original object no longer exists there.
Syntax and Examples
Unsafe example
int* foo()
{
int a = 5;
return &a; // bad: returns address of a local variable
}
This is wrong because a is destroyed when foo() ends.
Safe approach: return by value
For simple types like int, the best solution is usually to return the value directly.
int foo()
{
int a = 5;
return a;
}
int main()
{
int value = foo();
std::cout << value << '\n';
}
This is safe because value in main() receives a copy of the returned integer.
Safe approach: use dynamic allocation carefully
If you truly need a pointer, you can allocate memory dynamically, though modern C++ usually prefers smart pointers.
Step by Step Execution
Consider this code:
int* foo()
{
int a = 5;
return &a;
}
int main()
{
int* p = foo();
std::cout << *p << '\n';
}
Here is what happens conceptually:
main()callsfoo().- Inside
foo(), a local variableais created and set to5. &agets the address ofa.foo()returns that address.foo()ends, soais destroyed.- Back in
main(),pnow holds an address whereaused to be. - tries to read from that old address.
Real World Use Cases
Understanding object lifetime and pointer validity is important in many real programs.
Function return values
A function often creates temporary data. You must know whether it is safe to return:
- a value
- a reference
- a pointer
Returning a pointer to a local variable is unsafe.
APIs and libraries
When using a library, you need to know whether returned pointers remain valid after the call. Some APIs return pointers to internal buffers that may later change.
Data structures
Linked lists, trees, and caches often use pointers. If an object is deleted or goes out of scope while pointers still refer to it, bugs appear.
Multithreaded code
Dangling pointers become even more dangerous when different threads access memory at different times.
Game engines and systems code
Low-level code often manages memory manually. Lifetime mistakes can cause rare, hard-to-reproduce crashes.
Real Codebase Usage
In real C++ codebases, developers avoid this problem by designing ownership and lifetime clearly.
Common safe patterns
Return small objects by value
For types like int, double, std::string, or even many structs, returning by value is normal and efficient.
std::string get_name()
{
return "Alice";
}
Use references only when lifetime is guaranteed
A reference can be safe if the referred object outlives the function call.
void print_value(const int& value)
{
std::cout << value << '\n';
}
Use smart pointers for dynamic ownership
When a function creates an object whose lifetime must continue after return, std::unique_ptr is a common choice.
std::unique_ptr<int> make_value()
{
return std::<>();
}
Common Mistakes
1. Returning a pointer to a local variable
Broken code:
int* foo()
{
int x = 10;
return &x;
}
Why it is wrong:
xis destroyed when the function returns.- The returned pointer dangles.
Fix:
int foo()
{
int x = 10;
return x;
}
2. Returning a reference to a local variable
Broken code:
int& foo()
{
int x = 10;
return x;
}
This has the same lifetime problem.
3. Assuming "it worked once" means it is valid
Broken reasoning:
- "The program printed the correct value."
- "There was no crash."
- "So the pointer must be fine."
This is false. Undefined behavior can look correct by accident.
Comparisons
| Concept | What it means | Safe to return from a function? | Notes |
|---|---|---|---|
| Local variable | Object created inside a function | No, not by pointer/reference to it | Destroyed when function ends |
| Return by value | Return a copy or moved value | Yes | Best for simple data and many class types |
| Pointer to heap object | Points to dynamically allocated memory | Yes, if ownership is handled correctly | Must free memory or use smart pointers |
| Reference to local variable | Alias to a local object | No | Same lifetime problem as pointer |
| Static local variable | Local variable with program-long lifetime | Yes, with care | Shared across calls |
Local vs static local
Cheat Sheet
Quick rules
- Do not return a pointer to a local variable.
- Do not return a reference to a local variable.
- Local variables inside a function are destroyed when the function returns.
- A pointer to a destroyed object is a dangling pointer.
- Dereferencing a dangling pointer causes undefined behavior.
- Undefined behavior may seem to work, crash, or do anything else.
Unsafe pattern
int* foo()
{
int x = 5;
return &x; // wrong
}
Safe patterns
Return by value:
int foo()
{
return 5;
}
Return smart pointer:
std::unique_ptr<int> foo()
{
return std::make_unique<int>(5);
}
Return pointer to static local if shared lifetime is intended:
FAQ
Why does the code print 58 instead of crashing?
Because the program has undefined behavior. The old stack memory happened to still contain usable-looking data, but that is not guaranteed.
Is the memory really gone after the function returns?
The bytes may still physically exist, but the object no longer exists there as a valid int. You cannot legally use that memory as if the local variable were still alive.
Is this a scope problem or a lifetime problem?
It is mainly a lifetime problem. The variable name a is out of scope outside the function, and the object itself is destroyed when the function returns.
Would setting the pointer to nullptr help?
Only if you do it before using it. The problem is that foo() returns a non-null pointer that is already invalid.
Can I safely return a reference to a local variable instead?
No. A reference to a local variable has the same lifetime problem.
What should I return instead of &a?
Usually return the value directly:
int foo()
{
return 5;
}
Are there tools that detect this bug?
Mini Project
Description
Build a small C++ program that demonstrates safe and unsafe ways to return data from functions. This helps you see the difference between returning by value, returning a dangling pointer, and returning a smart pointer.
Goal
Create a program that prints values returned from functions safely and clearly identifies the unsafe pattern to avoid.
Requirements
Requirement 1
Keep learning
Related questions
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Base Class Constructor Rules Explained
Learn how C++ base class constructors are called from derived classes, including order, syntax, defaults, and common mistakes.
C++ Casts Explained: C-Style Cast vs static_cast vs dynamic_cast
Learn the difference between C-style casts, static_cast, and dynamic_cast in C++ with clear examples, safety rules, and real usage tips.