Cómo inicializar una estructura de acuerdo con los estándares del lenguaje de programación C

466

Quiero inicializar un elemento de estructura, dividido en declaración e inicialización. Esto es lo que tengo:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

¿Es esta la forma de declarar e inicializar una variable local de MY_TYPEacuerdo con los estándares del lenguaje de programación C (C89, C90, C99, C11, etc.)? ¿O hay algo mejor o al menos funciona?

Actualización Terminé teniendo un elemento de inicialización estático donde configuré cada subelemento según mis necesidades.

encogerse
fuente
3
realmente debería aceptar una mejor respuesta, veo que tuvo que usar alguna guía de codificación incorrecta, pero aún así no debería sugerir a otras personas que esa es la forma correcta de hacerlo ...
Karoly Horvath
@KarolyHorvath, la mayoría de las buenas respuestas son específicas de C99. Tal vez mi pregunta es un duplicado de stackoverflow.com/questions/6624975/… ?
Cringe
si esa era su intención original, entonces probablemente sí, pero luego 1) los votos serían muy engañosos. 2) desde los primeros resultados de búsqueda, este es el único que muestra la forma C99 ... sería mejor reutilizar esta página para la demostración C99 ... (aparentemente la gente comenzó a vincular esta página para mostrar cómo hacerlo)
Karoly Horvath
10
Es interesante que la respuesta aceptada (y muy votada) en realidad no responda la pregunta, incluso como se publicó originalmente. Los inicializadores designados no abordan el problema del OP, que es dividir la declaración de la inicialización. Para C anterior a 1999, la única solución real es asignar a cada miembro; para C99 y posteriores, un literal compuesto, como en la respuesta de CesarB, es la solución. (Por supuesto, un inicializador real, con o sin designadores, sería aún mejor, pero al parecer el PO se cargan con una muy mala estándar de codificación.)
Keith Thompson
32
Estrictamente hablando, el término "ANSI C" ahora se refiere al estándar ISO 2011, que ANSI ha adoptado. Pero en la práctica, el término "ANSI C" se refiere comúnmente al estándar (oficialmente obsoleto) de 1989. Por ejemplo, "gcc -ansi" todavía aplica el estándar de 1989. Dado que es ISO el que publicó los estándares de 1990, 1999 y 2011, es mejor evitar el término "ANSI C" y consultar la fecha del estándar si existe alguna posibilidad de confusión.
Keith Thompson

Respuestas:

728

En (ANSI) C99, puede usar un inicializador designado para inicializar una estructura:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Editar: Otros miembros se inicializan como cero: "Los miembros de campo omitidos se inicializan implícitamente igual que los objetos que tienen una duración de almacenamiento estático". ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )

philant
fuente
87
genial gracias. incluso puedes anidarlos: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
orion elenzil
44
Esto no responde a la pregunta en absoluto. OP quiere separar la inicialización de la declaración.
osvein
3
C99! = ANSI C - Entonces esto no funciona en C89, ni en C90.
Tim Wißmann
3
C99 es ANSI C, mira esto
Cutberto Ocampo
3
@CutbertoOcampo Entiendo lo que pasó ahora. La pregunta original era pedir soluciones en ANSI C, obviamente quería respuestas solo para C89. En 2016 se editó la pregunta y se eliminó "ANSI C", lo que ahora hace que sea difícil entender por qué esta respuesta y comentarios mencionan "(ANSI) C99".
Étienne
198

Puedes hacerlo con un literal compuesto . Según esa página, funciona en C99 (que también cuenta como ANSI C ).

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

Las designaciones en los inicializadores son opcionales; también podrías escribir:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };
CesarB
fuente
Entonces, ¿los inicializadores designados son para cuando declara y define al mismo tiempo, pero los literales compuestos son para cuando define una variable ya declarada?
Geremia
@Geremia primero debe definir la estructura en ambos casos, pero con los inicializadores designados, hace que el código sea más legible y más resistente a los errores, en caso de cambios en la estructura.
hoijui
2
Esto es un poco complicado. Muchas veces (algunas exitosas) he tratado de usar inicializadores designados. Sin embargo, ahora solo ha quedado claro (gracias a su ejemplo y el de @ philant) que debe existir un reparto si el inicializador no se utiliza en el momento de la creación del objeto. Sin embargo, ahora también he aprendido que si los inicializadores se usan en un momento que no sea la creación de objetos (como en su ejemplo), entonces se lo denomina literal compuesto , no estrictamente un inicializador designado . ideone.com/Ze3rnZ
sherrellbc
93

Veo que ya ha recibido una respuesta sobre ANSI C 99, así que voy a echar un vistazo sobre ANSI C 89. ANSI C 89 le permite inicializar una estructura de esta manera:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

Una cosa importante para recordar, en el momento en que inicialice incluso un objeto / variable en la estructura, todas sus otras variables se inicializarán al valor predeterminado.

Si no inicializa los valores en su estructura, todas las variables contendrán "valores basura".

¡Buena suerte!

Ron Nuni
fuente
3
¿Es posible usar variables en la inicialización?
Cian
2
@Cyan Es para objetos con duración de almacenamiento automático. Ver 6.7.9 13) en open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Para objetos globales está bastante restringido a literales. Ni siquiera puedes usar otros objetos globales, incluso si son constantes.
PSkocik
Entonces, ¿la única diferencia con C 99 es que no puede especificar las designaciones?
Akaisteph7
42

a = (MYTYPE){ true, 15, 0.123 };

haría bien en C99

qrdl
fuente
1
Sinceramente, creo que la respuesta es la más útil, pero lo que sea.
user12211554
22

Casi lo tienes ...

MY_TYPE a = { true,15,0.123 };

La búsqueda rápida en 'struct initialize c' me muestra esto

Kieveli
fuente
1
Creo que esto es cierto para el estándar C, pero no para ANSI C (99?). Además, estoy obligado a codificar reglas que no me permitirán hacer una declaración e inicialización a la vez, así que tengo que dividirlo e inicializar cada subelemento individual.
Cringe
8
La inicialización solo puede ocurrir en el punto de declaración. Eso es lo que significa "inicializar". De lo contrario, está permitiendo que un valor indefinido sea el valor inicial de su variable / estructura, y luego asigna un valor más adelante.
Kieveli
8
um ... ¿Tienes reglas de codificación que son deliberadamente malas? Tal vez deberías presentar al tipo que los escribió en "La adquisición de recursos es la inicialización" ( en.wikipedia.org/wiki/RAII )
James Curran
Confía en mí, realmente, realmente, no puedo cambiar un poco de estas reglas en este momento. Se siente horrible codificarlo. Y hay muchas otras reglas de los años 70, como los encabezados de código estático con información de administración como el número de revisión. Cambios para cada lanzamiento, incluso si la fuente no cambió ...
Cringe
19

El lenguaje de programación estándar ISO / IEC 9899: 1999 (comúnmente conocido como C99) permite utilizar un inicializador designado para inicializar miembros de una estructura o unión de la siguiente manera:

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

Se define en la paragraph 7sección 6.7.8 Initializationde la norma ISO / IEC 9899: 1999 como:

Si un designador tiene la forma
. identificador
entonces el objeto actual (definido a continuación) se tiene estructura o unión tipo y el identificador deberá ser el nombre de un miembro de ese tipo.

Tenga en cuenta que paragraph 9de la misma sección establece que:

Excepto donde se indique explícitamente lo contrario, a los efectos de esta subcláusula, los miembros sin nombre de objetos de estructura y tipo de unión no participan en la inicialización. Los miembros sin nombre de los objetos de estructura tienen un valor indeterminado incluso después de la inicialización.

Sin embargo, en la implementación de GNU GCC, los miembros omitidos se inicializan como valor apropiado para el tipo cero o similar a cero. Como se indica en la sección 6.27 Inicializadores designados de la documentación de GNU GCC:

Los miembros de campo omitidos se inicializan implícitamente igual que los objetos que tienen una duración de almacenamiento estático.

El compilador de Microsoft Visual C ++ debe admitir inicializadores designados desde la versión 2013 según la publicación oficial del blog C ++ Conformance Roadmap . El párrafo Initializing unions and structsdel artículo Inicializadores en la documentación de MSDN Visual Studio sugiere que los miembros sin nombre se inicializaron a valores apropiados similares a cero de manera similar a GNU GCC.

El estándar ISO / IEC 9899: 2011 (comúnmente conocido como C11) que había reemplazado al ISO / IEC 9899: 1999 conserva los inicializadores designados en la sección 6.7.9 Initialization. También conserva paragraph 9sin cambios.

El nuevo estándar ISO / IEC 9899: 2018 (comúnmente conocido como C18) que había reemplazado a ISO / IEC 9899: 2011 conserva los inicializadores designados en la sección 6.7.9 Initialization. También conserva paragraph 9sin cambios.

PF4 Público
fuente
3
Inicialmente sugerí esto como edición a la respuesta aceptada, pero fue rechazada. Por lo tanto, lo publico como respuesta.
PF4Public
Creo que "miembro sin nombre" no significa "campos omitidos", sino "miembros anónimos" como el sindicato en struct thing { union { char ch; int i; }; };. Más adelante, el estándar dice: "Si hay menos inicializadores en una lista entre llaves que elementos o miembros de un agregado, o menos caracteres en un literal de cadena utilizado para inicializar una matriz de tamaño conocido que elementos en la matriz, el resto del agregado se inicializará implícitamente igual que los objetos que tienen una duración de almacenamiento estático ".
Emersion
14

como dijo Ron Nuni:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

Una cosa importante para recordar: en el momento en que inicialice incluso un objeto / variable en la estructura, todas sus otras variables se inicializarán al valor predeterminado.

Si no inicializa los valores en su estructura (es decir, si solo declara esa variable), todos variable.memberscontendrán "valores basura", solo si la declaración es local!

Si la declaración es global o estática (como en este caso), todos los no variable.membersinicializados se inicializarán automáticamente para:

  • 0 para enteros y coma flotante
  • '\0'para char(por supuesto, esto es lo mismo que 0, ychar es un tipo entero)
  • NULL para punteros
r_goyal
fuente
Interesante sobre lo local / global, esto también se aplica a los valores de tiempo de estructura. Tuve uno local y en un caso DST estaba activado, en otro no, no debido a nada de lo que hice. No estaba usando DST (el campo tm_isdst), esto era desde strptime, pero cuando me convertí a time_t algunos tenían una hora libre.
Alan Corey
3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}
robert
fuente
24
una vez que escuché 'la inicialización es diferente a la asignación'. entonces, ¿no es esto una tarea en lugar de una inicialización?
Hayri Uğur Koltuk
2

Si MS no se ha actualizado a C99, MY_TYPE a = {true, 15,0.123};

eddyq
fuente
2

Encontré otra forma de inicializar estructuras.

La estructura:

typedef struct test {
    int num;
    char* str;
} test;

Inicializacion:

test tt = {
    num: 42,
    str: "nice"
};

Según la documentación de GCC, esta sintaxis es obsoleta desde GCC 2.5 .

MichaelWang
fuente
2

No me gustó ninguna de estas respuestas, así que hice la mía. No sé si esto es ANSI C o no, es solo GCC 4.2.1 en su modo predeterminado. Nunca recuerdo el paréntesis, así que comienzo con un subconjunto de mis datos y lucho con los mensajes de error del compilador hasta que se cierra. La legibilidad es mi primera prioridad.

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

Los datos pueden comenzar como un archivo delimitado por tabulaciones que usted busca, reemplaza para dar masajes a otra cosa. Sí, esto es cosa de Debian. Entonces, un par externo de {} (que indica la matriz), luego otro par para cada estructura interna. Con comas entre. Poner cosas en un encabezado no es estrictamente necesario, pero tengo alrededor de 50 elementos en mi estructura, así que los quiero en un archivo separado, tanto para mantener el desorden de mi código como para que sea más fácil de reemplazar.

Alan Corey
fuente
3
¡No hubo dudas sobre la inicialización de la matriz de estructuras! Quiero decir, tu respuesta contiene gastos generales.
Victor Signaevskyi
Supongo que mi punto era declarar la estructura con valores ya en ella versus usar = para establecer cada valor más adelante.
Alan Corey
Ese principio se aplica tanto si se trata de una estructura como de una serie de ellas. Usar = no es realmente inicialización, no creo.
Alan Corey
0

La estructura en C se puede declarar e inicializar así:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}
Ajay Parashar
fuente
0

Ya he leído la documentación de Microsoft Visual Studio 2015 para inicializar tipos agregados , todas las formas de inicialización con{...} se explican allí, pero la inicialización con punto, llamada '' designador '' no se menciona allí. No funciona tampoco.

El capítulo estándar C99 6.7.8 Inicialización explica la posibilidad de designadores, pero en mi opinión no está realmente claro para estructuras complejas. El estándar C99 como pdf .

En mi opinión, puede ser mejor

  1. Utilizar el = {0}; inicialización para todos los datos estáticos. Es menos esfuerzo para el código de la máquina.
  2. Use macros para inicializar, por ejemplo

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

La macro se puede adaptar, su lista de argumentos puede ser independiente del contenido de la estructura modificada. Es apropiado si se deben inicializar menos elementos. También es apropiado para estructuras anidadas. 3. Una forma simple es: Inicializar en una subrutina:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

Esta rutina se parece a ObjectOriented en C. ¡Use thiz, no thispara compilarla también con C ++!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);
Hartmut Schorrig
fuente
0

He estado buscando una buena manera de inicializar mi estructura, y tengo que usar el siguiente (C99). Esto me permite inicializar una sola estructura o una matriz de estructuras de la misma manera que los tipos simples.

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

Esto se puede usar en el código como:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */
div0man
fuente
Pero esto no inicializa una matriz de estructuras a los valores predeterminados. Inicializa la primera estructura en la matriz a los valores dados jsmn_ts_default, pero el resto de las estructuras se inicializan como si la matriz tuviera una duración de almacenamiento estático, lo que significa que los miembros se inicializan en NULLy 0. Pero si cambia a #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}, verá que solo el primer elemento de la matriz se inicializa.
ex nihilo
Sí, acabo de regresar de más pruebas, quería editar la publicación :) por cierto, el mismo comportamiento ocurre con int a[10] = {1};Solo el primer elemento será==1
div0man