Spec-Zone.ru › C

Инициализация структур и объединений

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

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

где спецификатор — это последовательность (разделённых пробелами или расположенных рядом) отдельных спецификаторов членов в форме . член и спецификаторы массивов в форме [ индекс ].

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

Объяснение

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

union { int x; char c[4]; }
  u = {1},           // makes u.x active with value 1
 u2 = { .c={'\1'} }; // makes u2.c active with value {'\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 };      // order of elements in div_t may vary

Спецификатор вызывает инициализацию следующего инициализатора для члена структуры, описанного в спецификаторе. Затем инициализация продолжается в порядке объявления, начиная со следующего элемента после описанного спецификатором.

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 = { // start of initializer list for struct example
                     { // start of initializer list for ex.addr
                        80 // initialized struct's only member
                     }, // end of initializer list for ex.addr
                     { // start of initializer-list for ex.in_u
                        {127,0,0,1} // initializes first element of the union
                     } };

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

struct example ex = {80, 127, 0, 0, 1}; // 80 initializes ex.addr.port
                                        // 127 initializes ex.in_u.a8[0]
                                        // 0 initializes ex.in_u.a8[1]
                                        // 0 initializes ex.in_u.a8[2]
                                        // 1 initializes 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, // trailing comma OK
                          };

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

(до C23)

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

(с C23)
struct {int n;} s = {0}; // OK
struct {int n;} s = {}; // Error until C23: initializer-list cannot be empty
                        // OK since C23: s.n is initialized to 0
struct {} s = {}; // Error: struct cannot be empty

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

(до 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++ для Агрегатной инициализации

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/c/language/struct_initialization

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API