Spec-Zone.ru › C

Составные литералы (с C99)

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

Синтаксис

( спецификаторы класса хранения (необязательно)(с C23) тип ) { список инициализаторов } (с C99)
( спецификаторы класса хранения (необязательно)(с C23) тип ) { список инициализаторов , } (с C99)
( спецификаторы класса хранения (необязательно) тип ) { } (с C23)

где

спецификаторы класса хранения - (с C23) Список спецификаторов класса хранения, который может содержать только constexpr, static, register, или thread_local
тип - имя типа, определяющее любой полный тип объекта или массив неизвестного размера, но не VLA
список инициализаторов - список инициализаторов, подходящих для инициализации объекта типа тип

Объяснение

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

Тип составного литерала — тип (за исключением случаев, когда тип — массив неизвестного размера; его размер выводится из списка инициализаторов, как при инициализации массивов).

Категория значения составного литерала — lvalue (можно получить его адрес).

Безымянный объект, к которому относится составной литерал, имеет статический срок жизни, если составной литерал встречается в области файла, и автоматический срок жизни, если составной литерал встречается в области блока (в этом случае срок жизни объекта заканчивается в конце окружающего блока). (до C23)
Если составной литерал оценивается вне тела функции и вне любого списка параметров, он связан с областью файла; в противном случае он связан с окружающим блоком. В зависимости от этого связывания, спецификаторы класса хранения (возможно, пустые), имя типа и, при наличии, список инициализаторов должны быть такими, чтобы они были допустимыми спецификаторами для определения объекта в области файла или области блока соответственно, следующей формы:
   storage-class-specifiers typeof(type) ID = { initializer-list };
где ID — идентификатор, уникальный для всей программы. Составной литерал предоставляет безымянный объект, чьё значение, тип, срок жизни и другие свойства такие же, как если бы задавались синтаксисом определения выше; если срок жизни автоматический, срок жизни экземпляра безымянного объекта — текущее выполнение окружающего блока. Если спецификаторы класса хранения содержат другие спецификаторы, кроме constexpr, static, register, или thread_local, поведение не определено.
(с C23)

Примечания

Составные литералы с константными квалифицированными типами символьных или широких символьных массивов могут совмещать область хранения со строковыми литералами.

(const char []){"abc"} == "abc" // might be 1 or 0, unspecified

Каждый составной литерал создаёт только один объект в своём объёме:

int f (void)
{
    struct s {int i;} *p = 0, *q;
    int j = 0;
again:
    q = p, p = &((struct s){ j++ });
    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
    return p == q && q->i == 1; // always returns 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:2023):
    • 6.5.2.5 Составные литералы (стр: TBD)
  • Стандарт 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/w/c/language/compound_literal

Spec-Zone.ru

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