Search

1.6 — Uninitialized variables and undefined behavior

Uninitialized variables

Unlike some programming languages, C/C++ does not initialize most variables to a given value (such as zero) automatically. Thus when a variable is assigned a memory location by the compiler, the default value of that variable is whatever (garbage) value happens to already be in that memory location! A variable that has not been given a known value (usually through initialization or assignment) is called an uninitialized variable.

Author's note

Many readers expect the terms “initialized” and “uninitialized” to be strict opposites, but they aren’t quite! Initialization means the object was provided with an initial value at the point of definition. Uninitialized means the object has not been given a known value (through any means, including assignment). Therefore, an object that is not initialized but is then assigned a value is no longer uninitialized (because it has been given a known value).

To recap:

  • Initialization = The object is given a known value at the point of definition.
  • Assignment = The object is given a known value beyond the point of definition.
  • Uninitialized = The object has not been given a known value yet.

As an aside...

This lack of initialization is a performance optimization inherited from C, back when computers were slow. Imagine a case where you were going to read in 100,000 values from a file. In such case, you might create 100,000 variables, then fill them with data from the file.

If C++ initialized all of those variables with default values upon creation, this would result in 100,000 initializations (which would be slow), and for little benefit (since you’re overwriting those values anyway).

For now, you should always initialize your variables because the cost of doing so is miniscule compared to the benefit. Once you are more comfortable with the language, there may be certain cases where you omit the initialization for optimization purposes. But this should always be done selectively and intentionally.

Using the values of uninitialized variables can lead to unexpected results. Consider the following short program:

In this case, the computer will assign some unused memory to x. It will then send the value residing in that memory location to std::cout, which will print the value (interpreted as an integer). But what value will it print? The answer is “who knows!”, and the answer may (or may not) change every time you run the program. When the author ran this program in Visual Studio, std::cout printed the value 7177728 one time, and 5277592 the next. Feel free to compile and run the program yourself (your computer won’t explode).

As an aside...

Some compilers, such as Visual Studio, will initialize the contents of memory to some preset value when you’re using a debug build configuration. This will not happen when using a release build configuration. Therefore, if you want to run the above program yourself, make sure you’re using a release build configuration (see lesson 0.9 -- Configuring your compiler: Build configurations for a reminder on how to do that). For example, if you run the above program in a Visual Studio debug configuration, it will consistently print -858993460, because that’s the value (interpreted as an integer) that Visual Studio initializes memory with in debug configurations.

Most modern compilers will attempt to detect if a variable is being used without being given a value. If they are able to detect this, they will generally issue a compile-time error. For example, compiling the above program on Visual Studio produced the following warning:

c:\VCprojects\test\test.cpp(11) : warning C4700: uninitialized local variable 'x' used

If your compiler won’t let you compile and run the above program for this reason, here is a possible solution to get around this issue:

Using uninitialized variables is one of the most common mistakes that novice programmers make, and unfortunately, it can also be one of the most challenging to debug (because the program may run fine anyway if the uninitialized variable happened to get assigned to a spot of memory that had a reasonable value in it, like 0).

This is the primary reason for the “always initialize your variables” best practice.

Undefined behavior

Using the value from an uninitialized variable is our first example of undefined behavior. Undefined behavior (often abbreviated UB) is the result of executing code whose behavior is not well defined by the C++ language. In this case, the C++ language doesn’t have any rules determining what happens if you use the value of a variable that has not been given a known value. Consequently, if you actually do this, undefined behavior will result.

Code implementing undefined behavior may exhibit any of the following symptoms:

  • Your program produces different results every time it is run.
  • Your program consistently produces the same incorrect result.
  • Your program behaves inconsistently (sometimes produces the correct result, sometimes not).
  • Your program seems like its working but produces incorrect results later in the program.
  • Your program crashes, either immediately or later.
  • Your program works on some compilers but not others.
  • Your program works until you change some other seemingly unrelated code.

Or, your code may actually produce the correct behavior anyway. The nature of undefined behavior is that you never quite know what you’re going to get, whether you’ll get it every time, and whether that behavior will change when you make other changes.

C++ contains many cases that can result in undefined behavior if you’re not careful. We’ll point these out in future lessons whenever we encounter them. Take note of where these cases are and make sure you avoid them.

Rule

Take care to avoid all situations that result in undefined behavior, such as using uninitialized variables.

Author's note

One of the most common types of comment we get from readers says, “You said I couldn’t do X, but I did it anyway and my program works! Why?”.

There are two common answers. The most common answer is that your program is actually exhibiting undefined behavior, but that undefined behavior just happens to be producing the result you wanted anyway… for now. Tomorrow (or on another compiler or machine) it might not.

Alternatively, sometimes compiler authors take liberties with the language requirements when those requirements may be more restrictive than needed. For example, the standard may say, “you must do X before Y”, but a compiler author may feel that’s unnecessary, and make Y work even if you don’t do X first. This shouldn’t affect the operation of correctly written programs, but may cause incorrectly written programs to work anyway. So an alternate answer to the above question is that your compiler may simply be not following the standard! It happens. You can avoid much of this by making sure you’ve turned compiler extensions off, as described in lesson 0.10 -- Configuring your compiler: Compiler extensions.

Quiz time

Question #1

What is an uninitialized variable? Why should you avoid using them?

Show Solution

Question #2

What is undefined behavior, and what can happen if you do something that exhibits undefined behavior?

Show Solution


1.7 -- Keywords and naming identifiers
Index
1.5 -- Introduction to iostream: cout, cin, and endl

96 comments to 1.6 — Uninitialized variables and undefined behavior

  • Joseph Garza

    That's weird! My program just failed to build. Could it be due to the version that I am using? Can anyone please shed some light on this? It just won't let me build my project.

    NOTE: I get a warning sign reading the following: Using uninitialized memory X.

    ADDITION TO THE NOTE: I AM SWITCHING FROM DEBUG TO RELEASE CONFIGURATION.

    Thanks in advance.

    • Alex

      Did you try the version with the doNothing() function?

      • Joseph Garza

        I thought it was the weirdest thing because I did try the doNothing() function but still it just would not compile. I just moved forward with the course as this is my first time ever doing anything with programming. I figured it would clear up as I moved forward =P

  • chunkboi

    i get 0 for x whenever i run an uninitialized variable . i am using Clion 2021.1.2

    • Gonc

      You're probably running the program with release configuration instead of debug.

      • Waldo Lemmer

        "Some compilers, such as Visual Studio, will initialize the contents of memory to some preset value when you’re using a debug build configuration. This will not happen when using a release build configuration."

        So it's the opposite.

        I get 0 either way for some reason (Visual Studio)

        • Alex

          Getting a consistent value of 0 in release builds is still undefined behavior, as it could change the next time you compile, or if the program is ported to another system.

    • Pratik

      Are you using direct list initialization to define a variable?

  • Grg

    (because the program may run fine anyway if the uninitialized value happened to get assigned to a spot of memory that had a reasonable value in it, like 0).

    Excuse me, is not that?

    (...if the uninitialized variable happened to...).

    Thank you anyway.

  • Julian

    Can someone explain the menu pop-up when I hit Alt+F4 on visual studio?

  • Sahil

    In the answer for quiz 1,

    An uninitialized variable is a variable that has not been given "a value" by the program (generally through initialization or assignment).

    shouldn't it be "a known value" instead? As the program will assign memory and the variable will have a certain value, its just that it'll be some garbage data.

    • Nerf Longshot

      This is not true for all cases, for example if you initialize a variable outsid of a function (For Eg- main), then it is initialised to 0 by default. This is also considered as an uninitialised variable.

  • jason

    I wonder when and where should i use void doNothing(int &),and will we explain the basic principle of this after this chapter , thank you

  • sarayu sree

    what is a default value

    • Robert Warner

      Your compiler may simply assign a default value of whatever it is programmed to assign if you don't assign a variable a value. The default value could be anything. Or it may not. The old Borland Turbo never did- what ever happened to be in the memory slot that the variable points to was what became the variable's value.
      A variable is a memory address. If I code 'int x', x is a pointer to that memory address. Whatever is randomly in that block of memory is what I retrieve when I code 'cout << x;' unless compiler assigns a default value.

  • Otabek

    my program is always giving 0 fo x

    • Allan

      For me, it initially did but then I created a doNothing() function that declared another int and assigned it to an arbitrary number and returned the int + 1. I then executed the function (well, I assigned the result to another variable to trick the compiler into not optimizing it out) and after that I defined x. Now it gave another seemingly random value (always the same). Does it happen because my OS nulls the newly allocated memory (for security reasons?) but then by assigning a value to a temp var that then gets removed (I can't find a better word) and its memory space gets assigned to x, I already have something else there?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]