본문으로 건너뛰기 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


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Zero initialization sets storage to zero.

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the C++ series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers C++, zero initialization, static initialization, globals, BSS.