Compound literals (since 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 . Designated initializers поддерживаются.
Тип составного литерала — type (за исключением случая, когда type является массивом неизвестного размера; его размер выводится из initializer-list как при инициализации массива ).
Категория значения составного литерала является lvalue (его адрес может быть получен).
| Безымянный объект, в который вычисляется составной литерал, имеет статическую продолжительность хранения если составной литерал встречается в области видимости файла, и автоматическую продолжительность хранения если составной литерал встречается в области видимости блока (в этом случае время жизни объекта заканчивается в конце охватывающего блока). | (до C23) | |||||||||||||||||||||||
Если составной литерал вычисляется вне тела функции и вне любого списка параметров, он ассоциируется с областью видимости файла; в противном случае он ассоциируется с охватывающим блоком. В зависимости от этой ассоциации, спецификаторы класса хранения (возможно пустые), имя типа и список инициализаторов, если они есть, должны быть такими, чтобы они были допустимыми спецификаторами для определения объекта в области видимости файла или блока соответственно, в следующей форме:
|
(начиная с C23) | |||||||||||||||||||||||
Примечания
Составные литералы типов символьных или широкосимвольных массивов с квалификатором const могут использовать общую память с строковыми литералами .
(const char []){"abc"} == "abc" // может быть 1 или 0, не определено
Каждый составной литерал создаёт только один объект в своей области видимости:
#include <assert.h> int main(void) { struct S { int i; } *p = 0, *q; int j = 0; again: q = p, p = &((struct S){ j++ }); // создает безымянный объект типа S, // инициализирует его значением, ранее // хранившимся в j, затем присваивает адрес // этого безымянного объекта указателю p if (j < 2) goto again; // примечание: если бы использовался цикл, здесь бы завершалась область видимости, // что прекратило бы время жизни составного // литерала, оставляя p висячим указателем assert(p == q && q->i == 1); }
Поскольку составные литералы являются безымянными, составной литерал не может ссылаться на себя (именованная структура может содержать указатель на себя).
Хотя синтаксис составного литерала похож на приведение типа , важное различие заключается в том, что приведение типа является не-lvalue выражением, тогда как составной литерал является lvalue.
Пример
#include <stdio.h> int *p = (int[]){2, 4}; // создает неименованный статический массив типа int[2] // инициализирует массив значениями {2, 4} // создает указатель p, указывающий на первый элемент // массива const float *pc = (const float []){1e0, 1e1, 1e2}; // составной литерал только для чтения struct point {double x,y;}; int main(void) { int n = 2, *p = &n; p = (int [2]){*p}; // создает неименованный автоматический массив типа int[2] // инициализирует первый элемент значением, ранее // хранившимся в *p // инициализирует второй элемент нулем // сохраняет адрес первого элемента в p void drawline1(struct point from, struct point to); void drawline2(struct point *from, struct point *to); drawline1( (struct point){.x=1, .y=1}, // создает две структуры с областью видимости блока и (struct point){.x=3, .y=4}); // вызывает drawline1, передавая их по значению drawline2( &(struct point){.x=1, .y=1}, // создает две структуры с областью видимости блока и &(struct point){.x=3, .y=4}); // вызывает drawline2, передавая их адреса } 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)