Namespaces
Variants

Struct and union initialization

From cppreference.net

При инициализации объекта типа struct или union инициализатор должен быть непустым, (до C23) заключенным в фигурные скобки, разделенным запятыми списком инициализаторов для членов:

= { выражение , ... } (1) (до C99)
= { дескриптор (опционально) выражение , ... } (2) (начиная с C99)
= { } (3) (начиная с C23)

где designator представляет собой последовательность (разделенную пробелами или смежную) отдельных обозначений членов вида . member и array designators вида [ index ] .

Все члены, которые не инициализированы явно, пусто-инициализированы .

Содержание

Объяснение

При инициализации union список инициализаторов должен содержать только один элемент, который инициализирует первый член объединения если не используется designated initializer (since C99) .

union { int x; char c[4]; }
  u = {1},           // делает u.x активным со значением 1
 u2 = { .c={'\1'} }; // делает u2.c активным со значением {'\1','\0','\0','\0'}

При инициализации struct первый инициализатор в списке инициализирует первый объявленный член (если не указан дезигнатор) (начиная с C99) , а все последующие инициализаторы без дезигнаторов (начиная с C99) инициализируют члены struct, объявленные после члена, инициализированного предыдущим выражением.

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}
(since 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} // инициализирует первый элемент объединения
                     } };

Если вложенный инициализатор не начинается с открывающей фигурной скобки, из списка берётся только достаточное количество инициализаторов для элементов или членов массива, структуры или объединения; оставшиеся инициализаторы используются для инициализации следующего члена структуры:

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 example ex2 = { // current object is ex2, designators are for members of example
                       .in_u.a8[0]=127, 0, 0, 1, .addr=80}; 
struct example ex3 = {80, .in_u={ // changes current object to the union ex.in_u
                           127,
                           .a8[2]=1 // this designator refers to the member of in_u
                      } };

Если любой подчинённый объект инициализируется явно дважды (что может произойти при использовании спецификаторов), используется инициализатор, который появляется позже в списке (более ранний инициализатор может не вычисляться):

struct {int n;} s = {printf("a\n"), // this may be printed or skipped
                     .n=printf("b\n")}; // always printed

Хотя любые неинициализированные подчинённые объекты инициализируются неявно, неявная инициализация подчинённого объекта никогда не переопределяет явную инициализацию того же подчинённого объекта, если она появилась ранее в списке инициализации (выберите 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 initialized to {42, 43, {18, 19} }
int main(void)
{
    S l = { 1,          // initializes l.i to 1
           .t = x,      // initializes l.t to {42, 43, {18, 19} }
           .t.l = 41,   // changes l.t to {42, 41, {18, 19} }
           .t.a[1] = 17 // changes l.t to {42, 41, {18, 17} }
          };
    printf("l.t.k is %d\n", l.t.k); // .t = x sets l.t.k to 42 explicitly
                                    // .t.l = 41 would zero out l.t.k implicitly
}

Вывод:

l.t.k is 42

Однако, когда инициализатор начинается с открывающей фигурной скобки, его текущий объект полностью переинициализируется, и любые предыдущие явные инициализаторы для любых его подчинённых объектов игнорируются:

struct fred { char s[4]; int n; };
struct fred x[ ] = { { { "abc" }, 1 }, // inits x[0] to { {'a','b','c','\0'}, 1 }
                      [0].s[0] = 'q'   // changes x[0] to { {'q','b','c','\0'}, 1 }
                   };
struct fred y[ ] = { { { "abc" }, 1 }, // inits y[0] to { {'a','b','c','\0'}, 1 }
                     [0] = { // current object is now the entire y[0] object
                             .s[0] = 'q' 
                            } // replaces y[0] with { {'q','\0','\0','\0'}, 0 }
                    };
(начиная с C99)

Примечания

Список инициализации может иметь завершающую запятую, которая игнорируется.

struct {double x,y;} p = {1.0,
                          2.0, // завершающая запятая допустима
                          };

В языке C список инициализаторов в фигурных скобках не может быть пустым (обратите внимание, что C++ допускает пустые списки, а также учтите, что struct в C не может быть пустым):

(до C23)

Список инициализаторов может быть пустым в C, как и в C++:

(начиная с C23)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // Ошибка до C23: список инициализации не может быть пустым
                        // OK начиная с C23: s.n инициализируется в 0
struct {} s = {}; // Ошибка: структура не может быть пустой

Каждое выражение в списке инициализаторов должно быть константным выражением при инициализации агрегатов любой продолжительности хранения.

(до C99)

Как и при любой другой инициализации , каждое выражение в списке инициализаторов должно быть константным выражением при инициализации агрегатов со статической или поточно-локальной (начиная с C11) продолжительностью хранения :

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

Порядок вычисления подвыражений в любом инициализаторе неопределенно последователен (но не в C++, начиная с C++11):

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];
    // designated initializers simplify the use of structs whose
    // 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 Инициализация (стр. 101-105)
  • Стандарт C11 (ISO/IEC 9899:2011):
  • 6.7.9/12-38 Инициализация (стр. 140-144)
  • Стандарт C99 (ISO/IEC 9899:1999):
  • 6.7.8/12-38 Инициализация (стр. 126-130)
  • Стандарт C89/C90 (ISO/IEC 9899:1990):
  • 6.5.7 Инициализация

Смотрите также

C++ documentation для Aggregate initialization