In order to understand WHY you shouldn't try to return a pointer to a local variable, you need to visualize how local variables are allocated in the first place.
Local variables are allocated in the STACK. The stack is a reserved memory area having as main purpose, leaving a "breadcrumb" trail of memory addresses where the CPU should jump once it finishes executing a subroutine.
Before a subroutine is entered (usually via a CALL
machine language Instruction in x86 architectures), the CPU will push on the stack the address of the Instruction immediately following the CALL.
ret_address_N
. . . . . . .
ret_address_3
ret_address_2
ret_address_1
When the subroutine ends, a RET
urn Instruction makes the CPU pop the most recent address from the stack and redirects execution by jumping to it, effectively resuming execution on the subroutine or function that initiated the call.
This stack arrangement is very powerful, as it allows you to nest a high number of independent subroutine calls (allowing generic, reusable libraries to be built), it also allows recursive calls, where a function can call itself (either directly, or indirectly, by a nested subroutine).
Additionally, nothing prevents you from pushing custom data on the stack (there are special CPU instructions for this) AS LONG AS THE STACK STATE IS RESTORED BEFORE RETURNING FROM A SUBROUTINE, otherwise when the RET Instructions pops the expected return address, it will fetch garbage and it will try to jump execution to it, most likely crashing. (Incidentally, this is also how many malware exploits work, by overwriting the stack with a valid address, and forcing the CPU to jump to malicious code when it performs a RET instruction)
This stack feature may be used, for example, to store the original state of the CPU registers that are modified inside a subroutine - allowing the code to restore their values before the subroutine exits so that the caller subroutine can see the registers in the same state as they were BEFORE performing the subroutine CALL.
Languages like C also use this feature to allocate local variables by setting up a Stack Frame. The compiler basically adds up how many bytes are required to account for every local variable in a certain subroutine, and will emit CPU instructions that will displace the top of the stack by this computed byte amount when a subroutine is called. Now every local variable can be accessed as a relative offset to the current stack's state.
-------------
------------- local variables for subroutine N
-------------
ret_address_N
------------- local variables for subroutine 3
ret_address_3
------------- local variables for subroutine 2
-------------
ret_address_2
-------------
------------- local variables for subroutine 1
-------------
-------------
ret_address_1
Besides emitting instructions to set up the stack frame (effectively allocating local variables on the stack and preserving current register values), the C compiler Will emit instructions that will restore the stack state to its original state before the function call, so the RET Instruction can find at the top of the stack the correct memory address when it pops the value it should jump to.
Now you can understand why you can not should not return a pointer to a local variable. By doing so, you are returning an address to a value that was stored temprorarily in the stack. You can dereference the pointer and MIGHT see what looks like valid data as you immediately return from the subroutine returning the pointer to the local variable, but this data will certainly be overwritten, probably in the very near future, as program execution continues calling subroutines.