객체를 초기화 할 때 {0}은 무엇을 의미합니까?


252

{0}객체를 초기화하는 데 언제 사용됩니까? {0}어디에서나 참조를 찾을 수 없으며 중괄호 때문에 Google 검색이 도움이되지 않습니다.

예제 코드 :

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if(ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if(wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

그렇지 않으면 위의 코드가 런타임에 충돌합니다.

답변:


302

여기서 일어나고있는 것을 집계 초기화 라고 합니다. 다음은 ISO 사양 섹션 8.5.1의 집계에 대한 (약식) 정의입니다.

집합은 사용자가 선언 한 생성자, 개인 또는 보호 된 비 정적 데이터 멤버, 기본 클래스 및 가상 함수가없는 배열 또는 클래스입니다.

이제 {0}이와 같은 집계를 초기화하는 데 사용 하는 것은 기본적으로 0전체에 대한 트릭 입니다. 집계 초기화 사용할 때 모든 멤버를 지정할 필요가 없으며 스펙에서 지정되지 않은 모든 멤버를 기본적으로 초기화해야 0하므로 단순 유형으로 설정됩니다 .

스펙의 관련 인용문은 다음과 같습니다.

집계에 멤버가있는 것보다 목록에 이니셜 라이저가 적은 경우 명시 적으로 초기화되지 않은 각 멤버는 기본 초기화됩니다. 예:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

초기화 ss.a1, ss.b"asdf",와 ss.c형태의 식의 값에 int(),이다 0.

이 주제에 대한 전체 사양은 여기 에서 찾을 수 있습니다.


15
탁월한 반응. {0}으로 집계를 초기화하는 것은 단순히 {}로 집계를 초기화하는 것과 같습니다. 아마도 전자는 내장 유형이 제로화된다는 것을 더 분명하게 만듭니다.
제임스 홉킨

7
일부 컴파일러는 {}을 질식
시켰기

10
C ++에서 첫 번째 멤버를 0으로 구성 할 수 없으면 {0}이 작동하지 않습니다. 예 : struct A {B b; int i; 숯불 c; }; 구조체 B {B (); B (문자열); }; A a = {}; //이 문장은 'A a = {0}'으로 다시 쓸 수 없습니다.
Aaron

25
@Branan, 그것은 C "{}"에서 유효하지 않기 때문입니다. C ++에서는 그렇습니다. @ don.neufeld, 이것은 C ++ 03 (default-initialized-> value-initialized)으로 변경되었습니다. 이 인용문은 C ++ 98 표준을 인용합니다.
Johannes Schaub-litb

3
그렇다면 이것이 VC ++에서 ZeroMemory ()를 사용할 필요를 대체합니까?
Ray

89

한 가지 알아야 할 것은이 기술은 패딩 바이트를 0으로 설정하지 않는다는 것입니다. 예를 들면 다음과 같습니다.

struct foo
{
    char c;
    int  i;
};

foo a = {0};

다음과 동일하지 않습니다 :

foo a;
memset(&a,0,sizeof(a));

첫 번째 경우, c와 i 사이의 pad 바이트는 초기화되지 않습니다. 왜 신경 쓰겠 어? 이 데이터를 디스크에 저장하거나 네트워크 등을 통해 전송하는 경우 보안 문제가 발생할 수 있습니다.


13
물론 다른 프로세서 / 컴파일러에서 다른 파일 인코딩을 생성 할 수있는 'write (f, & a, sizeof (a))'인 경우 보안 문제 일뿐입니다. 형식이없는 출력은 memset없이 안전합니다.
Aaron

3
또한 네트워크를 통해 물건을 보내는 경우 항상 정렬을 포장하도록 설정합니다. 그렇게하면 가능한 한 적은 패딩 바이트를 얻을 수 있습니다.
Mark Kegel

18
스펙에서 패딩을 초기화 할 필요는 없지만, 제정신 컴파일러는 "어라운드"를 초기화하는 데 시간이 걸리기 때문에 괜찮습니다.
Tomas

4
"is not"이라는 단어를 사용하는 데 문제가 있습니다. 패딩 바이트는 구현 정의입니다. 컴파일러는 자유로이 foo a = {0}을 memset (& a, 0, sizeof (a))로 바꿀 수 있습니다. 패딩 바이트를 "건너 뛰지"않아도되고 foo.c 및 foo.i 설정하면됩니다. (잠재적) 보안 버그 tho +1
SecurityMatt

3
@Leushenko 점 19는 "명시 적으로 초기화되지 않은 모든 하위 객체"라고 말하므로 21 점 (당신이 인용 한)이 너절한 것으로 기댈 것입니다. 스펙이 마지막 이니셜 라이저가 시작될 때까지 패딩이 초기화되지 않은 것을 허용 한 경우, 특히 지정된 이니셜 라이저가 사용될 때 이니셜 라이저가 순서가 잘못 나타날 수 있다는 점을 고려하여 패딩을 0으로 설정 해야하는 경우 실제로 기괴합니다.
MM

20

빈 집계 초기화 프로그램도 작동합니다.

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};

11

ShellExecuteEx()충돌 하는지에 대한 대답으로 : SHELLEXECUTEINFO"sexi"구조체에는 많은 멤버가 있으며 일부만 초기화하고 있습니다.

예를 들어, 멤버 sexi.lpDirectory가 아무 데나 가리킬 수 있지만 ShellExecuteEx()여전히 사용하려고 시도하므로 메모리 액세스 위반이 발생합니다.

라인을 포함시킬 때 :

SHELLEXECUTEINFO sexi = {0};

나머지 구조 설정 전에 관심있는 특정 멤버를 초기화하기 전에 모든 구조 멤버 를 제로화하도록 컴파일러에 지시합니다 . 제로인 ShellExecuteEx()경우 sexi.lpDirectory무시해야한다는 것을 알고 있습니다.


7

또한 문자열을 초기화하는 데 사용합니다.

char mytext[100] = {0};

5
물론 mytext를 문자열로 사용한다면 char mytext [100]; mytext [0] = '\ 0'; 빈 문자열을주는 것과 동일한 효과가 있지만 구현이 첫 번째 바이트를 0으로 만듭니다.
크리스 영

@Chris : 부분적인 객체 초기화를위한 문법이 있었으면 좋겠다. x를 "선언하고 초기화"할 수 있다면, y, z와 마찬가지로 x, y 및 z를 선언 한 다음 x, y 및 z를 초기화하는 것보다 훨씬 좋지만, 100 바이트 만 초기화하면 실제로 초기화가 필요한 것은 다소 낭비 적입니다.
supercat

7

{0}C 및 C ++ 모두에서 모든 (완전한 객체) 유형에 대한 유효한 초기화 프로그램입니다. 객체를 0 으로 초기화하는 데 사용되는 일반적인 관용구입니다 (이것이 무엇을 의미하는지 읽으십시오).

스칼라 유형 (산술 및 포인터 유형)의 경우 중괄호는 필요하지 않지만 명시 적으로 허용됩니다. ISO C 표준 의 N1570 초안 인용 , 섹션 6.7.9 :

스칼라의 이니셜 라이저는 선택적으로 중괄호로 묶인 단일 표현식이어야합니다.

객체를 0으로 초기화합니다 ( 0정수, 0.0부동 소수점, 포인터의 널 포인터).

스칼라가 아닌 유형 (구조, 배열, 공용체) 의 경우 객체 {0}첫 번째 요소가 0으로 초기화되도록 지정합니다. 구조, 구조의 배열 등을 포함하는 구조의 경우에는 재귀 적으로 적용되므로 첫 번째 스칼라 요소는 유형에 따라 0으로 설정됩니다. 이니셜 라이저에서와 같이 지정되지 않은 요소는 0으로 설정됩니다.

중간 괄호 ( {, })는 생략 할 수 있습니다. 예를 들어 둘 다 유효하고 동등합니다.

int arr[2][2] = { { 1, 2 }, {3, 4} };

int arr[2][2] = { 1, 2, 3, 4 };

그렇기 때문에 예를 들어 { { 0 } }첫 번째 요소가 스칼라가 아닌 유형에 대해 쓸 필요가 없습니다 .

그래서 이거:

some_type obj = { 0 };

obj0 으로 초기화하는 짧은 방법으로 , 각 스칼라 하위 오브젝트 obj0정수인 0.0경우, 부동 소수점 인 경우, 포인터 인 경우 널 포인터 인 경우로 설정됩니다.

규칙은 C ++에서 비슷합니다.

특정 경우에 값을 할당하는 sexi.cbSizeSHELLEXECUTEINFO의 이유로 구조체 또는 클래스 유형 (또는 아마도 조합이지만 가능하지는 않음)이 분명하므로 모든 것이 적용되는 것은 아니지만 { 0 }공통적입니다. 보다 일반적인 상황에서 사용할 수있는 관용구.

이것은 객체 표현을 all-bits-zero로 설정 하는 데 사용 하는 것과 (필수적으로) 동일 하지 않습니다memset . 부동 소수점 0.0이나 널 포인터가 모두 0으로 표시 될 { 0 }필요는 없으며 초기화 프로그램이 반드시 패딩 바이트를 특정 값으로 설정하지 않아도됩니다. 그러나 대부분의 시스템에서 동일한 효과가있을 수 있습니다.


1
C ++에서 {0}생성자를 허용하지 않는 객체의 유효한 초기화 프로그램이 아닙니다 0. 첫 요소가 같은 집계 (또는 요소가없는 집계)
MM

3

c / c ++에서 일했지만 IIRC에서는 배열에 동일한 바로 가기를 사용할 수 있기 때문에 시간이 오래 걸렸습니다.


2

난 항상 궁금해, 당신이 같은 것을 사용해야

struct foo bar = { 0 };

다음은 설명 할 테스트 사례입니다.

check.c

struct f {
    int x;
    char a;
} my_zero_struct;

int main(void)
{
    return my_zero_struct.x;
}

컴파일 gcc -O2 -o check check.c하고 심볼 테이블을 사용하여 출력합니다 readelf -s check | sort -k 2(x64 시스템의 우분투 12.04.2의 gcc 4.6.3). 발췌 :

59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end

여기서 중요한 부분 my_zero_struct은 이후 __bss_start입니다. C 프로그램에서 ".bss라고"부분은 0으로 설정되어 메모리 섹션 전에 main 보기라고 .bss라고에 위키를 .

위 코드를 다음과 같이 변경하면

} my_zero_struct = { 0 };

그런 다음 결과 "체크"실행 파일은 적어도 우분투 12.04.2의 gcc 4.6.3 컴파일러와 정확히 동일합니다. 는 my_zero_struct여전히 .bss이전 섹션에 따라서 자동으로 0으로 초기화 될 것 main이라고합니다.

memset"전체"구조를 초기화 할 수도 있다는 의견에서 힌트 .bss는 "전체"구조가 0으로 설정되어 있음을 의미 하기 때문에 섹션이 완전히 지워 지기 때문에 개선되지 않습니다 .

그것은 수있는 C 언어 표준이 중 하나를 언급하지 않는,하지만 현실 세계의 C 컴파일러에 나는 다른 행동을 본 적이 없다.


전역 및 정적 변수는 항상 기본적으로 0 또는 기본 ctor로 초기화됩니다. 그러나 f의 인스턴스를 로컬로 선언하면 다른 결과를 얻을 수 있습니다.
Logman

0

전체 구조를 빈 / 제로 / 널 값으로 초기화하는 것은 합성 설탕입니다.

긴 버전

SHELLEXECUTEINFO sexi;
sexi.cbSize = 0;
sexi.fMask = 0;
sexi.hwnd = NULL;
sexi.lpVerb = NULL;
sexi.lpFile = NULL;
sexi.lpParameters = NULL;
sexi.lpDirectory = NULL;
sexi.nShow = nShow;
sexi.hInstApp = 0;
sexi.lpIDList = NULL;
sexi.lpClass = NULL;
sexi.hkeyClass = 0;
sexi.dwHotKey = 0;
sexi.hMonitor = 0;
sexi.hProcess = 0;

짧은 버전

SHELLEXECUTEINFO sexi = {0};

그렇게 쉽지 않았습니까?

다음과 같은 이유로도 좋습니다.

  • 모든 멤버를 찾아서 초기화 할 필요는 없습니다.
  • 나중에 추가 할 때 새 멤버를 초기화하지 않아도된다고 걱정할 필요가 없습니다.
  • 당신은 전화 할 필요가 없습니다ZeroMemory

-5

{0}은 (는) 해당 요소를 0으로 포함 하는 익명 배열 입니다.

이것은 배열의 하나 또는 모든 요소를 ​​0 으로 초기화 하는 데 사용됩니다 .

예를 들어, int arr [8] = {0};

이 경우 arr의 모든 요소는 0으로 초기화됩니다.


4
{0}익명 배열이 아닙니다. 심지어 표현이 아닙니다. 이니셜 라이저입니다.
Keith Thompson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.