in school the teacher told me that when the call to a function reaches an end everything declared inside the function's block will be erased.
But I wrote the following code:
int * secret()
{
int arr[10]={0};
arr[0]=9999;
return arr;
}
int main() {
printf("%d",secret()[0]);
return 0;
}
and the output was 9999 which doesn't suit what I was taught.
in school the teacher told me that when the call to a function reaches an end everything declared inside the function's block will be erased.
That is a misleading characterization. If your instructor used that specific wording then they did you a disservice. It is not altogether wrong, though, depending on how one interprets it.
What the language specification says is that the lifetime of an object declared, without a storage-class specifier, inside a block (such as the block serving as the function body in a function definition) ends when execution of the innermost containing block ends. You might characterize that as such objects being "erased" in the sense of erasure from existence, but not in the sense of having their contents cleared.
the output was 9999 which doesn't suit what I was taught.
Trying to be as charitable towards the instructor as possible, I do suggest considering that you may have misunderstood what they were trying to tell you.
In any event, attempting to access an object whose lifetime has ended produces undefined behavior. Moreover, when an object's lifetime ends, the values of any pointers to that object become indeterminate. This means that your program's output does not, indeed cannot, contradict what the language specification says about the situation, because the program output is undefined. Any output or none would be equally consistent with C language semantics. If we suppose that your instructor was trying to convey a characterization consistent with the specification, then they are not contradicted either.
In your example, the local variable "arr" gets allocated on the stack. When you return from secret(), the program releases that portion of the stack for use by the next function that gets called, but the pointer you returned is still pointing to that memory location. It still exists as it was until another function comes along and uses that portion of the stack.
Your teacher was correct if you assume they meant that the memory should not be used after returning, but specifically, the stack is not erased as part of the return. This is because by definition the local variables are no longer in scope, and cannot be expected to be in any particular state. There is no reason to use processor cycles to erase it.
The C language requires the designer to manage their use of pointers, which can be both useful and terrible. It gives the designer a lot of freedom, but it is totally up to the designer to know what is on the other end of that pointer and what types of operations they should being doing with it.
In most implementations function epilogue just changes the stack pointer but does not purge the actual data. If you need to purge the data you
need to do it yourself.
void foo()
{
char verySecret[5000];
char verySecret2[5000];
char verySecret4[5000];
/* do something */
/* now purge the data */
purge(verySecret,0, sizeof(verySecret));
purge(verySecret2,0, sizeof(verySecret2));
purge(verySecret4,0, sizeof(verySecret4));
}
Related
#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.
I found out something hilarious problem in C.
Here is my Code
#include <stdio.h>
void method()
{
int indx;
printf("%d\n", indx);
indx++;
}
int main(void)
{
method();
method();
method();
}
This is simple example.
indx variable is not initialized. So, The result of printf in method function would be something strange value.
And indx is local variable. So indx++ is useless.
But The answer is
0
1
2
It seems like that indx is remain
I can't understand. why?
Local variables are stored on the stack. Because you are doing no other action between each function call, the stack is essentially preserved (unaltered) between calls, and the lack of initialisation means that the value of indx is whatever happens to be stored in that stack location.
If you made other function calls, the stack might well be overridden, resulting in non-sequential values, and likely not starting from zero.
One reason why the stack might contain zero values at startup can be because, depending on compiler, compiler options and standard library startup, the stack might be deliberately initialised to zero (or just because the region of memory used happens to contain zeros from a previous application etc).
One should never rely on such behaviour, it is specifically undefined behaviour, and thus initialising variables and ensuring you understand variable scope is an important thing to learn in software development.
I think there are plenty of descriptions of how stack is used in software development languages (and variability across hardware architectures and implementations) that you should research that separately.
The local variables are especially stored in the memory area of the corresponding function, hence you can see the increment of the indx value by single value each time the function is called.
But the answer returns the memory values of the stored variable.
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.
I came across a piece of code which is working fine as of now,but in my opinion its undefined behavior and might introduce a bug in future.
Pseudo code :
void OpertateLoad(int load_id)
{
int value = 0;
/* code to calculate value */
SetLoadRequest(load_id,&value);
/*some processing not involving value**/
}
void SetLoadRequest(int load_id, int* value)
{
/**some processing**/
LoadsArray[load_id] = *value;
/**some processing**/
}
In my understanding C compiler will not guarantee where Auto variables will be stored. It could be stack/register(if available and suitable for processing).
I am suspecting that if compiler decides to store value onto general purpose register then, SetLoadRequest function might refer to wrong data.
Am I getting it right?or I am overthinking it?
I am using IARARM compiler for ARM CORTEX M-4 processor.
----------:EDIT:----------
Answers Summarize that " Compiler will ensure that data is persisted between the calls, no matter where the variable is stored ".
Just want to confirm : Is this behavior also true 'if a function is returning the address of local auto variable and caller is de-referencing it?'.
If NO then is there anything in C standard which guarantees the behavior in both cases? Or As I stated earlier its undefined behavior?
You are overthinking it. The compiler knows that, if value is in a register, that it must be stored to memory before passing a pointer to that memory to SetLoadRequest.
More generally, don't think about stack and registers at all. The language says there's a variable (without saying how it's implemented), and that you can take its address and use that in another function to refer to the variable. So you can!
The language also says that local variables cease to exist when leaving a block, so this permission does not extend to returning pointers to local variables (which causes undefined behavior if the caller does anything at all with the pointer).
I am overthinking it?
Yes!
The compiler will take care of this. If it stores it in a register, it will know how to handle it (with a memory load).
C11 draft standard n1570:
6.2.4 Storage durations of objects
6 For such an object that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.) If the block is entered recursively, a new instance of the
object is created each time. The initial value of the object is indeterminate. If an
initialization is specified for the object, it is performed each time the declaration or
compound literal is reached in the execution of the block; otherwise, the value becomes
indeterminate each time the declaration is reached.
To my understanding, the scope of value is the body of OpertateLoad. However, SetLoadRequest assigns the value pointed to, so the actual value of value is copied. No undefined behaviour is involved.
I thought that once a function returns, all the local variables declared within (barring those with static keyword) are garbage collected. But when I am trying out the following code, it still prints the value after the function has returned. Can anybody explain why?
int *fun();
main() {
int *p;
p = fun();
printf("%d",*p); //shouldn't print 5, for the variable no longer exists at this address
}
int *fun() {
int q;
q = 5;
return(&q);
}
There's no garbage collection in C. Once the scope of a variable cease to exist, accessing it in any means is illegal. What you see is UB(Undefined behaviour).
It's undefined behavior, anything can happen, including appearing to work. The memory probably wasn't overwritten yet, but that doesn't mean you have the right to access it. Yet you did! I hope you're happy! :)
If you really want it to loose the value, perhaps call another function with at least a few lines of code in it, before doing the printf by accessing the location. Most probably your value would be over written by then.
But again as mentioned already this is undefined behavior. You can never predict when (or if at all) it crashes or changes. But you cannot rely upon it 'changing or remaining the same' and code an application with any of these assumptions.
What i am trying to illustrate is, when you make another function call after returning from previous one, another activation record is pushed on to the stack, most likely over writing the previous one including the variable whose value you were accessing via pointer.
No body is actually garbage collecting or doing a say memset 0 once a function and it's data goes out of scope.
C doesn't support garbage collection as supported by Java. Read more about garbage collection here
Logically, q ceases to exist when fun exits.
Physically (for suitably loose definitions of "physical"), the story is a bit more complicated, and depends on the underlying platform. C does not do garbage collection (not that garbage collection applies in this case). That memory cell (virtual or physical) that q occupied still exists and contains whatever value was last written to it. Depending on the architecture / operating system / whatever, that cell may still be accessible by your program, but that's not guaranteed:
6.2.4 Storage durations of objects
2 The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address,33)
and retains
its last-stored value throughout its lifetime.34)
If an object is referred to outside of its
lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when
the object it points to (or just past) reaches the end of its lifetime.
33) The term ‘‘constant address’’ means that two pointers to the object constructed at possibly different
times will compare equal. The address may be different during two different executions of the same
program.
34) In the case of a volatile object, the last store need not be explicit in the program.
"Undefined behavior" is the C language's way of dealing with problems by not dealing with them. Basically, the implementation is free to handle the situation any way it chooses to, up to ignoring the problem completely and letting the underlying OS kill the program for doing something naughty.
In your specific case, accessing that memory cell after fun had exited didn't break anything, and it had not yet been overwritten. That behavior is not guaranteed to be repeatable.