Namespaces
Variants

Struct and union initialization

From cppreference.net

Al inicializar un objeto de tipo struct o union , el inicializador debe ser una lista no vacía, (hasta C23) entre llaves y separada por comas de inicializadores para los miembros:

= { expresión , ... } (1) (hasta C99)
= { designador (opcional) expresión , ... } (2) (desde C99)
= { } (3) (desde C23)

donde el designator es una secuencia (separada por espacios en blanco o adyacente) de designadores de miembros individuales de la forma . member y array designators de la forma [ index ] .

Todos los miembros que no son inicializados explícitamente son empty-initialized .

Contenidos

Explicación

Al inicializar una union , la lista de inicializadores debe tener solo un miembro, que inicializa el primer miembro de la union a menos que se utilice un inicializador designado (desde C99) .

union { int x; char c[4]; }
  u = {1},           // hace que u.x esté activo con valor 1
 u2 = { .c={'\1'} }; // hace que u2.c esté activo con valor {'\1','\0','\0','\0'}

Al inicializar una struct , el primer inicializador en la lista inicializa el primer miembro declarado (a menos que se especifique un designador) (since C99) , y todos los inicializadores posteriores sin designadores (since C99) inicializan los miembros de la estructura declarados después del inicializado por la expresión anterior.

struct point {double x,y,z;} p = {1.2, 1.3}; // p.x=1.2, p.y=1.3, p.z=0.0
div_t answer = {.quot = 2, .rem = -1 };      // el orden de los elementos en div_t puede variar

Un designador hace que el siguiente inicializador inicialice el miembro de la estructura descrito por el designador. La inicialización luego continúa hacia adelante en orden de declaración, comenzando con el siguiente elemento declarado después del descrito por el designador.

struct {int sec,min,hour,day,mon,year;} z
   = {.day=31,12,2014,.sec=30,15,17}; // initializes z to {30,15,17,31,12,2014}
(desde C99)

Es un error proporcionar más inicializadores que miembros.

Inicialización anidada

Si los miembros de la struct o union son arrays, structs o unions, los inicializadores correspondientes en la lista de inicializadores entre llaves son cualquier inicializador válido para esos miembros, excepto que sus llaves pueden omitirse de la siguiente manera:

Si el inicializador anidado comienza con una llave de apertura, todo el inicializador anidado hasta su llave de cierre inicializa el objeto miembro correspondiente. Cada llave izquierda de apertura establece un nuevo objeto actual . Los miembros del objeto actual se inicializan en su orden natural , a menos que se utilicen designadores (desde C99) : elementos de array en orden de subíndice, miembros de struct en orden de declaración, solo el primer miembro declarado de cualquier union. Los subobjetos dentro del objeto actual que no son inicializados explícitamente por la llave de cierre son empty-initialized .

struct example {
    struct addr_t {
       uint32_t port;
    } addr;
    union {
       uint8_t a8[4];
       uint16_t a16[2];
    } in_u;
};
struct example ex = { // inicio de la lista de inicialización para struct example
                     { // inicio de la lista de inicialización para ex.addr
                        80 // miembro único de la estructura inicializado
                     }, // fin de la lista de inicialización para ex.addr
                     { // inicio de la lista de inicialización para ex.in_u
                        {127,0,0,1} // inicializa el primer elemento de la unión
                     } };

Si el inicializador anidado no comienza con una llave de apertura, solo se toman suficientes inicializadores de la lista para cubrir los elementos o miembros del array, struct o union miembro; cualquier inicializador restante se deja para inicializar el siguiente miembro del struct:

struct example ex = {80, 127, 0, 0, 1}; // 80 inicializa ex.addr.port
                                        // 127 inicializa ex.in_u.a8[0]
                                        // 0 inicializa ex.in_u.a8[1]
                                        // 0 inicializa ex.in_u.a8[2]
                                        // 1 inicializa ex.in_u.a8[3]

Cuando los designadores están anidados, los designadores para los miembros siguen a los designadores para las estructuras/uniones/arreglos que los contienen. Dentro de cualquier lista de inicialización entre llaves anidadas, el designador más externo se refiere al objeto actual y selecciona únicamente el subobjeto a inicializar dentro del objeto actual .

struct example ex2 = { // objeto actual es ex2, los designadores son para miembros de example
                       .in_u.a8[0]=127, 0, 0, 1, .addr=80}; 
struct example ex3 = {80, .in_u={ // cambia el objeto actual a la unión ex.in_u
                           127,
                           .a8[2]=1 // este designador se refiere al miembro de in_u
                      } };

Si algún subobjeto se inicializa explícitamente dos veces (lo cual puede ocurrir cuando se usan designadores), el inicializador que aparece más tarde en la lista es el que se utiliza (el inicializador anterior podría no ser evaluado):

struct {int n;} s = {printf("a\n"), // esto podría imprimirse o omitirse
                     .n=printf("b\n")}; // siempre se imprime

Aunque cualquier subobjeto no inicializado se inicializa implícitamente, la inicialización implícita de un subobjeto nunca anula la inicialización explícita del mismo subobjeto si apareció antes en la lista de inicializadores (seleccione clang para observar la salida correcta):

#include <stdio.h>
typedef struct { int k; int l; int a[2]; } T;
typedef struct { int i;  T t; } S;
T x = {.l = 43, .k = 42, .a[1] = 19, .a[0] = 18 };
 // x inicializado a {42, 43, {18, 19} }
int main(void)
{
    S l = { 1,          // inicializa l.i a 1
           .t = x,      // inicializa l.t a {42, 43, {18, 19} }
           .t.l = 41,   // cambia l.t a {42, 41, {18, 19} }
           .t.a[1] = 17 // cambia l.t a {42, 41, {18, 17} }
          };
    printf("l.t.k is %d\n", l.t.k); // .t = x establece l.t.k a 42 explícitamente
                                    // .t.l = 41 pondría l.t.k a cero implícitamente
}

Salida:

l.t.k is 42

Sin embargo, cuando un inicializador comienza con una llave abierta izquierda, su objeto actual se reinicializa completamente y cualquier inicializador explícito previo para cualquiera de sus subobjetos se ignora:

struct fred { char s[4]; int n; };
struct fred x[ ] = { { { "abc" }, 1 }, // inicializa x[0] a { {'a','b','c','\0'}, 1 }
                      [0].s[0] = 'q'   // cambia x[0] a { {'q','b','c','\0'}, 1 }
                   };
struct fred y[ ] = { { { "abc" }, 1 }, // inicializa y[0] a { {'a','b','c','\0'}, 1 }
                     [0] = { // el objeto actual ahora es todo el objeto y[0]
                             .s[0] = 'q' 
                            } // reemplaza y[0] con { {'q','\0','\0','\0'}, 0 }
                    };
(desde C99)

Notas

La lista de inicializadores puede tener una coma final, la cual se ignora.

struct {double x,y;} p = {1.0,
                          2.0, // coma final permitida
                          };

En C, la lista entre llaves de inicializadores no puede estar vacía (nótese que C++ permite listas vacías, y también nótese que un struct en C no puede estar vacío):

(until C23)

La lista de inicializadores puede estar vacía en C como en C++:

(since C23)
struct {int n;} s = {0}; // CORRECTO
struct {int n;} s = {}; // Error hasta C23: la lista de inicialización no puede estar vacía
                        // CORRECTO desde C23: s.n se inicializa a 0
struct {} s = {}; // Error: la estructura no puede estar vacía

Cada expresión en la lista de inicializadores debe ser una expresión constante al inicializar agregados de cualquier duración de almacenamiento.

(hasta C99)

Como con todas las demás inicializaciones , cada expresión en la lista de inicializadores debe ser una expresión constante al inicializar agregados de duración de almacenamiento estática o local de hilo (desde C11) duración de almacenamiento :

static struct {char* p} s = {malloc(1)}; // error

El orden de evaluación de las subexpresiones en cualquier inicializador tiene secuencia indeterminada (pero no en C++ desde C++11):

int n = 1;
struct {int x,y;} p = {n++, n++}; // no especificado, pero comportamiento bien definido:
                                  // n se incrementa dos veces en orden arbitrario
                                  // p igual {1,2} y {2,1} son ambos válidos
(desde C99)

Ejemplo

#include <stdio.h>
#include <time.h>
int main(void)
{
    char buff[70];
    // los inicializadores designados simplifican el uso de estructuras cuyo
    // orden de miembros no está especificado
    struct tm my_time = { .tm_year=2012-1900, .tm_mon=9, .tm_mday=9,
                          .tm_hour=8, .tm_min=10, .tm_sec=20 };
    strftime(buff, sizeof buff, "%A %c", &my_time);
    puts(buff);
}

Salida posible:

Sunday Sun Oct  9 08:10:20 2012

Referencias

  • Estándar C17 (ISO/IEC 9899:2018):
  • 6.7.9/12-39 Inicialización (p: 101-105)
  • Estándar C11 (ISO/IEC 9899:2011):
  • 6.7.9/12-38 Inicialización (p: 140-144)
  • Estándar C99 (ISO/IEC 9899:1999):
  • 6.7.8/12-38 Inicialización (p: 126-130)
  • Estándar C89/C90 (ISO/IEC 9899:1990):
  • 6.5.7 Inicialización

Véase también

Documentación de C++ para Inicialización de agregados