0
#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

  • Does this answer your question? [Uninitialized variable behaviour in C++](https://stackoverflow.com/questions/30172416/uninitialized-variable-behaviour-in-c) – Silvio Mayolo Sep 16 '22 at 17:02
  • default initialization for `int` means no initialization for `int`, which means you get a garbage value and using it has unspecified behavior. – NathanOliver Sep 16 '22 at 17:02
  • 2
    Pick *one* language: C and C++ are two very different language, and their specification say different things about this case. – Some programmer dude Sep 16 '22 at 17:03
  • The only similarity between C and C++ is that uninitialized local variables really *are* uninitialized, with an *indeterminate* value. – Some programmer dude Sep 16 '22 at 17:04
  • 2
    `0` is as much garbage as `4341551`, in the sense that you cannot predict either. Be grateful that you didn't get [nasal demons](http://catb.org/jargon/html/N/nasal-demons.html) instead. – Yksisarvinen Sep 16 '22 at 17:04
  • FYI, since you tagged as C++, you should change `` to ``. – Thomas Matthews Sep 16 '22 at 17:04
  • Yes, clarity is needed. They are different languages. Demonstrated is a C program, but it will compile as C++. – Chris Sep 16 '22 at 17:05
  • You have discovered what I call the "confounding expectations rule" of uninitialized local variables. The value of an uninitialized variable like `a` is... very hard to predict. But no matter what, uninitialized local variables **never** start out holding what you expect. If you expected them to be random, you'll find that they're utterly repeatable and predictable, and often 0. But if you expect them to be predictable (and, god help you, if you write code that depends on it), then by jingo, you'll find that they're quite random. – Steve Summit Sep 16 '22 at 17:07
  • BTW, the compiler is allowed to use registers instead of memory locations. The keyword `const` means that you set up an agreement with the compiler that the variable `b` will not be written to after the declaration. The variable `a` is allowed to change; but changing `a` will not affect the value in `b`. Without any initialization for `a`, and the compiler is using registers, the `a` variable will contain whatever value was in the register (used for 'a'). – Thomas Matthews Sep 16 '22 at 17:07
  • Indeterminate value. Can be zero, can be garbage, and there is no predictable behavior. – user16217248 Sep 16 '22 at 17:12
  • An *uninitialized* variable is okay until you access it before assigning to it (initializing). One possible outcome, on the right platform, is that the program terminates due to the **undefined behavior** of reading an uninitialized variable. That's not required behavior by the standard, but is certainly allowed, because **undefined behavior** means that the entire execution is without meaning (so any behavior is allowed - including appearing to work as expected). – Eljay Sep 16 '22 at 17:16

4 Answers4

2

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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

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:

  1. 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.

  2. onlinegdb.com is buggy. It should have given truly random values.

  3. 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?

  1. Hastily put the milk in the refrigerator, and vow to be more careful next time.
  2. 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.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • I was expecting garbage values from both the results. But, the onlinegdb compiler might be buggy as you said. –  Sep 16 '22 at 17:16
  • @SheshadriV, what makes you think you *didn't* see garbage values from both results? Being seemingly predictable under some set of circumstances does not mean the value isn't garbage. Being 0 doesn't mean the value isn't garbage. – John Bollinger Sep 16 '22 at 17:26
  • If the first items labeled 1., 2., and 3. are supposed to be a hypothetical wrong answer, that is very poorly presented. – Eric Postpischil Sep 16 '22 at 17:30
  • @EricPostpischil, I do take the list at the beginning as a list of possible wrong lines of thought in which the OP might have engaged. That's not entirely clear, especially at the beginning, but the inconsistency among them and the comment afterward about teasing seem to point to that. – John Bollinger Sep 16 '22 at 17:33
  • 1
    @EricPostpischil No, the presentation doesn't really work, but I think it was worth a try, because this misunderstanding — "I didn't get random values! Why not?" — is *so* very prevalent. As for all such popular misunderstandings, I'm always searching — perhaps in vain — for better ways of breaking through. – Steve Summit Sep 16 '22 at 17:38
1

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:

  1. 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.
  2. 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.
  3. 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.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

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.

Catcow
  • 7
  • 2
  • Re “no stack frame has written the memory that local variables are placed at”: The start-up code does things before calling `main`, typically including using the stack. And describing the state of uninitialized variables as depending on stack contents (rather than being possibly influenced by stack contents) is wrong because compilers optimize programs and might not use stack at all to deal with variables. – Eric Postpischil Sep 16 '22 at 18:34