6.28 Составные литералы
Составной литерал похож на приведение к типу скобочной начальной инициализирующей последовательности. Его значение — это объект указанного в приведении типа, содержащий элементы, указанные в инициализаторе. В отличие от результата приведения, составной литерал является lvalue. ISO C99 и более поздние версии поддерживают составные литералы. Как расширение, GCC поддерживает составные литералы также в режиме C90 и в C++, хотя, как объясняется ниже, семантика C++ несколько отличается.
Обычно, указанный тип составного литерала — это структура. Предположим, что struct foo и structure объявлены, как показано:
struct foo {int a; char b[2];} structure;
Вот пример создания struct foo с помощью составного литерала:
structure = ((struct foo) {x + y, 'a', 0});
Это эквивалентно написанию следующего:
{
struct foo temp = {x + y, 'a', 0};
structure = temp;
}
Вы также можете создать массив, хотя это опасно в C++, как объяснено ниже. Если все элементы составного литерала являются (состоят из) простых константных выражений, подходящих для использования в инициализаторах объектов со статической продолжительностью жизни, то составной литерал можно привести к указателю на его первый элемент и использовать в таком инициализаторе, как показано здесь:
char **foo = (char *[]) { "x", "y", "z" };
Составные литералы для скалярных типов и типов объединения также разрешены. В следующем примере переменная i инициализируется значением 2, результатом инкрементирования безымянного объекта, созданного составным литералом.
int i = ++(int) { 1 };
Как расширение GNU, GCC разрешает инициализацию объектов со статической продолжительностью жизни с помощью составных литералов (что невозможно в ISO C99, потому что инициализатор не является константой). Это обрабатывается так, как если бы объект был инициализирован только скобочной последовательностью, если типы составного литерала и объекта совпадают. Элементы составного литерала должны быть константами. Если объект, который инициализируется, имеет тип массива неизвестного размера, размер определяется размером составного литерала.
static struct foo x = (struct foo) {1, 'a', 'b'};
static int y[] = (int []) {1, 2, 3};
static int z[] = (int [3]) {1};
Вышеприведённые строки эквивалентны следующему:
static struct foo x = {1, 'a', 'b'};
static int y[] = {1, 2, 3};
static int z[] = {1, 0, 0};
В C, составной литерал обозначает безымянный объект со статической или автоматической продолжительностью жизни. В C++, составной литерал обозначает временный объект, который существует только до конца его полного выражения. В результате, хорошо определённый код C, который получает адрес подобъекта составного литерала, может быть неопределённым в C++, поэтому G++ отклоняет преобразование временного массива в указатель. Например, если пример составного литерала массива встречается внутри функции, любое последующее использование foo в C++ будет иметь неопределённое поведение, потому что продолжительность жизни массива заканчивается после объявления foo.
В качестве оптимизации, G++ иногда даёт составным литералам массивов более длительную продолжительность жизни: когда массив либо появляется вне функции, либо имеет тип с квалификатором const. Если foo и его инициализатор имели элементы типа char *const вместо char *, или если foo была глобальной переменной, массив имел бы статическую продолжительность жизни. Но, вероятно, безопаснее всего просто избегать использования составных литералов массивов в коде C++.
Далее: Определённые инициализации, Предыдущее: Инициализаторы, Вверх: Расширения C [Оглавление][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-9.5.0/gcc/Compound-Literals.html