Namespaces
Variants

Struct and union initialization

From cppreference.net

초기화 과정에서 struct 또는 union 타입의 객체를 초기화할 때, 초기화자는 반드시 비어 있지 않은 (C23 이전) 중괄호로 묶인 멤버 초기화자들의 쉼표로 구분된 목록이어야 합니다:

= { expression , ... } (1) (C99 이전)
= { designator (선택적) expression , ... } (2) (C99 이후)
= { } (3) (C23 이후)

여기서 designator . member 형태의 개별 멤버 지정자와 array designators 형태인 [ index ] 의 인접 또는 공백으로 구분된 시퀀스입니다.

명시적으로 초기화되지 않은 모든 멤버는 empty-initialized 됩니다.

목차

설명

union 를 초기화할 때, 초기화 목록은 반드시 하나의 멤버만 가져야 하며, 이는 지정된 초기화자를 사용하지 않는 한 (C99부터) union의 첫 번째 멤버를 초기화합니다.

union { int x; char c[4]; }
  u = {1},           // u.x를 활성화하여 값 1을 가짐
 u2 = { .c={'\1'} }; // u2.c를 활성화하여 값 {'\1','\0','\0','\0'}을 가짐

구조체를 초기화할 때, 목록의 첫 번째 초기화자는 첫 번째로 선언된 멤버를 초기화합니다 (지정자가 명시되지 않는 한) (C99부터) , 그리고 모든 후속 초기화자들은 지정자 없이 (C99부터) 이전 표현식으로 초기화된 멤버 다음에 선언된 구조체 멤버들을 초기화합니다.

struct point {double x,y,z;} p = {1.2, 1.3}; // p.x=1.2, p.y=1.3, p.z=0.0
div_t answer = {.quot = 2, .rem = -1 };      // div_t 내 요소들의 순서는 달라질 수 있음

지정자는 이후 초기화자가 지정자가 기술한 구조체 멤버를 초기화하도록 합니다. 초기화는 지정자가 기술한 멤버 다음에 선언된 요소부터 선언 순서대로 계속 진행됩니다.

struct {int sec,min,hour,day,mon,year;} z
   = {.day=31,12,2014,.sec=30,15,17}; // initializes z to {30,15,17,31,12,2014}
(C99부터)

초기화자의 수가 멤버 수보다 많으면 오류입니다.

중첩 초기화

구조체 또는 공용체의 멤버가 배열, 구조체 또는 공용체인 경우, 중괄호로 묶인 초기화 목록에서 해당 멤버에 대한 유효한 초기화자는 다음과 같이 중괄호를 생략할 수 있다는 점을 제외하고 해당 멤버에 유효한 모든 초기화자입니다:

중첩된 초기화자가 여는 중괄호로 시작하는 경우, 해당 닫는 중괄호까지의 전체 중첩 초기화자는 해당 멤버 객체를 초기화합니다. 각 왼쪽 여는 중괄호는 새로운 현재 객체 를 설정합니다. 현재 객체의 멤버들은 자연스러운 순서로 초기화됩니다 (지시자가 사용되지 않는 한) (C99부터) : 배열 요소는 첨자 순서로, 구조체 멤버는 선언 순서로, 모든 공용체의 첫 번째로 선언된 멤버만 초기화됩니다. 닫는 중괄호에 의해 명시적으로 초기화되지 않는 현재 객체 내의 하위 객체들은 빈 초기화 됩니다.

struct example {
    struct addr_t {
       uint32_t port;
    } addr;
    union {
       uint8_t a8[4];
       uint16_t a16[2];
    } in_u;
};
struct example ex = { // struct example의 초기화 리스트 시작
                     { // ex.addr의 초기화 리스트 시작
                        80 // 구조체의 유일한 멤버 초기화
                     }, // ex.addr의 초기화 리스트 끝
                     { // ex.in_u의 초기화 리스트 시작
                        {127,0,0,1} // union의 첫 번째 요소 초기화
                     } };

중첩된 초기화자가 여는 중괄호로 시작하지 않는 경우, 멤버 배열, 구조체 또는 공용체의 요소나 멤버를 설명하기 위해 목록에서 충분한 초기화자만 가져옵니다; 남은 초기화자는 다음 구조체 멤버를 초기화하는 데 사용됩니다:

struct example ex = {80, 127, 0, 0, 1}; // 80은 ex.addr.port를 초기화합니다
                                        // 127은 ex.in_u.a8[0]을 초기화합니다
                                        // 0은 ex.in_u.a8[1]을 초기화합니다
                                        // 0은 ex.in_u.a8[2]을 초기화합니다
                                        // 1은 ex.in_u.a8[3]을 초기화합니다

지정자가 중첩된 경우, 멤버에 대한 지정자는 이를 감싸는 struct/union/array에 대한 지정자를 따릅니다. 중괄호로 묶인 초기화 리스트 내부에서 가장 바깥쪽 지정자는 현재 객체(current object) 를 참조하며, 현재 객체 내에서 초기화할 하위 객체만을 선택합니다.

struct example ex2 = { // 현재 객체는 ex2, 지정자는 example의 멤버를 위한 것
                       .in_u.a8[0]=127, 0, 0, 1, .addr=80}; 
struct example ex3 = {80, .in_u={ // 현재 객체를 union ex.in_u로 변경
                           127,
                           .a8[2]=1 // 이 지정자는 in_u의 멤버를 참조
                      } };

어떤 하위 객체가 두 번 명시적으로 초기화되면(지정자를 사용할 때 발생할 수 있음), 리스트에서 나중에 나타나는 초기화자가 사용됩니다(이전 초기화자는 평가되지 않을 수 있음):

struct {int n;} s = {printf("a\n"), // 이는 출력되거나 건너뛸 수 있음
                     .n=printf("b\n")}; // 항상 출력됨

초기화되지 않은 모든 하위 객체는 암시적으로 초기화되지만, 하위 객체의 암시적 초기화는 초기화 리스트에서 이전에 나타난 동일한 하위 객체의 명시적 초기화를 절대 재정의하지 않습니다(올바른 출력을 보려면 clang을 선택하십시오):

#include <stdio.h>
typedef struct { int k; int l; int a[2]; } T;
typedef struct { int i;  T t; } S;
T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 };
 // x는 {42, 43, {18, 19} }로 초기화됨
int main(void)
{
    S l = { 1,          // l.i를 1로 초기화
           .t = x,      // l.t를 {42, 43, {18, 19} }로 초기화
           .t.l = 41,   // l.t를 {42, 41, {18, 19} }로 변경
           .t.a[1] = 17 // l.t를 {42, 41, {18, 17} }로 변경
          };
    printf("l.t.k is %d\n", l.t.k); // .t = x는 l.t.k를 42로 명시적으로 설정
                                    // .t.l = 41은 l.t.k를 암시적으로 0으로 만들었을 것
}

출력:

l.t.k is 42

그러나 초기화자가 여는 중괄호로 시작할 경우, 해당 현재 객체 는 완전히 재초기화되며 이전에 명시적으로 초기화된 모든 하위 객체들은 무시됩니다:

struct fred { char s[4]; int n; };
struct fred x[ ] = { { { "abc" }, 1 }, // x[0]을 { {'a','b','c','\0'}, 1 }로 초기화
                      [0].s[0] = 'q'   // x[0]을 { {'q','b','c','\0'}, 1 }로 변경
                   };
struct fred y[ ] = { { { "abc" }, 1 }, // y[0]을 { {'a','b','c','\0'}, 1 }로 초기화
                     [0] = { // 현재 객체는 이제 전체 y[0] 객체
                             .s[0] = 'q' 
                            } // y[0]을 { {'q','\0','\0','\0'}, 0 }로 대체
                    };
(C99부터)

참고 사항

초기화자 목록은 무시되는 후행 쉼표를 가질 수 있습니다.

struct {double x,y;} p = {1.0,
                          2.0, // 후행 쉼표 허용
                          };

C에서는 초기화자의 중괄호 목록이 비어 있을 수 없습니다 (C++에서는 빈 목록을 허용하며, 또한 C에서 struct 는 비어 있을 수 없음에 유의하십시오):

(until C23)

C++에서와 같이 C에서도 초기화자 목록이 비어 있을 수 있습니다:

(since C23)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // C23 이전까지 오류: 초기화 리스트가 비어 있을 수 없음
                        // C23 이후 OK: s.n이 0으로 초기화됨
struct {} s = {}; // 오류: 구조체는 비어 있을 수 없음

모든 저장 기간의 집합체를 초기화할 때 초기화 목록의 모든 표현식은 상수 표현식 이어야 합니다.

(C99까지)

다른 모든 초기화 와 마찬가지로, 정적 또는 스레드 지역 (C11부터) 저장 기간 의 집합체를 초기화할 때 초기화 목록의 모든 표현식은 상수 표현식 이어야 합니다:

static struct {char* p} s = {malloc(1)}; // error

모든 초기화에서 부분 표현식들의 평가 순서 는 비결정적으로 순서 지정됩니다 (단 C++11 이후 C++에서는 아님):

int n = 1;
struct {int x,y;} p = {n++, n++}; // unspecified, but well-defined behavior:
                                  // n is incremented twice in arbitrary order
                                  // p equal {1,2} and {2,1} are both valid
(C99부터)

예제

#include <stdio.h>
#include <time.h>
int main(void)
{
    char buff[70];
    // 지정 초기화자는 멤버 순서가 지정되지 않은 구조체의 사용을 단순화합니다
    // order of members is unspecified
    struct tm my_time = { .tm_year=2012-1900, .tm_mon=9, .tm_mday=9,
                          .tm_hour=8, .tm_min=10, .tm_sec=20 };
    strftime(buff, sizeof buff, "%A %c", &my_time);
    puts(buff);
}

가능한 출력:

Sunday Sun Oct  9 08:10:20 2012

참고문헌

  • C17 표준 (ISO/IEC 9899:2018):
  • 6.7.9/12-39 초기화 (p: 101-105)
  • C11 표준 (ISO/IEC 9899:2011):
  • 6.7.9/12-38 초기화 (p: 140-144)
  • C99 표준 (ISO/IEC 9899:1999):
  • 6.7.8/12-38 초기화 (p: 126-130)
  • C89/C90 표준 (ISO/IEC 9899:1990):
  • 6.5.7 초기화

참고 항목

C++ documentation for Aggregate initialization