본문으로 건너뛰기 C++ Default Initialization — Complete Guide

C++ Default Initialization — Complete Guide

C++ Default Initialization — Complete Guide

이 글의 핵심

Default initialization happens with no initializer. Local scalars may be indeterminate; reading them is undefined behavior. Differs from globals (zero init first) and from value initialization.

What is default initialization?

Declaring a variable without an initializer applies default initialization. For local scalars, the value can be indeterminate—reading it is undefined behavior. Prefer value initialization (T{}) or explicit assignment.

void func() {
    int x;        // ❌ Default-initialized: indeterminate (garbage)
    int y = 10;   // ✅ Explicit initialization
    int z{};      // ✅ Value-initialized: 0
}

Default initialization by context

ContextScalarsClass types
Local variablesIndeterminate ❌Default constructor
Static/globalZero first, then dynamic initZero first, then constructor
new TIndeterminate ❌Default constructor
new T()Zero-initialized ✅Default constructor
Class membersDepends on constructorDefault constructor

Scalars (automatic storage)

The danger zone

void dangerous() {
    int x;       // Indeterminate value
    double d;    // Indeterminate value
    int* ptr;    // Indeterminate value
    
    // ❌ All of these are undefined behavior:
    if (x > 0) { }           // UB
    std::cout << d << "\n";  // UB
    *ptr = 10;               // UB (likely crash)
}

The safe way

void safe() {
    int x = 0;           // ✅ Explicit
    double d{};          // ✅ Value-initialized
    int* ptr = nullptr;  // ✅ Explicit
    
    // Now safe to use
    if (x > 0) { }
    std::cout << d << "\n";
    if (ptr != nullptr) {
        *ptr = 10;
    }
}

Real-world examples of bugs

Bug 1: Uninitialized accumulator

// ❌ Bug: sum is indeterminate
int calculateSum(const std::vector<int>& numbers) {
    int sum;  // Garbage value!
    for (int num : numbers) {
        sum += num;  // UB: using indeterminate value
    }
    return sum;
}
// ✅ Fix
int calculateSum(const std::vector<int>& numbers) {
    int sum = 0;  // Properly initialized
    for (int num : numbers) {
        sum += num;
    }
    return sum;
}

Bug 2: Uninitialized pointer

// ❌ Bug: ptr points to random memory
void processData() {
    int* ptr;  // Indeterminate!
    
    if (someCondition) {
        ptr = new int(42);
    }
    
    // If someCondition was false, ptr is still indeterminate
    *ptr = 10;  // UB: may crash or corrupt memory
    delete ptr;  // UB: deleting invalid pointer
}
// ✅ Fix
void processData() {
    int* ptr = nullptr;  // Initialized to null
    
    if (someCondition) {
        ptr = new int(42);
    }
    
    if (ptr != nullptr) {
        *ptr = 10;
        delete ptr;
    }
}

Bug 3: Uninitialized flag

// ❌ Bug: success flag not initialized
bool processFile(const std::string& filename) {
    bool success;  // Indeterminate!
    
    if (fileExists(filename)) {
        success = doProcessing(filename);
    }
    
    // If file doesn't exist, success is indeterminate
    return success;  // UB
}
// ✅ Fix
bool processFile(const std::string& filename) {
    bool success = false;  // Default to failure
    
    if (fileExists(filename)) {
        success = doProcessing(filename);
    }
    
    return success;
}

Class types and default initialization

Classes with default constructors

class Widget {
    int value_;
public:
    Widget() : value_(0) {}  // Default constructor
};
void func() {
    Widget w;  // Default-initialized: calls Widget()
    // w.value_ is 0
}

Classes without default constructors

class Point {
    int x_, y_;
public:
    Point(int x, int y) : x_(x), y_(y) {}
    // No default constructor!
};
void func() {
    // Point p;  // ❌ Error: no default constructor
    Point p(0, 0);  // ✅ Must provide arguments
}

Class members

Uninitialized members

class Bad {
    int value_;  // ❌ Not initialized in constructor
    
public:
    Bad() {}  // value_ is indeterminate!
};
// ✅ Fix 1: Member initializer list
class Good1 {
    int value_;
    
public:
    Good1() : value_(0) {}
};
// ✅ Fix 2: Default member initializer (C++11)
class Good2 {
    int value_ = 0;
    
public:
    Good2() = default;
};

Partially initialized objects

class Dangerous {
    int x_;
    int y_;
    
public:
    Dangerous(int x) : x_(x) {}  // ❌ y_ is indeterminate!
};
// ✅ Fix
class Safe {
    int x_;
    int y_ = 0;  // Default member initializer
    
public:
    Safe(int x) : x_(x) {}  // y_ gets default value
};

Arrays

void func() {
    int arr1[5];     // ❌ All elements indeterminate
    int arr2[5]{};   // ✅ All elements zero-initialized
    int arr3[5]{1};  // ✅ {1, 0, 0, 0, 0}
    
    // ❌ Reading uninitialized array
    for (int i = 0; i < 5; ++i) {
        std::cout << arr1[i] << "\n";  // UB
    }
}

Dynamic allocation

The following example demonstrates the concept in cpp:

// Default initialization
int* p1 = new int;      // ❌ Indeterminate value
int* p2 = new int();    // ✅ Zero-initialized
int* p3 = new int{};    // ✅ Zero-initialized
int* p4 = new int(42);  // ✅ Initialized to 42
// Arrays
int* arr1 = new int[5];    // ❌ All indeterminate
int* arr2 = new int[5]();  // ✅ All zero-initialized
int* arr3 = new int[5]{}; // ✅ All zero-initialized
// Cleanup
delete p1;
delete[] arr1;

Common mistakes

Mistake 1: Conditional initialization

// ❌ Bug
void process(bool flag) {
    int result;
    
    if (flag) {
        result = 42;
    }
    
    std::cout << result << "\n";  // UB if flag is false
}
// ✅ Fix
void process(bool flag) {
    int result = 0;  // Default value
    
    if (flag) {
        result = 42;
    }
    
    std::cout << result << "\n";
}

Mistake 2: Using memset on non-trivial types

struct Data {
    std::string name;
    int count;
};
// ❌ WRONG: Destroys std::string
Data d;
memset(&d, 0, sizeof(d));
// ✅ Correct
Data d{};  // Value-initialize all members

Mistake 3: Assuming zero

// ❌ Wrong assumption
void increment() {
    static int counter;  // ✅ Zero-initialized (static)
    int local;           // ❌ Indeterminate (automatic)
    
    ++counter;  // OK: counter starts at 0
    ++local;    // UB: local is indeterminate
}

Detecting uninitialized variables

Compiler warnings

# GCC/Clang
g++ -Wall -Wextra -Wuninitialized -O2 file.cpp
# MSVC
cl /W4 /analyze file.cpp

Runtime detection with sanitizers

# Memory Sanitizer (Clang)
clang++ -fsanitize=memory -g file.cpp
./a.out
# Valgrind
g++ -g file.cpp
valgrind --track-origins=yes ./a.out

Static analysis

// Clang-Tidy
clang-tidy file.cpp -checks='cppcoreguidelines-init-variables'

Best practices

1. Always initialize variables

// ✅ Good
int count = 0;
double value = 0.0;
bool flag = false;
int* ptr = nullptr;
std::string name;  // Empty string (constructor)

2. Use value initialization for containers

std::vector<int> vec(100);    // ❌ Elements are indeterminate
std::vector<int> vec(100, 0); // ✅ All elements are 0

3. Initialize in declaration

// ❌ Separate declaration and initialization
int x;
// ... many lines ...
x = 10;
// ✅ Initialize immediately
int x = 10;

4. Use default member initializers

class Config {
    int timeout = 30;           // ✅ Default value
    bool enabled = true;        // ✅ Default value
    std::string name = "default"; // ✅ Default value
    
public:
    Config() = default;
};

Performance considerations

Myth: “Initializing to zero wastes performance” Reality: Modern compilers optimize away unnecessary initialization:

// No performance difference with optimization
int x = 0;
x = computeValue();  // Compiler elides the zero initialization

Benchmark (GCC -O2, 1M iterations):

CodeTime
int x; x = f();2.1ms
int x = 0; x = f();2.1ms
Identical performance!

Compiler support

CompilerUninitialized warningsSanitizers
GCC4.0+4.8+ (ASan)
Clang3.0+3.1+ (MSan)
MSVCAll versions2019+ (ASan)

Keywords

C++, default initialization, undefined behavior, uninitialized variable, indeterminate value, initialization, memory safety


자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. Default initialization happens with no initializer. Local scalars may be indeterminate; reading them is undefined behavi… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


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

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


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

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