Namespaces
Variants

Compound literals (since C99)

From cppreference.net

Construye un objeto sin nombre de tipo especificado (que puede ser struct, union o incluso tipo array) en el lugar.

Contenidos

Sintaxis

( storage-class-specifiers  (opcional) (desde C23) type ) { initializer-list } (desde C99)
( storage-class-specifiers  (opcional) (desde C23) type ) { initializer-list , } (desde C99)
( storage-class-specifiers  (opcional) type ) { } (desde C23)

donde

storage-class-specifiers - (desde C23) Una lista de especificadores de clase de almacenamiento que solo puede contener constexpr , static , register , o thread_local
type - un nombre de tipo que especifica cualquier tipo de objeto completo o un arreglo de tamaño desconocido, pero no un VLA
initializer-list - lista de inicializadores adecuados para la inicialización de un objeto de tipo type

Explicación

La expresión literal compuesta construye un objeto sin nombre del tipo especificado por type y lo inicializa como se especifica en initializer-list . Los inicializadores designados están aceptados.

El tipo del literal compuesto es type (excepto cuando type es un array de tamaño desconocido; su tamaño se deduce de la initializer-list como en la inicialización de arrays ).

La categoría de valor de un literal compuesto es lvalue (se puede tomar su dirección).

El objeto sin nombre al que evalúa el literal compuesto tiene duración de almacenamiento estática si el literal compuesto aparece en el ámbito de archivo y duración de almacenamiento automática si el literal compuesto aparece en el ámbito de bloque (en cuyo caso la vida útil del objeto termina al final del bloque que lo contiene). (hasta C23)
Si el literal compuesto se evalúa fuera del cuerpo de una función y fuera de cualquier lista de parámetros, se asocia con el ámbito de archivo; de lo contrario, se asocia con el bloque que lo contiene. Dependiendo de esta asociación, los especificadores de clase de almacenamiento (posiblemente vacíos), el nombre de tipo y la lista de inicializadores, si los hay, deben ser tales que sean especificadores válidos para una definición de objeto en el ámbito de archivo o de bloque, respectivamente, de la siguiente forma:
storage-class-specifiers type typeof( type ) ID = { initializer-list } ;
donde ID es un identificador único para todo el programa. Un literal compuesto proporciona un objeto sin nombre cuyo valor, tipo, duración de almacenamiento y otras propiedades son como si se proporcionaran mediante la sintaxis de definición anterior; si la duración de almacenamiento es automática, la vida útil de la instancia del objeto sin nombre es la ejecución actual del bloque que lo contiene. Si los especificadores de clase de almacenamiento contienen otros especificadores que no sean constexpr , static , register , o thread_local , el comportamiento es indefinido.
(desde C23)

Notas

Los literales compuestos de tipos de arreglo de caracteres o caracteres anchos calificados como const pueden compartir almacenamiento con string literals .

(const char []){"abc"} == "abc" // podría ser 1 o 0, no especificado

Cada literal compuesto crea solo un objeto único en su ámbito:

#include <assert.h>
int main(void)
{
    struct S
    {
        int i;
    }
    *p = 0, *q;
    int j = 0;
again:
    q = p,
    p = &((struct S){ j++ }); // crea un objeto sin nombre de tipo S,
                              // lo inicializa con el valor previamente
                              // contenido en j, luego asigna la dirección
                              // de este objeto sin nombre al puntero p
    if (j < 2)
        goto again; // nota: si se usara un bucle, terminaría el ámbito aquí,
                    // lo que finalizaría el tiempo de vida del literal
                    // compuesto dejando a p como un puntero colgante
    assert(p == q && q->i == 1);
}

Debido a que los literales compuestos no tienen nombre, un literal compuesto no puede referenciarse a sí mismo (una estructura nombrada puede incluir un puntero a sí misma).

Aunque la sintaxis de un literal compuesto es similar a un cast , la distinción importante es que un cast es una expresión no lvalue mientras que un literal compuesto es un lvalue.

Ejemplo

#include <stdio.h>
int *p = (int[]){2, 4}; // crea un array estático sin nombre de tipo int[2]
                        // inicializa el array a los valores {2, 4}
                        // crea el puntero p para apuntar al primer elemento
                        // del array
const float *pc = (const float []){1e0, 1e1, 1e2}; // literal compuesto de solo lectura
struct point {double x,y;};
int main(void)
{
    int n = 2, *p = &n;
    p = (int [2]){*p}; // crea un array automático sin nombre de tipo int[2]
                       // inicializa el primer elemento al valor previamente
                       // contenido en *p
                       // inicializa el segundo elemento a cero
                       // almacena la dirección del primer elemento en p
    void drawline1(struct point from, struct point to);
    void drawline2(struct point *from, struct point *to);
    drawline1(
        (struct point){.x=1, .y=1},  // crea dos structs con ámbito de bloque y
        (struct point){.x=3, .y=4}); // llama a drawline1, pasándolos por valor
    drawline2(
        &(struct point){.x=1, .y=1},  // crea dos structs con ámbito de bloque y
        &(struct point){.x=3, .y=4}); // llama a drawline2, pasando sus direcciones
}
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);
}

Salida posible:

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}

Referencias

  • Estándar C23 (ISO/IEC 9899:2024):
  • 6.5.2.5 Literales compuestos (p: 77-80)
  • Estándar C17 (ISO/IEC 9899:2018):
  • 6.5.2.5 Literales compuestos (p: 61-63)
  • Estándar C11 (ISO/IEC 9899:2011):
  • 6.5.2.5 Literales compuestos (p: 85-87)
  • Estándar C99 (ISO/IEC 9899:1999):
  • 6.5.2.5 Literales compuestos (p: 75-77)