C++ Default Initialization | A Guide to "Default Initialization"

C++ Default Initialization | A Guide to "Default Initialization"

이 글의 핵심

Default initialization in C++: when scalars are indeterminate, static zero-init, and avoiding UB with value initialization.

What is Default Initialization?

When a variable is declared without an initializer, default initialization is applied. For local variables, this can result in garbage values. If you need safe zero/default values, use value initialization or zero initialization. To avoid undefined behavior, it’s a good practice to ensure variables are properly initialized before use.

int x;        // Default initialization (garbage value)
int y = 10;   // Explicit initialization

Primitive Types

void func() {
    int x;       // Garbage value
    double d;    // Garbage value
    int* ptr;    // Garbage value
    
    // Must initialize before use
    x = 10;
    d = 3.14;
    ptr = nullptr;
}

Practical Examples

Example 1: Local Variables

void func() {
    int x;  // Garbage value
    
    // ❌ Using before initialization
    std::cout << x << std::endl;  // Undefined behavior
    
    // ✅ Using after initialization
    x = 10;
    std::cout << x << std::endl;  // OK
}

Example 2: Classes

class Widget {
    int value;  // Default initialization
    
public:
    Widget() {}  // `value` is a garbage value
};

// ✅ Member initialization
class Widget {
    int value;
    
public:
    Widget() : value(0) {}  // Explicit initialization
};

Example 3: Arrays

void func() {
    int arr[5];  // Garbage values
    
    // ❌ Using before initialization
    for (int x : arr) {
        std::cout << x << " ";  // Undefined behavior
    }
    
    // ✅ Initialize before use
    for (int& x : arr) {
        x = 0;
    }
}

Example 4: Global vs Local Variables

int global;  // 0 (global variable)

int main() {
    int local;  // Garbage value (local variable)
    
    std::cout << global << std::endl;  // 0
    // std::cout << local << std::endl;  // Undefined behavior
}

Undefined Behavior

void func() {
    int x;
    
    // ❌ Undefined behavior
    if (x > 0) {}
    int y = x + 10;
    int* ptr = &x;
    std::cout << x;
}

Common Issues

Issue 1: Missing Initialization

// ❌ Not initialized
int sum;
for (int i = 0; i < 10; i++) {
    sum += i;  // Undefined behavior
}

// ✅ Proper initialization
int sum = 0;
for (int i = 0; i < 10; i++) {
    sum += i;
}

Issue 2: Pointers

// ❌ Not initialized
int* ptr;
if (ptr) {}  // Undefined behavior

// ✅ Initialize to nullptr
int* ptr = nullptr;
if (ptr) {}  // OK

Issue 3: Class Members

// ❌ Not initialized
class Bad {
    int value;
    
public:
    Bad() {}  // `value` is a garbage value
    
    int getValue() { return value; }  // Risky
};

// ✅ Proper initialization
class Good {
    int value = 0;  // Member initialization
    
public:
    Good() = default;
};

Issue 4: Conditional Statements

int x;

// ❌ Using before initialization
if (condition) {
    x = 10;
}
std::cout << x;  // Garbage value if condition is false

// ✅ Proper initialization
int x = 0;
if (condition) {
    x = 10;
}

Best Practices for Initialization

// ✅ Always initialize
int x = 0;
int y{};
int* ptr = nullptr;

// ✅ Member initialization
class MyClass {
    int value = 0;
    double data = 0.0;
};

// ✅ Array initialization
int arr[5]{};

Practical Examples

Example 1: Accumulator Bug

// ❌ Common bug: uninitialized accumulator
int calculate_sum(const std::vector<int>& numbers) {
    int sum;  // Garbage value!
    for (int n : numbers) {
        sum += n;  // UB: sum is uninitialized
    }
    return sum;
}

// ✅ Correct: initialize accumulator
int calculate_sum(const std::vector<int>& numbers) {
    int sum = 0;  // Initialized
    for (int n : numbers) {
        sum += n;  // OK
    }
    return sum;
}

Example 2: Configuration Class

// ❌ Uninitialized members
class Config {
    int port;
    bool enabled;
    std::string host;
    
public:
    Config() {}  // port and enabled are garbage!
};

// ✅ Initialize all members
class Config {
    int port = 8080;
    bool enabled = false;
    std::string host = "localhost";
    
public:
    Config() = default;  // All members initialized
};

// ✅ Alternative: Constructor initialization
class Config {
    int port;
    bool enabled;
    std::string host;
    
public:
    Config() : port(8080), enabled(false), host("localhost") {}
};

Example 3: Buffer Initialization

// ❌ Uninitialized buffer (security risk)
void process_data() {
    char buffer[1024];  // Garbage values
    // If not fully written, may leak old memory contents
    send_to_network(buffer, sizeof(buffer));  // Security risk!
}

// ✅ Zero-initialize buffer
void process_data() {
    char buffer[1024]{};  // All zeros
    // Safe even if not fully written
    send_to_network(buffer, sizeof(buffer));
}

Example 4: Optional Values

// ❌ Using sentinel value with uninitialized variable
int find_max(const std::vector<int>& numbers) {
    int max;  // Garbage!
    bool found = false;
    
    for (int n : numbers) {
        if (!found || n > max) {
            max = n;
            found = true;
        }
    }
    return max;  // What if numbers is empty?
}

// ✅ Use std::optional
std::optional<int> find_max(const std::vector<int>& numbers) {
    if (numbers.empty()) return std::nullopt;
    
    int max = numbers[0];
    for (int n : numbers) {
        if (n > max) max = n;
    }
    return max;
}

Debugging Tips

Tip 1: Use Compiler Warnings

# Enable uninitialized variable warnings
g++ -Wall -Wextra -Wuninitialized -Werror main.cpp

# Clang
clang++ -Weverything -Werror main.cpp

Example warning:

warning: variable 'x' is uninitialized when used here [-Wuninitialized]
    std::cout << x;
                 ^

Tip 2: Use Static Analysis Tools

# Clang-Tidy
clang-tidy main.cpp -- -std=c++20

# Cppcheck
cppcheck --enable=all main.cpp

Tip 3: Use Sanitizers

# Memory Sanitizer (detects uninitialized reads)
clang++ -fsanitize=memory -fno-omit-frame-pointer main.cpp
./a.out

# Output:
# ==12345==WARNING: MemorySanitizer: use-of-uninitialized-value

Tip 4: Initialize in Debug Builds

#ifdef DEBUG
    int x = 0xDEADBEEF;  // Obvious garbage value for debugging
#else
    int x;  // Release build
#endif

Tip 5: Use Valgrind

# Detect uninitialized values
valgrind --track-origins=yes ./a.out

# Output:
# Conditional jump or move depends on uninitialised value(s)

FAQ

Q1: What is default initialization?

A: It occurs when no initializer is provided. This can result in garbage values.

Q2: Why is it dangerous?

A: It can lead to undefined behavior. Always initialize variables.

Q3: What about global variables?

A: They are zero-initialized by default.

Q4: What about classes?

A: The default constructor is called, but members are default-initialized.

Q5: How can I prevent issues?

A:

  • Initialize variables at the time of declaration
  • Use member initialization
  • Initialize in constructors
  • Enable compiler warnings (-Wall -Wextra -Wuninitialized)
  • Use static analysis tools (clang-tidy, cppcheck)
  • Use sanitizers in debug builds

Q6: What’s the difference between default and value initialization?

A:

  • Default initialization: int x; → garbage value
  • Value initialization: int x{}; or int x = int(); → zero

Q7: Are class members always default-initialized?

A: Yes, but the result depends on the type:

  • Primitive types: Garbage values (unless explicitly initialized)
  • Class types: Default constructor is called
  • Arrays: Each element is default-initialized

Q8: How do I detect uninitialized variables?

A: Use:

  • Compiler warnings: -Wuninitialized
  • Static analysis: clang-tidy, cppcheck
  • Runtime tools: Valgrind, MemorySanitizer
  • Code review: Look for variables declared without = or {}

Q9: Is it safe to use default initialization for performance?

A: Only if you immediately assign a value before any use. Otherwise, always initialize. The performance difference is negligible, and safety is more important.

// ✅ Safe: immediate assignment
int x;
x = compute_value();  // OK

// ❌ Risky: conditional assignment
int y;
if (condition) y = 10;
use(y);  // UB if condition is false

Q10: Where can I learn more about default initialization?

A:

Related Posts: Value Initialization, Zero Initialization, Aggregate Initialization, Undefined Behavior.

One-line summary: Default initialization can leave variables with garbage values; always initialize variables explicitly to avoid undefined behavior.

## 같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

- [C++ Value Initialization | "값 초기화" 가이드](/blog/cpp-value-initialization/)
- [C++ Zero Initialization | "0 초기화" 가이드](/blog/cpp-zero-initialization/)
- [C++ Aggregate Initialization | "집합체 초기화" 가이드](/blog/cpp-aggregate-initialization/)
- [C++ Undefined Behavior | "미정의 동작" 완벽 가이드](/blog/cpp-undefined-behavior/)

---

---

## 이 글에서 다루는 키워드 (관련 검색어)

C++, default-initialization, initialization, undefined, behavior 등으로 검색하시면 이 글이 도움이 됩니다.