본문으로 건너뛰기
Previous
Next
C++ Zero Initialization | The 'All Bits Zero' First Step

C++ Zero Initialization | The 'All Bits Zero' First Step

C++ Zero Initialization | The 'All Bits Zero' First Step

이 글의 핵심

Zero initialization sets storage to zero. Static and thread-local objects get it before dynamic init; locals do not unless you value-initialize. Relation to value and default init.

What is zero initialization?

Zero initialization sets the object’s initial value to zero (or null pointer / false as appropriate). Static storage duration objects get it automatically before dynamic initialization. For automatic locals, use T{} or value initialization—do not wait for zero init.

Here is the 0 implementation:

int global;        // 0 (static duration → zero-initialized)

static int s;      // 0 (static → zero-initialized)

void func() {
    static int x;  // 0 (static → zero-initialized)
    int y;         // ❌ indeterminate (automatic → NOT zero-initialized)
    int z{};       // ✅ 0 (value-initialized)
}

Zero initialization results by type

TypeZero-init resultExample
Integer types0int x; → 0 (if static)
Floating-point+0.0double d; → 0.0 (if static)
boolfalsebool b; → false (if static)
Pointersnull pointer valueint* p; → nullptr (if static)
ArraysEach element zero-initializedint arr[5]; → {0,0,0,0,0} (if static)
Class typesRecursively zero-init membersThen constructors run

Who gets zero initialization automatically?

Static storage duration

// Namespace scope
int globalVar;              // Zero-initialized before main()

// Static local
void func() {
    static int counter;     // Zero-initialized on first call
}

// Static member
class MyClass {
    static int count;       // Zero-initialized before main()
};
int MyClass::count;

Thread-local storage

thread_local int threadVar;  // Zero-initialized per thread

void func() {
    thread_local int localThreadVar;  // Zero-initialized per thread
}

Initialization phases for static objects

Static initialization happens in stages:

  1. Zero initialization — All bits set to zero
  2. Constant initializationconstexpr / compile-time constants
  3. Dynamic initialization — Runtime constructors before main()

Here is the compute implementation:

int g1;                    // 1. Zero-init → 0
constexpr int g2 = 50;     // 2. Constant-init → 50
int g3 = compute();        // 3. Dynamic-init (runtime)

int compute() {
    return 42;
}

Locals vs static: Critical difference

#include <iostream>

int g;  // Static duration → zero-initialized

void test() {
    static int s;  // Static duration → zero-initialized
    int a;         // Automatic → NOT zero-initialized (indeterminate!)
    int b{};       // Automatic → value-initialized (zero)
    
    std::cout << "g = " << g << "\n";  // 0
    std::cout << "s = " << s << "\n";  // 0
    // std::cout << "a = " << a << "\n";  // ❌ UB: reading indeterminate value
    std::cout << "b = " << b << "\n";  // 0
}

Common mistake: Assuming locals are “almost zero” like globals—they are not!


Real-world examples

1. Global counters

// Automatically zero-initialized
int requestCount;
int errorCount;

void handleRequest() {
    ++requestCount;
    
    if (error) {
        ++errorCount;
    }
}

// Safe to use without explicit initialization

2. Static lookup tables

Here is the countCharacters implementation:

// Zero-initialized before main()
int frequencyTable[256];

void countCharacters(const std::string& text) {
    for (char c : text) {
        ++frequencyTable[static_cast<unsigned char>(c)];
    }
}

3. Singleton pattern

class Logger {
    static Logger* instance;
    
public:
    static Logger& getInstance() {
        if (!instance) {  // instance is nullptr (zero-initialized)
            instance = new Logger();
        }
        return *instance;
    }
};

Logger* Logger::instance;  // Zero-initialized to nullptr

Classes and zero initialization

Trivial types

struct Point {
    int x;
    int y;
};

Point globalPoint;  // Zero-initialized: {0, 0}

void func() {
    Point localPoint;   // ❌ NOT zero-initialized (indeterminate)
    Point valuePoint{}; // ✅ Zero-initialized: {0, 0}
}

Non-trivial types

class Widget {
    int value_;
public:
    Widget() : value_(42) {}  // Constructor runs after zero-init
};

Widget globalWidget;  // 1. Zero-init, 2. Constructor → value_ = 42

void func() {
    static Widget staticWidget;  // 1. Zero-init, 2. Constructor
    Widget localWidget;          // Just constructor (no zero-init first)
}

Common mistakes

Mistake 1: Expecting locals to be zero

void badFunction() {
    int sum;  // ❌ NOT zero! Indeterminate value
    
    for (int i = 0; i < 10; ++i) {
        sum += i;  // ❌ UB: using indeterminate value
    }
}

// ✅ Fix
void goodFunction() {
    int sum = 0;  // Explicit initialization
    
    for (int i = 0; i < 10; ++i) {
        sum += i;
    }
}

Mistake 2: Relying on zero for non-trivial types

struct Data {
    std::string name;  // Non-trivial
    int count;
};

Data globalData;  // Zero-init doesn't make name = "" directly
                  // name's constructor runs after zero-init

void func() {
    Data localData;   // ❌ count is indeterminate!
    Data valueData{}; // ✅ Both members properly initialized
}

Mistake 3: Using memset on non-trivial types

struct Widget {
    std::string name;
    int value;
};

Widget w;

// ❌ WRONG: Destroys std::string's internal state
memset(&w, 0, sizeof(w));

// ✅ Correct: Use value initialization or assignment
Widget w{};  // or
w = Widget{};

Zero initialization vs value initialization

InitializationSyntaxStatic objectsLocal objects
Zeroint x; (static)✅ Zero❌ Indeterminate
Valueint x{};✅ Zero✅ Zero
Defaultint x; (local)✅ Zero first❌ Indeterminate

Here is the func implementation:

// Static storage
int g1;      // Zero-init → 0
int g2{};    // Value-init (includes zero) → 0

void func() {
    // Automatic storage
    int a;   // Default-init → indeterminate ❌
    int b{}; // Value-init → 0 ✅
}

BSS segment

BSS (Block Started by Symbol): Uninitialized data segment where zero-initialized globals live.

Here is the main implementation:

int globalArray[1000000];  // 4MB in BSS (not in executable file)

int main() {
    // globalArray is zero-initialized at program start
    // No 4MB added to executable size!
}

Benefit: Large zero-initialized arrays don’t bloat executable size.


Static initialization order fiasco

Here is the computeValue implementation:

// file1.cpp
int computeValue() { return 42; }
int value1 = computeValue();  // Dynamic init

// file2.cpp
extern int value1;
int value2 = value1 * 2;  // ⚠️ Order undefined across TUs!

// If value2 initializes first, value1 is still zero!

Solution: Use function-local statics or constexpr:

The following example demonstrates the concept in cpp:

// file1.cpp
constexpr int value1 = 42;  // Constant init (safe)

// Or
int& getValue1() {
    static int value = 42;  // Initialized on first call
    return value;
}

Performance implications

Zero cost: Zero initialization of static objects happens before main() and is typically free (BSS segment).

Here is the func implementation:

// No runtime cost
int largeArray[1000000];  // BSS segment, zero at load time

// Runtime cost
void func() {
    int largeArray[1000000]{};  // Stack allocation + zero-fill
}

Debugging zero initialization

Check if variable is zero-initialized

#include <iostream>
#include <type_traits>

template<typename T>
void checkInit() {
    if constexpr (std::is_trivially_default_constructible_v<T>) {
        std::cout << "Trivial: static objects zero-initialized\n";
    } else {
        std::cout << "Non-trivial: constructor runs\n";
    }
}

checkInit<int>();         // Trivial
checkInit<std::string>(); // Non-trivial

Compiler support

CompilerZero initializationBSS optimization
GCCAll versionsYes
ClangAll versionsYes
MSVCAll versionsYes

Keywords

C++, zero initialization, static initialization, BSS, globals, static storage, initialization, memory


자주 묻는 질문 (FAQ)

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

A. Zero initialization sets storage to zero. Static and thread-local objects get it before dynamic init; locals do not unle… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

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

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

Q. 더 깊이 공부하려면?

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


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

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


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

C++, zero initialization, static initialization, globals, BSS 등으로 검색하시면 이 글이 도움이 됩니다.