How does C manage stack with pointers? - c

So I was messing up with dynamic memory and pointers, and I was wondering how C was managing the stack when it comes to pointers that points to local variables.
I came out with this simple function :
int* dummy(){
int test = 4;
int *t2;
t2 = &test;
return t2;
}
This function initialize a pointer, and an int as a local variable (should not be accessible outside of my function, as the stack state will be restored once I get out of the function). However, I am returning the pointer as the result of my function.
I can get the pointer back and print the value of my local variable with :
#include <stdio.h>
int main(void){
int* p = dummy();
// some other calls to other functions to mess up the bellow stack,
// where my local variable "test" was supposed to be landing
printf("%d\n", *p); // printing the value of "test" (which is 4)
}
Result
$ ./a.out
4
Why is this printing the correct result? Isn't the pointer pointing at a variable in a stack from an other state? I am confused.
If the memory stays somewhere without dynamic allocation, where does it stay? Is it lost forever? (no way to "free" it)
EDIT after the comments
The behavior is undefined. Adding compiler options for warning such as pedantic will print a warning that I am returning a pointer pointing at a local variable, and the executable gets bugus.
The reason for this is that dummy's stack state get lost when the program exists the function, thus not assuring the value of local variables, because they are... local.

One of the possible outcomes of undefined behavior is - behaving as expected.
Once dummy returns, test no longer exists - logically speaking. However, the region of the stack it occupied may not be immediately overwritten, so that value may persist in that (virtual) location for some time afterwards.
The pointer is invalid - we’re using it to access an object outside of that object’s lifetime - so the behavior is definitely undefined. But that doesn’t mean that the value must be something other than 4.

Undefined behavior is undefined. There is no value this program can print that is, in any sense, "correct". What's confusing you is that you think there is some "correct" value this program can print and therefore you wonder why it's printing the "correct" value.
The problem is entirely in your incorrect understanding that some value is more "correct" than some other value. All values are equally "correct" for this program.

I was wondering how C was managing the stack when it comes to pointers that points to local variables
It does not. C does not even require a stack. What you are doing is undefined behavior which means that the C standard does not impose ANY requirements on the compiler.
So the answer to your question has nothing to do with the C standard. It can be boiled down to "how it's usually handled". But when you do stuff like it is, as I mentioned, undefined behavior. So this kind of code is likely to be messed up as soon as you turn on any compiler optimizations.
It's possible to make educated guesses about the behavior, but you have no guarantees.
Actually, it's kind of like looking into your neighbours window and be surprised that it's the same neighbour you've had for years. In the exact same way, you have no guarantees that your neighbour has not suddenly moved and someone else have moved in. Well, in your case, this happened. Your neighbour have not moved out yet.

Related

What value does a const variable take when it's initialized by a non-const and non-initialized variable?

#include <stdio.h>
int main()
{
int a;
const int b = a;
printf("%d %d\n", a, b);
return 0;
}
The same code I tried to execute on onlinegdb.com compiler and on Ubuntu WSL. In onlinegdb.com, I got both a and b as 0 with every run, whereas in WSL it was a garbage value. I am not able to understand why garbage value is not coming onlinegdb.com
Using int a; inside a function is described by C 2018 6.2.4 5:
An object whose identifier is declared with no linkage and without the storage-class specifier static has automatic storage duration, as do some compound literals…
Paragraph 6 continues:
… The initial value of the object is indeterminate…
An indeterminate value is not an actual specific value but is a theoretical state used to describe the semantics of C. 3.19.2 says it is:
… either an unspecified value or a trap representation…
and 3.19.3 says an unspecified value is:
… valid value of the relevant type where this document imposes no requirements on which value is chosen in any instance…
That “any instance” part means that the program may behave as if a has a different value each time it is used. For example, these two statements may print different values for a:
printf("%d\n", a);
printf("%d\n", a);
Regarding const int b = a;, this is not covered explicitly by the C standard, but I have seen a committee response: When an indeterminate value is assigned (or initialized into) another object, the other object is also said to have an indeterminate value. So, after this declaration, b has an indeterminate value. The const is irrelevant; it means the source code of the program is not supposed to change b, but it cannot remedy the fact that b does not have a determined value.
Since the C standard permits any value to be used in each instance, onlinegdb.com conforms when it prints zero, and WSL conforms when it prints other values. Any int values printed for printf("%d %d\n", a, b); conform to the C standard.
Further, another provision in the C standard actually renders the entire behavior of the program undefined. C 2018 6.3.2.1 2 says:
… If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
This applies to a: It could have been declared register because its address is never taken, and it is not initialized or assigned a value. So, using a has undefined behavior, and that extends to the entire behavior of the program on the code branch where a is used, which is the entire program execution.
I am not able to understand why garbage value is not coming
This is a very strange statement. I wonder: what kind of answer or explanation do you expect you might get? Something like:
Everyone who said that "uninitialized local variables start out containing random values" lied to you. WSL was wrong for giving you random values. You should have gotten 0, like you did with onlinegdb.com.
onlinegdb.com is buggy. It should have given truly random values.
The rules for const variables are special. When you say const int b = a;, it magically makes a's uninitialized value more predictable.
Are you expecting to get an answer like any of those? Because, no, none of those is true, none of those can possibly be true.
I'm sorry if it sounds like I'm teasing you here. I agree, it's surprising at first if an uninitialized local variable always starts out containing 0, because that's not very random, is it?
But the point is, the value of an uninitialized local variable is not defined. It is unspecified, indeterminate, and/or undefined. You cannot know what it is going to be. But that means that no value — no possible value — that it contains can ever be "wrong". In particular, onlinegdb.com is not wrong for not giving you random values: remember, it's not obligated to give you anything!
Think about it like this. Suppose you buy a carton of milk. Suppose it's printed on the label, "Contents may spoil — keep refrigerated." Suppose you leave the carton of milk on the counter overnight. That is, suppose you fail to properly refrigerate it. Suppose that, a day later, you realize your mistake. Horrified, you carefully open the milk carton and take a small taste, to see if it has spoiled. But you got lucky! It's still okay! It didn't spoil!
Now, at this point, what do you do?
Hastily put the milk in the refrigerator, and vow to be more careful next time.
March back to the store where you bought the milk, and accuse the shopkeeper of false advertising: "The label says 'contents may spoil', but it didn't!!" What do you think the shopkeeper is going to say?
This may seem like a silly analogy, but really, it's just like your C/C++ coding situation. The rules say you're supposed to initialize your local variables before you use them. You failed. Yet, somehow, you got predictable values anyway, at least under one compiler. But you can't complain about this, because it's not causing you a problem. And you can't depend on it, because as your experience with the other compiler showed you, it's not a guaranteed result.
Typically, local variables are stored on the stack. The stack gets used for all sorts of stuff. When you call a function, it receives a new stack frame where it can store its local variables, and where other stuff pertaining to that function call is stored, too. When a function returns, its stack frame is popped, but the memory is typically not cleared. That means that, when the next function is called, its newly-allocated stack frame may end up containing random bits of data left over from the previous function whose stack frame happened to occupy that part of stack memory.
So the question of what value an uninitialized local variable contains ends up depending on what the previous function might have been and what it might have left lying around on the stack.
In the case of main, it's quite possible that since it's the first function to be called, and the stack might start out empty, that main's stack frame always ends up being built on top of virgin, untouched, all-0 memory. That would mean that uninitialized variables in main might always seem to start out containing 0.
But this is not, not, not, not, not guaranteed!!!
Nobody said the stack was guaranteed to start out containing 0. Nobody said that there wasn't some startup code that ran before main that might have left some random garbage lying around on the stack.
If you want to enumerate possibilities, I can think of 3:
The function you're wondering about is one that always gets called first, or always gets called at a "deep leaf" of the call stack, meaning that it always gets a brand-new stack frame, and on a machine where the stack always starts out containing 0. Under these circumstances, uninitialized variables might always seem to start out containing 0.
The function you're wondering about does not always get a brand-new stack frame. It always gets a "dirty" stack frame with some previous function's random data lying around, and the program is such that, during every run, that previous function was doing something different and left something different on the stack, such that the next function's uninitialized local variables always seem to start out containing different, seemingly random values.
The function you're wondering about is always called right after a previous function that always does the same thing, meaning that it always leaves the same values lying around, meaning that the next function's uninitialized local variables always seem to start out with the same values every time, which aren't zero but aren't random.
But I hope it's obvious that you absolutely can't depend on any of this! And of course there's no reason to depend on any of this. If you want your local variables to have predictable values, you can simply initialize them. But if you're curious what happens when you don't, hopefully this explanation has helped you understand that.
Also, be aware that the explanation I've given here is somewhat of a simplification, and is not complete. There are systems that don't use a conventional stack at all, meaning that none of those possibilities 1, 2, or 3 could apply. There are systems that deliberately randomize the stack every time, either to help new programs not to accidentally become dependent on uninitialized variables, or to make sure that attackers can't exploit certain predictable results of a badly written program's undefined behavior.
When your operating system gives your program memory to work with, it will likely be zero to start (though not guaranteed). As your program calls functions it creates stack frames, and your program will effectively go from the .start assembly function to the int main() c function, so when main is called, no stack frame has written the memory that local variables are placed at. Therefore, a and b are both likely to be 0 (and b is guaranteed to be the same as a). However, it's not guaranteed to be 0, especially if you call some functions that have local variables or lots of parameters. For instance, if your code was instead
#include <stdio.h>
void foo()
{
int x = 42;
}
int main()
{
foo();
int a;
const int b = a;
printf("%d %d\n", a, b);
return 0;
}
then a would PROBABLY have the value 42 (in unoptimized builds), but that would depend on the ABI (https://en.wikipedia.org/wiki/Application_binary_interface) that your compiler uses and probably a few other things.
Basically, don't do that.

NULL behavior when function returns address of local variable in C

I have the following C code:
#include <stdlib.h>
#include <stdio.h>
char* foo() {
char abc[4] = "abc";
return abc;
}
int main() {
printf("%s", foo());
return 0;
}
If I compile it with gcc and run the executable file, I got (null)% as output.
If I run the slightly modified code:
#include <stdlib.h>
#include <stdio.h>
char* foo() {
char abc[4] = "abc";
return abc;
}
int main() {
printf("%c", *(foo()));
return 0;
}
I got a segmentation fault.
My question is: why wouldn't my first code get a segmentation fault? I'm running Linux and gcc version: gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Both code, when compiled, will generate a warning: function returns address of local variable [-Wreturn-local-addr] warning
At the moment return abc; starts to execute, abc is a pointer to an array defined inside foo. (Formally, it designates the array itself, but it is automatically converted to the address of the first element.) The function would be returning this pointer value. However, when execution of the function ends, the lifetime of the array ends.
Per C 2018 6.2.4 2:
The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
When a value is indeterminate in C, it may behave as if it has any value, including having a different value each time you attempt to use it or having a trap value (C 2018 3.19.2 and 3.19.3). Note that this does not just mean what the pointer value points to is indeterminate; the value of the pointer itself is indeterminate.
So, even if abc had some address in memory, say 100400, that does not mean 100400 is returned to the caller. The value returned to the caller is indeterminate: It can be anything, including a null pointer value.
It appears your compiler’s optimizer has responded to the undefined behavior in your code by providing or allowing a null pointer value as the return value of the function foo. This is allowed by the C standard.
When you passed this null pointer to printf for use with %s, your printf implementation checked the pointer, saw it was a null pointer, and printed “(null)” instead of attempting to use it to access a string in memory.
When you tried to dereference the pointer, using *(foo()), there was no preliminary check of the pointer value. The machine code of the program attempted to use the null pointer to access memory, and this resulted in a segment fault.
Because you are creating a local variable abc, that variable will only valid in the scope of the function foo. Returning the address of that variable makes no sense as as soon as you return from foo the address will not longer be valid.
Also keep in mind C uses the stack to pass arguments to functions and to return from values from them. As well the local variable is also creating in the stack which will be modified by the function call mechanism, so using that address will corrupt the stack eventually.
To create pointers you should use heap allocation (using malloc family of functions) or you must ensure the variable is inside an existing scope by the time you use it.
Your second code invokes undefined behavior as you try to dereference a pointer which points to a local variable. Now this local variable doesn't exists outside it's scope. Thus, the memory isn't valid
In first code, you try to access local variable outside it's scope. Now in this case function is expected to return a char *. As you return a local variable, what you get is null printing which doesn't cause segmentation fault.
Consider the following sequence of events:
You check into a hotel, and you’re placed in room 137.
You tell the front desk to call your friends and invite them to a wild dance party in your room tomorrow at 2AM.
However, your reservation doesn't last until 2AM tomorrow. Perhaps there's another reservation for a different guest. Perhaps the room will stay vacant. Who knows. Maybe the front desk people know. Maybe they don't.
So what should the front desk do?
They could still send an invitation that indicates room 137, perhaps not knowing that you won't be there at that time, because they forgot to check their reservation records. Or maybe they just don't care.
Or they could refuse to send an invitation and tell you that.
Or perhaps they could just ignore your request, not send anything, and not tell anyone.
Or they could send and invitation, but indicate a bogus room number. Perhaps they have invitation blanks prepared beforehand, and they need to just fill in the time and the room number. But being technologically advanced as they are, they won't fill a room number if they know it is not reserved to this particular guest, and sent out one with a default room number — zero perhaps?
Perhaps if we live in the future, they might even send an electronic invitation with room 137 indicated in it — that will self-destruct the moment you check out from the hotel!
Whatever they do, they cannot send an invitation indicating a correct room number, because there is no correct room number. You won't be at any room number. So they do whatever. They may always choose one strategy to deal with this situation. Or they may flip a coin. Or perhaps different staff members will do different things. Who knows.
So some of their strategies will produce a spectacular crash (your friend wakes up a wrong guest at a wrong time, they call a police, and all doesn't end well).
Other strategies will produce less dramatic outcomes. Refuse to continue and let you know? Let you know something is wrong, but continue anyway? Ignore a dangerous instruction? Replace it with a less dangerous instruction? All of these things are possible.
This corresponds to what a compiler might do when you instruct it to do an obviously dangerous and illegal thing. Ignore the danger, or refuse to continue with a diagnostic message, or produce a diagnostic message and continue anyway, or skip the dangerous instruction altogether (but only if it is 100% sure the destruction is imminent), or tweak it slightly so that it is less dangerous. Real compilers actually do all of these things in different circumstances. The important thing is to know that asking a compiler for an impossible thing doesn't always result in the program actually attempting to do the impossible thing.
This answer is in part based on the answer https://stackoverflow.com/a/63862176/775806 which was deleted by its author.
Dereferencing object which does not exist is an Undefined Behaviour.
Why first works: my guess is because compiler has optimized out the call to the function.

Returning a pointer to a variable stored in the stack

I am currently learning the C language and I please you to apologize me if my question is stupid.
As far as I am concerned returning pointers to variables stored in the stack is a bad idea, since the the memory that contains the variable is cleared when the function returns. Hence I expect to get a Segmentation fault when executing the following piece of code:
int *foo()
{
int j = 5;
int *ptr = &j;
return ptr;
}
int main()
{
int *p;
p = foo();
printf("%d\n", *p);
return 0;
}
However when compiled with gcc (version 8.3.0) the program works apparently fine (no compiler warnings as well) and outputs 5, rather than a Segmentation fault. My question is why does this piece of code work when it is supposed not to.
Thank you in advance!
Yes, returning the address of a local variable is a bad idea, as that local (j) ceases to exist when the function returns, and that pointer is now invalid.
However, dereferencing an invalid pointer is not guaranteed to lead to a segfault. The behavior is undefined, which means quite literally anything can happen, including appearing to work correctly.
What’s likely happening is that the portion of the stack that contained j has not yet been overwritten, so the printf just happens to work.
… the memory that contains the variable is cleared when the function returns…
That is not correct. When the function returns, the storage for its local objects is merely no longer reserved for use for those objects.
The C standard says that, when an object’s lifetime begins, storage (memory) is reserved for it. That means the C implementation provides some memory that can be used for that object, and that it will not use for any other purpose. When the object’s lifetime ends, the C standard says that storage is no longer reserved. The standard does not say that the memory is cleared or unmapped. It also does not say that it is not cleared or that it is not unmapped.
A C implementation could clear the memory, but normal C implementations do not, because that is generally a waste of resources. Most commonly, the memory remains unchanged until it is used for other purposes. But other effects are possible too, such as removing the memory from the process’ virtual address space. So it is normal that you would be able to use the memory after the function returns (and before you call any other functions that alter the memory), but it is also normal that errors would occur in your program if you use the memory. The behavior is not guaranteed either way.

Why is this C program returning correct value in VC++2008?

We know that automatic variables are destroyed upon the return of the function.
Then, why is this C program returning correct value?
#include <stdio.h>
#include <process.h>
int * ReturningPointer()
{
int myInteger = 99;
int * ptrToMyInteger = &myInteger;
return ptrToMyInteger;
}
main()
{
int * pointerToInteger = ReturningPointer();
printf("*pointerToInteger = %d\n", *pointerToInteger);
system("PAUSE");
}
Output
*pointerToInteger = 99
Edit
Then why is this giving garbage values?
#include <stdio.h>
#include <process.h>
char * ReturningPointer()
{
char array[13] = "Hello World!";
return array;
}
main()
{
printf("%s\n", ReturningPointer());
system("PAUSE");
}
Output
xŤ
There is no answer to that question: your code exhibits undefined behavior. It could print "the right value" as you are seeing, it could print anything else, it could segfault, it could order pizza online with your credit card.
Dereferencing that pointer in main is illegal, it doesn't point to valid memory at that point. Don't do it.
There's a big difference between you two examples: in the first case, *pointer is evaluated before calling printf. So, given that there are no function calls between the line where you get the pointer value, and the printf, chances are high that the stack location pointer points to will not have been overwritten. So the value that was stored there prior to calling printf is likely to be output (that value will be passed on to printf's stack, not the pointer).
In the second case, you're passing a pointer to the stack to printf. The call to printf overwrites (a part of) that same stack region the pointer is pointing to, and printf ends up trying to print its own stack (more or less) which doesn't have a high chance of containing something readable.
Note that you can't rely on getting gibberish either. Your implementation is free to use a different stack for the printf call if it feels like it, as long as it follows the requirements laid out by the standard.
This is undefined behavior, and it could have launched a missile instead. But it just happened to give you the correct answer.
Think about it, it kind of make sense -- what else did you expect? Should it have given you zero? If so, then the compiler must insert special instructions at the scope end to erase the variable's content -- waste of resources. The most natural thing for the compiler to do is to leave the contents unchanged -- so you just got the correct output from undefined behavior by chance.
You could say this behavior is implementation defined. For example. Another compiler (or the same compiler in "Release" mode) may decide to allocate myInteger purely in register (not sure if it actually can do this when you take an address of it, but for the sake of argument...), in that case no memory would be allocated for 99 and you would get garbage output.
As a more illustrative (but totally untested) example -- if you insert some malloc and exercise some memory usage before printf you may find the garbage value you were looking for :P
Answer to "Edited" part
The "real" answer that you want needs to be answered in disassembly. A good place to start is gcc -S and gcc -O3 -S. I will leave the in-depth analysis for wizards that will come around. But I did a cursory peek using GCC and it turns out that printf("%s\n") gets translated to puts, so the calling convention is different. Since local variables are allocated on the stack, calling a function could "destroy" previously allocated local variables.
Destroying is the wrong word imho. Locals reside on the stack, if the function returns the stack space may be reused again. Until then it is not overwritten and still accessible by pointers which you might not really want (because this might never point to something valid)
Pointers are used to address space in memory, for local pointers the same as I described in 1 is valid. However the pointer seems to be passed to the main program.
If it really is the address storing the former integer it will result in "99" up until that point in the execution of your program when the program overwrite this memory location. It may also be another 99 by coincidence. Any way: do not do this.
These kind of errors will lead to trouble some day, may be on other machines, other OS, other compiler or compiler options - imagine you upgrade your compiler which may change the behaviour the memory usage or even a build with optimization flags, e.g. release builds vs default debug builds, you name it.
In most C/C++ programs their local variables live on the stack, and destroyed means overwritten with something else. In this case that particular location had not been overwritten yet when it was passed as a parameter to printf().
Of course, having such code is asking for trouble because per the C and C++ standards it exhibits undefined behavior.
That is undefined behavior. That means that anything can happen, even what you would expect.
The tricky part of UB is when it gives you the result you expect, and so you think that you are doing it right. Then, any change in an unrelated part of the program changes that...
Answering your question more specifically, you are returning a pointer to an automatic variable, that no longer exists when the function returns, but since you call no other functions in the middle, it happens to keep the old value.
If you call, for example printf twice, the second time it will most likely print a different value.
The key idea is that a variable represents a name and type for value stored somewhere in memory. When it is "destroyed", it means that a) that value can no longer be accessed using that name, and b) the memory location is free to be overwritten.
The behavior is undefined because the implementation of the compiler is free to choose what time after "destruction" the location is actually overwritten.

C behavior (non initialized values)

I had a question regarding a C code.
#include <stdio.h>
void foo(void){
int a;
printf("%d\n",a);
}
void bar(void){
int a = 42;
}
int main(void){
bar();foo();
}
Apparently I am supposed to get a 42 as a result at the end of compilation. http://www.slideshare.net/olvemaudal/deep-c , slide #126. But when I compile it on my machine I get garbage values (gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5))
Do I need to turn off some optimization? Or is the author of the slide wrong about this? Can someone explain what should be the result of the code? If it is 42, could you also explain how/why? Also, would the result be any different on a C++ compiler (g++).
There is no knowing what you will get. The assumption I guess is that the second stack frame will be in the same location as the last and as the var a is the first variable it should be assigned the same location as the a from the previous functions stackframe. But that is not defined behavior.
Accessing uninitialized variables is simply undefined behavior.
Undefined behavior means that the standard doesn't specify how to behave in some situations, including this one. The compiler might even "make demons fly out of your nose" (popular joke about UB :) )
In that article the program works because bar's a and foo's a are placed in the same memory location, but this is completely uninsured and you should never rely on such behaviors!
Things that you can try to obtain the desired result (the value propagating from bar to foo):
Maybe the variable a is allocated in CPU registers and not in stack. In order to force allocation on stack, try using its address (&a) in some way.
Maybe the compiler generates some code to stuff the uninitialized variables on stack with a debugging pattern (0xcdcdcdcd) - i think gcc doesn't do it but maybe i am wrong?
Examine the machine code generated by the compiler - this is the ultimate way
When you call a function, the CPU first pushes the function arguments on the stack, pushes the return address on the stack and then proceeds to jump to the function's code.
There, it saves the old stack position by pushing it on the stack (function prologue).
Next, it allocates the a variable on the stack (again) and sets it's value to 42.
While exiting the function (function epilogue), the CPU removes the a variable from the stack by just moving the stack pointer back to where it saved the old stack position and gets that old position. The value 42 stored at that place in the memory is kept intact!
Then, the second function gets called and the same process takes place:
push arguments (we skip this step since there are none)
push return address (at the same place in memory that the other function's return address was stored)
push stack position (same as with return address)
allocate the a variable which ends in the exact same place in memory as the a variable from the bar() function!
Since bar's a variable was initialized to 42, that value is still there and foo's a "inherits" it by pointing to the same place in memory.
Stack before calling bar():
[someThings]<=StackPointer
Stack while in bar():
[someThings][returnAddress][oldStackPointer][a = 42]<=StackPointer
Stack after bar() and before foo():
[someThings]<-StackPointer {oldStackPointer}{a = 42}
Stack while in foo():
[someThings][newRetAddress][oldStackPointer][a (= 42 as the value was here before)]<=StackPointer
So, yes, the author of the slides is right and the ones saying he is wrong are in fact wrong themselves :)
(take note that it is important that the code is compiled without optimization)
As others noted, this is undefined behaviour.
However, the author is partly right, on some architectures the address of the local variables will be the same, so foo will read the data that bar left there.
# gcc x.c; ./a.out
42
It's an interesting way to show what goes behind the scenes, but do not depend on it, it's architecture, compiler and optimization dependent:
# gcc -O2 x.c; ./a.out
0
BTW, if you read the next slides you'll get all the answers...
The author of that code is making a lot of platform-specific assumptions that simply don't hold in general. He's assuming that local variables are stored in memory in a specific way, and that those locations won't be overwritten between function calls. In general, he is wrong.
This is a textbook example undefined behavior - it's bad code, and the language standard places no requirements on the compiler to do anything specific with it.
Your two 'a' variables are local to function they're contained inside. The memory space occupied by the 'a' in foo() is not the same memory space as the 'a' in bar. So when you initialize the 'a' in bar, that 42 is thrown away when the function returns, and never ever affects the a in foo.
Since you're accessing an uninitialized variable, the behaviour of your code is undefined.
Having said that, the program is quite likely to print 42 on architectures that I am familiar with. However, it would be outright silly to rely on this, even if the program were to behave this way on your architecture.
If anything, this behaviour might even be a hindrance, as getting consistent values from an uninitialized variables could mask bugs.

Resources