Compound literals (since C99)
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:
|
(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)