1

Are there cases where it's ok to use a variable when it has not been initialized, or is it always assumed to be garbage? For example, one case would be:

// global example?
// static example?
// extern example?
// etc.
{
    int n=1, max_n;
    printf("%d %d\n", n, max_n);
}

In this case max_n has a garbage/undefined value. But are there ever cases where the value is known and can be used, such as doing something like bool item being auto-initialized to 0/false, or is that never the case in C?

samuelbrody1249
  • 4,379
  • 1
  • 15
  • 58

6 Answers6

3

By definition, if something is not initialized, it does not have a defined value. If it does have a defined value, it is initialized.

From cppreference:

The value in an uninitialized variable can be anything – it is unpredictable, and may be different every time the program is run. Reading the value of an uninitialized variable is undefined behaviour – which is always a bad idea. It has to be initialized with a value before you can use it.

Also from cppreference about implicit initialization:

If an initializer is not provided:

  • objects with automatic storage duration are initialized to indeterminate values (which may be trap representations)
  • objects with static and thread-local storage duration are zero-initialized

So for example, int a; will be zero-initialized if declared in e.g. global scope, as a static class variable, as a thread_local variable, etc. In other cases, it will be uninitialized.

Alexander Guyer
  • 2,063
  • 1
  • 14
  • 20
  • But it seems from some of the other answers that it **is** initialized when it is outside of local scope, right? – samuelbrody1249 Jan 23 '21 at 21:53
  • 1
    Yes. It is. That doesn't in any way contradict my answer. But I suppose I can add an edit and clarify. – Alexander Guyer Jan 23 '21 at 21:54
  • 2
    cpp reference should not be used as source of knowledge. Refer to the Standard. cppreference if full of errors, misunderstandings and false imprecise information – 0___________ Jan 23 '21 at 22:01
  • cppreference is actually a fine source of information. It is open to community editing; it is not cplusplus.com. If you notice wrong information on it, you should edit it with the correct information and a citation to the standard rather than complaining that it is wrong. Saying it is a bad source of information is like saying SO is a bad source of information. Both are free, accessible, open, and, while not *always* correct, will almost always give you the answers you're looking for one way or another. And for the record, if I owned the standard, I would have cited it, but it costs 198 CHF – Alexander Guyer Jan 23 '21 at 22:51
  • @AlexanderGuyer you can sue draft which is more less the standard. – 0___________ Jan 23 '21 at 23:04
2

In C, reading an uninitialized variable results in undefined behavior. It may be 0, but it may also be any random value that happens to be in that memory address. To make a long story short - don't rely on the values of uninitialized variables.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • thanks, is there a place in the standard or elsewhere where it basically says that's the case across everywhere? – samuelbrody1249 Jan 23 '21 at 21:40
  • 1
    "but it may also be any random value that happens to be in that memory address". Strictly speaking, undefined behavior means all bets are off. The program could crash or it could format your hard drive. – Michael Burr Jan 23 '21 at 21:49
  • @MichaelBurr what about the case with non-local variables? – samuelbrody1249 Jan 23 '21 at 21:53
  • @samuelbrody1249 see in my answer – 0___________ Jan 23 '21 at 21:57
  • Re “In C, reading an uninitialized variable results in undefined behavior”: This is not generally true. Reading an object of automatic storage duration that has not been initialized or had its address taken has undefined behavior per C 2018 6.3.2.1 2, but this is not generally true for objects with other storage duration or if the address is taken. In those cases, the object has an indeterminate value (which is not a value at all but rather a property meaning that the object may act as if it has a different value each time it is used). – Eric Postpischil Jan 23 '21 at 23:24
2

Not sure if you would consider this a "case", but in C, local variables are allocated on the stack, and are uninitialized.

However, global and static variables are allocated in one of the program data sections, so they are zeroed by the OS at load time.

See this question for more information on that including standard reference: Why are global and static variables initialized to their default values?

Lev M.
  • 6,088
  • 1
  • 10
  • 23
  • 1
    C does not know anything about the stack – 0___________ Jan 23 '21 at 21:49
  • C also does not know anything about program data sections. It doesn't even know what .bss is. Not all platforms even have a .bss section. And even if the standard did know about it, not all OSes which use a .bss section zero-initialize the values in it. However, many of them *do* both have a .bss section and zero-initialize them, so this part of the standard may be implemented in such platforms by using the .bss section and leaving the zero-initialization up to the OS, but the C standard in no way guarantees this. – Alexander Guyer Jan 23 '21 at 22:08
  • @AlexanderGuyer `but the C standard in no way guarantees this` it does guarantee 6.7.8.10 of the standard. – 0___________ Jan 23 '21 at 23:02
  • @0___________: Alexander Guyer seems to be saying that the C standard does not guarantee that the zero-initialization is not done in the way stated, not that the C standard does not guarantee that zero-initialization is not done. And there is no 6.7.8.10 in the current standard, C 2018, nor a paragraph 10 of clause 6.7.8. 6.7.9 10 (note the space, not a period; using a period for the paragraph fails to distinguish between, for example, 6.7 1 and 6.7.1, which are different things; some people use `p` instead of space, so 6.7p1) addresses default initialization. – Eric Postpischil Jan 23 '21 at 23:18
  • @EricPostpischil it was from the 2007draft. – 0___________ Jan 23 '21 at 23:27
2

Global and static objects are initialized with the default value for their type (0 for integers, 0.0 for floats, NULL for pointers etc.)

No other uninitialized value can be relied upon.

Here is a quick explanation why: Most implementations have all local variables on a stack. This stack grows when you declare local variables or when you call a function, and shrinks when you do the reverse action - exit the scope of a variable or return from a function.

Now let's see a program:

void good()
{
    int p = 2;
    printf("%d", p);
}

void notgood()
{
    int p;
    printf("%d", p);
}

int main()
{
    good();
    notgood();
    notgood();
}

Here is the stack at the beginning of the program (stack grows downwards). The stack pointer always points to the top element (represented by an arrow):

|---------------------|
|main's return address| <-- stack pointer

Next is immediately after good() gets called:

|---------------------|
|main's return address|
|good's return address| <-- stack pointer

Next we declare p and initialize it with 2:

|---------------------|
|main's return address|
|good's return address|
|value 2 (variable p) | <-- stack pointer

After that, we have the call to printf:

|---------------------|
|main's return address|
|good's return address|
|value 2 (variable p) |
|value 2 (parameter)  |
|format string address|
|printf's return addr |
|#printf's frame#     | <-- stack pointer

When printf returns, the return address and parameters are popped of the stack, but they are not erased from memory. That would be inefficient. We simply decrease the stack pointer.

|---------------------|
|main's return address|
|good's return address|
|value 2 (variable p) | <-- stack pointer
|value 2 (parameter)  |
|format string address|
|printf's return addr |
|#printf's frame#     |

Next, our good() function returns to main:

|---------------------|
|main's return address| <-- stack pointer
|good's return address|
|value 2 (variable p) |
|value 2 (parameter)  |
|format string address|
|printf's return addr |
|#printf's frame#     |

Call notgood. Whatever trash is on the stack gets overwritten:

|---------------------|
|main's return address|
|notgood's return addr| <-- stack pointer
|value 2 (variable p) |
|value 2 (parameter)  |
|format string address|
|printf's return addr |
|#printf's frame#     |

Declare the variable (allocate the space), but we don't initialize. Hence, the old garbage value is still there:

|---------------------|
|main's return address|
|notgood's return addr|
|value 2 (variable p) | <-- stack pointer
|value 2 (parameter)  |
|format string address|
|printf's return addr |
|#printf's frame#     |

Next, we call printf again. Please note that it's return address actually changes, so the old trash is overwritten on the stack:

|---------------------|
|main's return address|
|notgood's return addr|
|value 2 (variable p) |
|value 2 (parameter)  |
|format string address|
|printf's return addr |
|#printf's frame#     | <-- stack pointer

So, as you can see, if you don't initialize variables, they take the value of whatever there was on the stack.

Be aware that the program may not work, as the compiler could optimize some function calls out.

DarkAtom
  • 2,589
  • 1
  • 11
  • 27
  • the parameter does not have to be passed on the stack (assuming implementation using stack) and actually in most real (when optimizations are on) cases it will be passed via the register. S – 0___________ Jan 23 '21 at 22:59
  • Re “if you don't initialize variables, they take the value of whatever there was on the stack”: This should not be asserted as it is not true and not reliable. When an object is not initialized, optimization may result in eliminating the load from stack that would occur, so the value may actually come from whatever is leftover in some register. Other behaviors are also possible. – Eric Postpischil Jan 23 '21 at 23:26
1

The answer to your question is NO. That will depend on which scope that variable was declared. If it is a global or static variable it will be initialized to 0. In the other cases there is nothing you can be sure about.

vmp
  • 2,370
  • 1
  • 13
  • 17
0

Result of any operation using not initialized automatic storage variables is Undefined. What they hold initially is not determined.

C (2007 draft) Standard (6.7.8 p10):

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

0___________
  • 60,014
  • 4
  • 34
  • 74
  • Indeterminate and undefined are different things. – Eric Postpischil Jan 23 '21 at 23:27
  • @EricPostpischil In this case standard says that the value is indeterminate **and** it is Undefined Behaviour. https://i.stack.imgur.com/y2KYH.png And I wrote that `result of the operation ***using*** ...` is UB. So please explain what are you complaining about – 0___________ Jan 23 '21 at 23:31
  • Annex J is informative, not normative, and is incomplete. The applicable normative text is in C 2018 6.3.2.1 2, which 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.” Thus, for example, reading array elements, which are necessarily accessed by address and cannot be declared `register`, does not have undefined behavior by this. – Eric Postpischil Jan 23 '21 at 23:47