영 초기화

영 초기화 (Zero initialization)

변수를 선언만 하고 초기값을 안 넣었을 때, 전역 변수는 왜 자동으로 0이 될까요? 그 규칙이 바로 **영 초기화(zero initialization)**예요. 객체의 초기 값을 0으로 설정해 주는 초기화 방식인데, 언어에 전용 문법은 없고 다른 초기화들이 내부적으로 수행하는 역할이에요. 이 페이지에서 영 초기화가 정확히 언제 일어나고, 타입별로 어떤 값을 넣는지 정리해 볼게요.

출처: cppreference

본문

문법

주의할 점: 아래는 영 초기화 자체의 문법이 아니에요. 영 초기화는 언어에 전용 문법이 없어요. 아래는 실제로는 다른 종류의 초기화들이지만, 그 초기화가 (가능하면) 영 초기화를 수행하는 예시 문법들이에요.

static T object;                    (1)

T();                                (2)
T t = {};                           (2)
T {};                               (2) (C++11부터)

CharT array[n] = "short-sequence";  (3)

설명

영 초기화는 다음 상황에서 수행돼요.

  1. 상수 초기화(constant initialization) 대상이 아닌, 정적(static) 또는 스레드 지역(thread-local)(C++11부터) 저장 기간을 가진 모든 이름 있는 변수에 대해, 다른 어떤 초기화보다 먼저 수행돼요.
  2. 비클래스 타입과, 생성자가 없는 값 초기화된 클래스 타입 멤버에 대한 값 초기화(value-initialization) 시퀀스의 일부로 수행돼요. 초기화자가 제공되지 않는 집합체(aggregate) 요소의 값 초기화도 포함돼요.
  3. 어떤 문자 타입의 배열을 너무 짧은 문자열 리터럴로 초기화할 때, 배열의 나머지 부분이 영 초기화돼요.

영 초기화의 효과는 다음과 같아요.

  • Tstd::meta::info라면, 객체는 널 반사 값(null reflection value)으로 초기화돼요. (C++26부터)
  • T가 스칼라 타입(scalar type)이라면, 객체는 정수 리터럴 0(영)을 T로 명시적으로 변환해 얻은 값으로 초기화돼요.
  • T가 비공용체(non-union) 클래스 타입이라면:
    • 모든 패딩 비트(padding bit)가 0비트로 초기화돼요.
    • 각 비정적 데이터 멤버가 영 초기화돼요.
    • 각 비가상 기반 클래스 하위 객체가 영 초기화돼요.
    • 객체가 기반 클래스 하위 객체가 아니라면, 각 가상 기반 클래스 하위 객체도 영 초기화돼요.
  • T가 공용체(union) 타입이라면:
    • 모든 패딩 비트가 0비트로 초기화돼요.
    • 객체의 첫 번째 비정적 이름 있는 데이터 멤버가 영 초기화돼요.
  • T가 배열 타입이라면, 각 요소가 영 초기화돼요.
  • T가 참조 타입이라면, 아무것도 하지 않아요.

참고 (Notes)

비지역 초기화(non-local initialization)에서 설명했듯이, 상수 초기화되지 않는 정적·스레드 지역(C++11부터) 변수는 다른 어떤 초기화가 일어나기 전에 영 초기화돼요.

비클래스 비지역 변수의 정의에 초기화자가 없다면, 기본 초기화(default initialization)는 아무것도 하지 않아요. 그래서 더 이른 영 초기화의 결과가 그대로 남아요.

영 초기화된 포인터는 그 타입의 널 포인터 값이에요. 널 포인터의 값이 정수 0이 아니어도 마찬가지로 그 값이 돼요.

예시 (Example)

#include <iostream>
#include <string>

struct A
{
    int a, b, c;
};

double f[3];   // 세 개의 0.0으로 영 초기화

int* p;        // 널 포인터 값으로 영 초기화
               // (값이 정수 0이 아니어도 마찬가지)

std::string s; // 불확정 값으로 영 초기화된 다음,
               // std::string 기본 생성자에 의해 ""으로 기본 초기화

int main(int argc, char*[])
{
    delete p; // 널 포인터 삭제는 안전

    static int n = argc; // 0으로 영 초기화된 다음 argc로 복사 초기화
    std::cout << "n = " << n << '\n';

    A a = A(); // A a{}; 또는 A a = {}; 와 같은 효과
    std::cout << "a = {" << a.a << ' ' << a.b << ' ' << a.c << "}\n";
}

출력:

n = 1
a = {0 0 0}

결함 보고 (Defect reports)

다음 동작 변경 결함 보고는 이전에 발표된 C++ 표준에 소급 적용됐어요.

DR 적용 대상 발표된 동작 올바른 동작
CWG 277 C++98 포인터가 값 0의 비상수 표현식(널 포인터 상수가 아님)으로 초기화될 수 있었음 값 0의 정수 상수 표현식으로 초기화해야 함
CWG 694 C++98 클래스 타입의 영 초기화가 패딩을 무시했음 패딩이 0비트로 초기화됨
CWG 903 C++98 스칼라 타입의 영 초기화가 값 0의 정수 상수 표현식에서 변환된 값으로 초기값을 설정했음 객체는 정수 리터럴 0에서 변환된 값으로 초기화됨
CWG 2026 C++98 영 초기화가 상수 초기화보다도 항상 먼저 일어나도록 규정됐음 상수 초기화가 적용되면 영 초기화하지 않음
CWG 2196 C++98 클래스 타입의 영 초기화가 기반 클래스 하위 객체를 무시했음 그것들도 영 초기화됨
CWG 2253 C++98 영 초기화가 이름 없는 비트 필드에 적용되는지 불명확했음 적용됨(모든 패딩 비트가 0비트로 초기화됨)

더 알아보기