These are both, of course, UB, as the other answerers said. They also gave some good ways to do what you want to do in a proper fashion. But you were asking why does this actually happen in your case. To understand it, you need to understand what happens in the stack when you call a function. I'll try to provide a really simplified explanation.
When a function is called, a new stack frame is created on top of the stack. All the data in the function is put onto the stack frame. So, for the function
char* allocateMemory()
{
char str[20] = "Hello world.";
return str;
}
The stack frame for allocateMemory
will contain, besides some other stuff, the 20 elements of the string (char array) str
.
For this function:
int* another()
{
int x = 5;
return &x;
}
The stack frame for another
will contain the contents of the variable x
.
When a function returns, the stack pointer, which marks the top of the stack, drops all the way down to where it was before a function invocation. However, the memory is still there on the stack, it doesn't get erased - it is a costy and pointless process. However, there is no longer anything protecting this memory from being overwritten by something: it has been marked "unneeded".
Now, what's the difference between your calls to printf
? Well, when you call printf
, it gets its own stack frame. It overwrites what was left of the previous called function's stack frame.
In the first case, you just pass pString
to printf
. Then printf
overwrites the memory that once was the stack frame of allocateMemory
, and the memory that was once str
gets covered with stuff printf
needs to work with string output, like iteration variables. Then it proceeds to try and get memory pointed to by the pointer you passed to it, pString
... But it has just overwritten this memory, so it outputs what looks like garbage to you.
In the second case, you first got the value of the pointer blah
, which resides in your local scope. Then you dereferenced it with *blah
. Now comes the fun part: you've done the dereferencing before you've called another function which could overwrite the contents of the old stack frame. Which means the memory that was once the variable x
in the function another
is sort of still there, and by dereferencing the pointer blah
, you get the value of x
. And then you pass it to printf
, but now, it doesn't matter that printf
will overwrite another
's stack frame: the values you passed to it are now sort of "safe". That's why the second call to printf
outputs the values you expect.
I've heard of people who dislike using the heap so much that they use this "trick" in the following way: they form a stack array in a function and return
a pointer to it, then, after the function returns, they copy its contents to an array in the caller's scope before calling any other function, and then proceed to use it. Never do this, for the sake of all the people who may read your code.