Namespaces
Variants

Compound literals (since C99)

From cppreference.net

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

Содержание

Синтаксис

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