Составные литералы (начиная с C99)
Создают на месте безымянный объект указанного типа (которым может быть структура, объединение или даже массив).
Синтаксис
( storage-class-specifiers (необязательно)(начиная с C23) type ) { initializer-list } | (начиная с C99) | |
( storage-class-specifiers (необязательно)(начиная с C23) type ) { initializer-list , } | (начиная с C99) | |
( storage-class-specifiers (необязательно) type ) { } | (начиная с C23) |
где
| storage-class-specifiers | - | (начиная с C23) Список спецификаторов класса хранения, содержащий только constexpr, static, register или thread_local |
| type | - | имя типа, указывающее на любой полный объектный тип или массив неизвестного размера, но не VLA |
| initializer-list | - | список инициализаторов, подходящий для инициализации объекта типа type |
Пояснение
Выражение составного литерала создает безымянный объект типа, указанного в type, и инициализирует его согласно initializer-list. Допускаются именованные инициализаторы.
Тип составного литерала — type (за исключением случая, когда type — массив неизвестного размера; его размер выводится из initializer-list, как при инициализации массива).
Категория значения составного литерала — lvalue (можно взять его адрес).
| Безымянный объект, к которому вычисляется составной литерал, имеет статическую длительность хранения, если составной литерал находится на уровне файла, и автоматическую длительность хранения, если он находится на уровне блока (в этом случае время жизни объекта заканчивается в конце содержащего его блока). | (до C23) | |||
Если вычисление составного литерала происходит вне тела функции и вне любого списка параметров, он связан с областью видимости файла; в противном случае он связан с содержащим его блоком. В зависимости от этой связи спецификаторы класса хранения (возможно, пустые), имя типа и список инициализаторов, если он есть, должны быть такими, чтобы соответствовать допустимым спецификаторам определения объекта соответственно в области видимости файла или блока в следующей форме:
| (начиная с C23) |
Примечания
Составные литералы типов массивов символов или широких символов с квалификатором const могут использовать общее хранилище с строковыми литералами.
(const char []){"abc"} == "abc" // might be 1 or 0, unspecified
Каждый составной литерал создает только один объект в своей области видимости:
#include <assert.h>
int main(void)
{
struct S
{
int i;
}
*p = 0, *q;
int j = 0;
again:
q = p,
p = &((struct S){ j++ }); // creates an unnamed object of type S,
// initializes it to the value formerly
// held in j, then assigns the address
// of this unnamed object to the pointer p
if (j < 2)
goto again; // note: if a loop were used, it would end scope here,
// which would terminate the lifetime of the compound
// literal leaving p as a dangling pointer
assert(p == q && q->i == 1);
}
Поскольку составные литералы являются безымянными, составной литерал не может ссылаться на самого себя (именованная структура может содержать указатель на саму себя).
Хотя синтаксис составного литерала похож на синтаксис приведения типа, важное отличие заключается в том, что приведение типа — это выражение, не являющееся lvalue, тогда как составной литерал является lvalue.
Пример
#include <stdio.h>
int *p = (int[]){2, 4}; // creates an unnamed static array of type int[2]
// initializes the array to the values {2, 4}
// creates pointer p to point at the first element of
// the array
const float *pc = (const float []){1e0, 1e1, 1e2}; // read-only compound literal
struct point {double x,y;};
int main(void)
{
int n = 2, *p = &n;
p = (int [2]){*p}; // creates an unnamed automatic array of type int[2]
// initializes the first element to the value formerly
// held in *p
// initializes the second element to zero
// stores the address of the first element in p
void drawline1(struct point from, struct point to);
void drawline2(struct point *from, struct point *to);
drawline1(
(struct point){.x=1, .y=1}, // creates two structs with block scope and
(struct point){.x=3, .y=4}); // calls drawline1, passing them by value
drawline2(
&(struct point){.x=1, .y=1}, // creates two structs with block scope and
&(struct point){.x=3, .y=4}); // calls drawline2, passing their addresses
}
void drawline1(struct point from, struct point to)
{
printf("drawline1: `from` @ %p {%.2f, %.2f}, `to` @ %p {%.2f, %.2f}\n",
(void*)&from, from.x, from.y, (void*)&to, to.x, to.y);
}
void drawline2(struct point *from, struct point *to)
{
printf("drawline2: `from` @ %p {%.2f, %.2f}, `to` @ %p {%.2f, %.2f}\n",
(void*)from, from->x, from->y, (void*)to, to->x, to->y);
}
Возможный вывод:
drawline1: `from` @ 0x7ffd24facea0 {1.00, 1.00}, `to` @ 0x7ffd24face90 {3.00, 4.00}
drawline2: `from` @ 0x7ffd24facec0 {1.00, 1.00}, `to` @ 0x7ffd24faced0 {3.00, 4.00}
Ссылки
- Стандарт C23 (ISO/IEC 9899:2024):
- 6.5.2.5 Составные литералы (с. 77–80)
- Стандарт C17 (ISO/IEC 9899:2018):
- 6.5.2.5 Составные литералы (с. 61–63)
- Стандарт C11 (ISO/IEC 9899:2011):
- 6.5.2.5 Составные литералы (с. 85–87)
- Стандарт C99 (ISO/IEC 9899:1999):
- 6.5.2.5 Составные литералы (с. 75–77)
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/c/language/compound_literal