Namespaces
Variants

Array initialization

From cppreference.net

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

= строковый-литерал (1)
= { выражение , ... } (2) (до C99)
= { дескриптор (необязательно) выражение , ... } (2) (с C99)
= { } (3) (с C23)
1) строковый литерал как инициализатор для массивов символов и широких символов
2) список, разделённый запятыми, константных (до C99) выражений, которые являются инициализаторами элементов массива , опционально использующих спецификаторы массива вида [ constant-expression ] = (начиная с C99)
3) пустой инициализатор пусто инициализирует каждый элемент массива

Массивы известного размера и массивы неизвестного размера могут быть инициализированы , но не VLA (since C99) (until C23) . VLA может быть только пусто-инициализирован. (since C23)

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

Содержание

Инициализация из строк

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

  • обычные строковые литералы и UTF-8 строковые литералы (начиная с C11) могут инициализировать массивы любого символьного типа ( char , signed char , unsigned char )
  • L-префиксные широкие строковые литералы могут использоваться для инициализации массивов любого типа, совместимого с (игнорируя cv-квалификаторы) wchar_t
  • Строковые литералы с префиксом u могут использоваться для инициализации массивов любого типа, совместимого с (игнорируя cv-квалификаторы) char16_t
  • Строковые литералы с префиксом U могут использоваться для инициализации массивов любого типа, совместимого с (игнорируя cv-квалификаторы) char32_t
(начиная с C11)

Последовательные байты строкового литерала или широкие символы широкого строкового литерала, включая завершающий нулевой байт/символ, инициализируют элементы массива:

char str[] = "abc"; // str имеет тип char[4] и содержит 'a', 'b', 'c', '\0'
wchar_t wstr[4] = L"猫"; // str имеет тип wchar_t[4] и содержит L'猫', '\0', '\0', '\0'

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

char str[3] = "abc"; // str имеет тип char[3] и содержит 'a', 'b', 'c'

Обратите внимание, что содержимое такого массива можно изменять, в отличие от прямого доступа к строковому литералу с помощью char * str = "abc" ; .

Инициализация списками в фигурных скобках

Когда массив инициализируется списком инициализаторов в фигурных скобках, первый инициализатор в списке инициализирует элемент массива с индексом ноль (если не указан дезигнатор) (since C99) , и каждый последующий инициализатор без дезигнатора (since C99) инициализирует элемент массива с индексом на единицу большим, чем инициализированный предыдущим инициализатором.

int x[] = {1,2,3}; // x имеет тип int[3] и содержит 1,2,3
int y[5] = {1,2,3}; // y имеет тип int[5] и содержит 1,2,3,0,0
int z[4] = {1}; // z имеет тип int[4] и содержит 1,0,0,0
int w[3] = {0}; // w имеет тип int[3] и содержит все нули

Это ошибка — предоставлять больше инициализаторов, чем элементов при инициализации массива известного размера (за исключением инициализации символьных массивов строковыми литералами).

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

int n[5] = {[4]=5,[0]=1,2,3,4}; // содержит 1,2,3,4,5
int a[MAX] = { // начинает инициализацию a[0] = 1, a[1] = 3, ...
    1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0
};
// для MAX=6,  массив содержит 1,8,6,4,2,0
// для MAX=13, массив содержит 1,3,5,7,9,0,0,0,8,6,4,2,0 ("разреженный массив")
(начиная с C99)

При инициализации массива неизвестного размера, наибольший индекс, для которого указан инициализатор, определяет размер объявляемого массива.

Вложенные массивы

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

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

int y[4][3] = { // массив из 4 массивов по 3 целых числа каждый (матрица 4x3)
    { 1 },      // строка 0 инициализирована как {1, 0, 0}
    { 0, 1 },   // строка 1 инициализирована как {0, 1, 0}
    { [2]=1 },  // строка 2 инициализирована как {0, 0, 1}
};              // строка 3 инициализирована как {0, 0, 0}

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

int y[4][3] = {    // массив из 4 массивов по 3 int каждый (матрица 4x3)
1, 3, 5, 2, 4, 6, 3, 5, 7 // строка 0 инициализирована как {1, 3, 5}
};                        // строка 1 инициализирована как {2, 4, 6}
                          // строка 2 инициализирована как {3, 5, 7}
                          // строка 3 инициализирована как {0, 0, 0}
struct { int a[3], b; } w[] = { { 1 }, 2 }; // массив структур
   // { 1 } воспринимается как полностью заключенный в фигурные скобки инициализатор для элемента #0 массива
   // этот элемент инициализируется как { {1, 0, 0}, 0}
   // 2 воспринимается как первый инициализатор для элемента #1 массива
   // этот элемент инициализируется как { {2, 0, 0}, 0}

Обозначения массивов могут быть вложенными; заключенное в скобки константное выражение для вложенных массивов следует за заключенным в скобки константным выражением для внешнего массива:

int y[4][3] = {[0][0]=1, [1][1]=1, [2][0]=1};  // строка 0 инициализирована как {1, 0, 0}
                                               // строка 1 инициализирована как {0, 1, 0}
                                               // строка 2 инициализирована как {1, 0, 0}
                                               // строка 3 инициализирована как {0, 0, 0}
(начиная с C99)

Примечания

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

int n = 1;
int a[2] = {n++, n++}; // неопределённое, но корректное поведение,
                       // n инкрементируется дважды (в произвольном порядке)
                       // инициализация a значениями {1, 2} или {2, 1} допустима
puts((char[4]){'0'+n} + n++); // неопределённое поведение:
                              // инкремент и чтение из n не упорядочены

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

(until C23)

Пустой инициализатор может использоваться для инициализации массива:

(since C23)
int a[3] = {0}; // допустимый способ обнуления массива с блочной областью видимости в C и C++
int a[3] = {}; // допустимый способ обнуления массива с блочной областью видимости в C++; допустим в C начиная с C23

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

static char* p[2] = {malloc(1), malloc(2)}; // ошибка

Пример

int main(void)
{
    // Следующие четыре объявления массивов эквивалентны
    short q1[4][3][2] = {
        { 1 },
        { 2, 3 },
        { 4, 5, 6 }
    };
    short q2[4][3][2] = {1, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 4, 5, 6};
    short q3[4][3][2] = {
        {
            { 1 },
        },
        {
            { 2, 3 },
        },
        {
            { 4, 5 },
            { 6 },
        }
    };
    short q4[4][3][2] = {1, [1]=2, 3, [2]=4, 5, 6};
    // Имена символов могут быть связаны с константами перечисления
    // с использованием массивов с дескрипторами:
    enum { RED, GREEN, BLUE };
    const char *nm[] = {
        [RED] = "red",
        [GREEN] = "green",
        [BLUE] = "blue",
    };
}

Ссылки

  • Стандарт 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 Инициализация