Come inizializzare una struttura in conformità con gli standard del linguaggio di programmazione C.


466

Voglio inizializzare un elemento struct, diviso in dichiarazione e inizializzazione. Questo è quello che ho:

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

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

È questo il modo di dichiarare e inizializzare una variabile locale MY_TYPEsecondo gli standard del linguaggio di programmazione C (C89, C90, C99, C11, ecc.)? O c'è qualcosa di meglio o almeno funziona?

Aggiornamento Ho finito per avere un elemento di inizializzazione statica in cui ho impostato ogni sottoelemento in base alle mie esigenze.


3
dovresti davvero accettare una risposta migliore, vedo che hai dovuto usare una cattiva guida alla programmazione, ma non dovresti ancora suggerire ad altre persone che quello è il modo giusto per farlo ..
Karoly Horvath,

@KarolyHorvath bene, la maggior parte delle buone risposte sono specifiche per C99. Forse la mia domanda è un duplicato di stackoverflow.com/questions/6624975/… ?
rabbia

se questa fosse la tua intenzione originale, probabilmente sì, ma poi 1) i voti sarebbero molto fuorvianti. 2) dai principali risultati di ricerca questo è l'unico che mostra il modo C99 ..... sarebbe meglio riutilizzare questa pagina per la dimostrazione C99 ... (apparentemente le persone hanno iniziato a collegare questa pagina per mostrare come farlo)
Karoly Horvath,

10
Interessante che la risposta accettata (e fortemente votata) in realtà non risponda alla domanda, anche se originariamente pubblicata. Gli inizializzatori designati non risolvono il problema del PO, ovvero dividere la dichiarazione dall'inizializzazione. Per la C precedente al 1999, l'unica vera soluzione è quella di assegnare a ciascun membro; per C99 e versioni successive, un letterale composto, come nella risposta di CesarB, è la soluzione. (Ovviamente un vero inizializzatore, con o senza designatori, sarebbe ancora meglio, ma a quanto pare l'OP è stato sellato con uno standard di codifica davvero scadente.)
Keith Thompson

32
A rigor di termini, il termine "ANSI C" si riferisce ora allo standard ISO 2011, adottato dall'ANSI. Ma in pratica il termine "ANSI C" si riferisce comunemente allo standard (ufficialmente obsoleto) del 1989. Ad esempio, "gcc -ansi" applica ancora lo standard del 1989. Poiché è ISO che ha pubblicato gli standard 1990, 1999 e 2011, è meglio evitare il termine "ANSI C" e fare riferimento alla data dello standard se esiste qualche possibilità di confusione.
Keith Thompson,

Risposte:


728

In (ANSI) C99, è possibile utilizzare un inizializzatore designato per inizializzare una struttura:

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

Modifica: gli altri membri vengono inizializzati come zero: "I membri del campo omessi vengono inizializzati implicitamente come gli oggetti con durata di archiviazione statica". ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )


87
fantastico, grazie. puoi persino nidificarli: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
Orion Elenzil,

4
Questo non risponde affatto alla domanda. OP vuole separare l'inizializzazione dalla dichiarazione.
osvein,

3
C99! = ANSI C - Quindi questo non funziona in C89, né in C90.
Tim Wißmann,

3
C99 è ANSI C, vedi questo
Cutberto Ocampo,

3
@CutbertoOcampo Capisco cosa è successo ora. La domanda originale era chiedere soluzioni in ANSI C, ovviamente voleva risposte solo per C89. Nel 2016 la domanda è stata modificata e "ANSI C" è stato rimosso, il che ora rende difficile capire perché questa risposta e commenti menzionino "(ANSI) C99".
Étienne,

198

Puoi farlo con un composto letterale . Secondo quella pagina, funziona in C99 (che conta anche come 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 };

Le designazioni negli inizializzatori sono opzionali; potresti anche scrivere:

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

Quindi, gli inizializzatori designati sono per quando dichiari e definisci allo stesso tempo, ma i letterali composti sono per quando definisci una variabile già dichiarata?
Geremia,

@Geremia devi prima definire la struttura in entrambi i casi, ma con gli inizializzatori designati, rendi il codice più leggibile e più resistente agli errori, in caso di cambiamenti nella struttura.
hoijui,

2
Questo è un po 'complicato. Molte volte (alcune hanno avuto successo) ho provato ad usare inizializzatori designati. Tuttavia, ora è diventato chiaro (grazie all'esempio di your e @ philant) che deve esistere un cast se l'inizializzatore non viene utilizzato al momento della creazione dell'oggetto. Tuttavia, ora ho anche imparato che se gli inizializzatori vengono utilizzati in un momento diverso dalla creazione di oggetti (come nel tuo esempio), allora viene definito letterale composto , non strettamente un inizializzatore designato . ideone.com/Ze3rnZ
sherrellbc,

93

Vedo che hai già ricevuto una risposta su ANSI C 99, quindi lancerò un osso su ANSI C 89. ANSI C 89 ti consente di inizializzare una struttura in questo modo:

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

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

Una cosa importante da ricordare, nel momento in cui si inizializza anche un oggetto / variabile nella struttura, tutte le sue altre variabili verranno inizializzate al valore predefinito.

Se non si inizializzano i valori nella propria struttura, tutte le variabili conterranno "valori inutili".

In bocca al lupo!


3
È possibile utilizzare le variabili nell'inizializzazione?
Ciano,

2
@Cyan È per oggetti con durata di memorizzazione automatica. Vedi 6.7.9 13) in open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Per gli oggetti globali è praticamente limitato ai letterali. Non puoi nemmeno usare altri oggetti globali, anche se sono const.
PSkocik,

Quindi l'unica differenza con C 99 è che non puoi specificare le designazioni?
Akaisteph7,

42

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

andrebbe bene nel C99


1
Sinceramente penso che la risposta sia la più utile, ma comunque.
user12211554

22

Hai quasi capito ...

MY_TYPE a = { true,15,0.123 };

Una rapida ricerca su 'Struct Initialize' mi mostra questo


1
Penso che questo sia vero per lo standard C, ma non per ANSI C (99?). Inoltre sono vincolato a regole di codifica che non mi permetteranno di fare la dichiarazione e l'inizializzazione in una volta, quindi devo dividerlo e inizializzare ogni singolo sottoelemento.
rabbia

8
L'inizializzazione può avvenire solo al momento della dichiarazione. Questo è ciò che significa "inizializzare". Altrimenti stai permettendo a un valore indefinito di essere il valore iniziale della tua variabile / struttura, quindi assegni un valore in un secondo momento.
Kieveli,

8
um ... Hai delle regole di codifica che sono volutamente cattive? Forse dovresti presentare il tizio che li ha scritti in "L'acquisizione delle risorse è inizializzazione" ( en.wikipedia.org/wiki/RAII )
James Curran,

Fidati di me, davvero, davvero, non posso cambiare un po 'di queste regole a questo punto. È orribile codificarlo. E ci sono molte altre regole fin dagli anni '70, come le intestazioni di codice statico con informazioni di gestione come il numero di revisione. Modifiche per ogni versione, anche se la fonte non è cambiata ...
rabbia

19

Lo standard ISO / IEC 9899: 1999 del linguaggio di programmazione C (comunemente noto come C99) consente di utilizzare un inizializzatore designato per inizializzare i membri di una struttura o unione come segue:

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

È definito nella paragraph 7sezione 6.7.8 Initializationdella norma ISO / IEC 9899: 1999 come:

Se un designatore ha il modulo
. identificatore
quindi l'oggetto corrente (definito di seguito) deve avere struttura o tipo di unione e l'identificatore deve essere il nome di un membro di quel tipo.

Si noti che paragraph 9nella stessa sezione si afferma che:

Salvo dove diversamente esplicitamente indicato, ai fini della presente sotto-clausola i membri senza nome di oggetti di struttura e tipo di unione non partecipano all'inizializzazione. I membri senza nome degli oggetti struttura hanno un valore indeterminato anche dopo l'inizializzazione.

Nell'implementazione di GNU GCC, tuttavia, i membri omessi vengono inizializzati come valore appropriato per tipo zero o zero. Come indicato nella sezione 6.27 Inizializzatori designati della documentazione GNU GCC:

I membri del campo omessi vengono inizializzati implicitamente come gli oggetti con durata di archiviazione statica.

Il compilatore di Microsoft Visual C ++ dovrebbe supportare gli inizializzatori designati dalla versione 2013 secondo la Roadmap di conformità C ++ sul post ufficiale del blog . Paragrafo Initializing unions and structsdegli inizializzatori articolo a MSDN documentazione di Visual Studio suggerisce che i membri senza nome inizializzato a zero-come valori appropriati in modo simile a GNU GCC.

Lo standard ISO / IEC 9899: 2011 (comunemente noto come C11) che aveva sostituito ISO / IEC 9899: 1999 mantiene gli inizializzatori designati nella sezione 6.7.9 Initialization. Mantiene ancheparagraph 9 invariato.

Il nuovo standard ISO / IEC 9899: 2018 (comunemente noto come C18) che aveva sostituito ISO / IEC 9899: 2011 mantiene gli inizializzatori designati nella sezione 6.7.9 Initialization. Mantiene inoltre paragraph 9invariato.


3
Inizialmente ho suggerito questo come modifica alla risposta accettata, ma è stato respinto. Quindi lo inserisco come risposta.
PF4 Pubblico

Credo che "Membro senza nome" non significhi "campi omessi", ma piuttosto "membri anonimi" come l'unione in struct thing { union { char ch; int i; }; };. Lo standard in seguito dice: "Se ci sono meno inizializzatori in un elenco racchiuso tra parentesi graffe di quanti siano gli elementi o i membri di un aggregato, o meno caratteri in un letterale stringa usato per inizializzare un array di dimensioni conosciute rispetto agli elementi nell'array, il resto dell'aggregato deve essere inizializzato implicitamente come gli oggetti che hanno una durata di memorizzazione statica. "
emersione

14

come disse 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 da ricordare: nel momento in cui inizializzi anche un oggetto / variabile nella struttura, tutte le sue altre variabili verranno inizializzate al valore predefinito.

Se non inizializzi i valori nella tua struttura (ovvero se dichiari semplicemente quella variabile), tutti variable.membersconterranno "valori inutili", solo se la dichiarazione è locale!

Se la dichiarazione è globale o statica (come in questo caso), tutti i non inizializzati variable.membersverranno inizializzati automaticamente per:

  • 0 per numeri interi e virgola mobile
  • '\0'per char(ovviamente questo è lo stesso di 0, echar è un tipo intero)
  • NULL per i puntatori.

Interessante per il locale / globale, questo vale anche per strutturare i valori temporali. Ne avevo uno locale e in un caso il DST era attivo, in un altro non lo era, non a causa di qualcosa che avevo fatto. Non stavo usando DST (il campo tm_isdst), questo era da strptime, ma quando mi sono convertito in time_t alcuni erano a un'ora libera.
Alan Corey,

3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}

24
una volta che ho sentito "l'inizializzazione è diversa dall'assegnazione". quindi non è un compito piuttosto che un'inizializzazione?
Hayri Uğur Koltuk,

2

Se MS non ha aggiornato a C99, MY_TYPE a = {true, 15,0.123};


2

Ho trovato un altro modo per inizializzare le strutture.

La struttura:

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

Inizializzazione:

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

Secondo la documentazione di GCC, questa sintassi è obsoleta da GCC 2.5 .


2

Non mi è piaciuta nessuna di queste risposte, quindi ho creato la mia. Non so se questo è ANSI C o meno, è solo GCC 4.2.1 nella sua modalità predefinita. Non ricordo mai il bracketing, quindi inizio con un sottoinsieme dei miei dati e combatto con i messaggi di errore del compilatore fino a quando non si chiude. La leggibilità è la mia prima priorità.

    // 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"}
    };

I dati possono iniziare la vita come file delimitati da tabulazioni che cerchi di sostituire per massaggiare qualcos'altro. Sì, questa è roba Debian. Quindi una coppia esterna di {} (che indica l'array), quindi un'altra coppia per ogni struttura interna. Tra virgole tra. Mettere le cose in un'intestazione non è strettamente necessario, ma ho circa 50 elementi nella mia struttura, quindi li voglio in un file separato, entrambi per mantenere il disordine dal mio codice ed è quindi più facile da sostituire.


3
Non c'erano dubbi sull'inizializzazione di array di strutture! Voglio dire, la tua risposta contiene spese generali.
Victor Signaevskyi,

Immagino che il mio punto fosse dichiarare la struttura con valori già presenti rispetto a utilizzare = per impostare ciascun valore in un secondo momento.
Alan Corey,

Tale principio si applica sia che si tratti di una struttura o di una matrice di esse. Usando = non è proprio l'inizializzazione, non credo.
Alan Corey,

0

La struttura in C può essere dichiarata e inizializzata in questo modo:

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;
}

0

Ho già letto la documentazione di Microsoft Visual Studio 2015 per l'inizializzazione dei tipi di aggregati , tutte le forme di inizializzazione con{...} sono spiegate , ma l'inizializzazione con punto, denominata '' designatore '' non è menzionata lì. Non funziona anche.

Lo standard C99 capitolo 6.7.8 Inizializzazione spiega la possibilità di designatori, ma nella mia mente non è molto chiaro per strutture complesse. Lo standard C99 come pdf .

Nella mia mente, potrebbe essere meglio

  1. Usa il = {0}; inizializzazione per tutti i dati statici. È meno sforzo per il codice macchina.
  2. Utilizzare le macro per l'inizializzazione, ad esempio

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

La macro può essere adattata, il suo elenco di argomenti può essere indipendente dal contenuto della struttura modificato. È corretto se devono essere inizializzati meno elementi. È anche appropriato per la struttura nidificata. 3. Un modulo semplice è: Inizializza in una subroutine:

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

Questa routine sembra ObjectOriented in C. Use thiz, per non thiscompilarla anche con C ++!

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

0

Ho cercato un modo carino per inizializzare la mia struttura e devo usare il seguito (C99). Questo mi permette di inizializzare una singola struttura o una matrice di strutture allo stesso modo dei tipi semplici.

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

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

Questo può essere usato nel codice come:

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 */

Ma ciò non inizializza una matrice di strutture sui valori predefiniti. Inizializza la prima struttura dell'array sui valori indicati jsmn_ts_default, ma il resto delle strutture viene inizializzato come se l'array avesse una durata di memorizzazione statica, il che significa che i membri sono inizializzati su NULLe 0. Ma se cambi a #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}vedrai che solo il primo elemento dell'array viene inizializzato.
ex nihilo,

Sì, sono appena tornato da altri test, volevo modificare il post :) a proposito, lo stesso comportamento si verifica con int a[10] = {1};Solo il 1 ° elemento sarà==1
div0man
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.