Posted in

Bug of the week #13

Let’s look at this C++ program:

C++
#include <iostream>

int main() {
    int x;

    if (x == 0) {
        std::cout << "Zero";
    }
    else {
        std::cout << "Not zero";
    }
}

When you build it in Debug and in Release mode, then:

  • Debug build prints Not zero
  • Release build prints Zero

Same code. Same compiler. Different result.

What does it mean?

The bug comes from the undefined behavior of the uninitialized variable x:

C++
int x;

x is declared, but never initialized and immediately used:

C++
if (x == 0)

At this point, the program is reading a variable that has no defined value at all.

Why it happens

Stack memory

When you write:

C++
int x;

on the stack, no initialization happens. The memory might contain:

  • leftover values from previous function calls
  • debug patterns inserted by the runtime
  • basically anything

So x is effectively “whatever bits happened to be in that memory slot”.

Debug vs Release changes memory behavior

This is where things get interesting. Debug runtimes often:

  • fill stack memory with known patterns (e.g. 0xCC, 0xCD)
  • keep extra safety checks
  • avoid aggressive register optimization

So x is very likely to contain a non-zero pattern, but in Release mode:

  • stack memory is not initialized
  • registers are heavily optimized
  • memory reuse is aggressive

So sometimesx happens to be 0, but sometimes it gets optimized into a register that defaults to zero in that execution path The C++ standard does not say that x is garbage or random or zero. It says that reading an uninitialized variable is undefined behavior what means:

  • compiler is allowed to assume it never happens
  • optimizations may remove or rewrite logic
  • results can differ between builds, machines or even runs

How to fix it?

The simplest fix may be limited to just adding curly braces {} after x – it will zero-initialize it, so that it has a known value (equal to 0) in both types of builds:

C++
#include <iostream>

int main() {
    int x {};

    if (x == 0) {
        std::cout << "Zero";
    }
    else {
        std::cout << "Not zero";
    }
}

Now behavior is fully defined and consistent. I also encourage to enable compiler warnings to catch all such places:

MDX
-Wall -Wextra -Wuninitialized -Werror

Read also: