Initialisieren einer Struktur gemäß den C-Programmiersprachenstandards

466

Ich möchte ein Strukturelement initialisieren, das in Deklaration und Initialisierung aufgeteilt ist. Das habe ich:

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

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

Ist dies die Möglichkeit, eine lokale Variable MY_TYPEgemäß den Programmiersprachenstandards C (C89, C90, C99, C11 usw.) zu deklarieren und zu initialisieren ? Oder gibt es etwas Besseres oder zumindest funktionierendes?

Update Am Ende hatte ich ein statisches Initialisierungselement, in dem ich jedes Unterelement gemäß meinen Anforderungen festlegte.

zusammenzucken
quelle
3
Sie sollten wirklich eine bessere Antwort akzeptieren, ich sehe, Sie mussten eine schlechte Codierungsanleitung verwenden, aber Sie sollten anderen Leuten immer noch nicht vorschlagen, dass dies der richtige Weg ist.
Karoly Horvath
@ KarolyHorvath Nun, die meisten guten Antworten sind spezifisch für C99. Vielleicht ist meine Frage ein Duplikat von stackoverflow.com/questions/6624975/… ?
erschaudern
Wenn das Ihre ursprüngliche Absicht wäre, dann wahrscheinlich ja, aber dann 1) wären die Stimmen sehr irreführend. 2) Von den Top-Suchtreffern ist dies die einzige, die den C99-Weg zeigt ..... es wäre besser, diese Seite für die C99-Demonstration wiederzuverwenden ... (anscheinend haben die Leute begonnen, diese Seite zu verlinken, um zu zeigen, wie es geht mach es)
Karoly Horvath
10
Interessant, dass die akzeptierte (und stark positiv bewertete) Antwort die Frage nicht wirklich beantwortet, selbst wenn sie ursprünglich veröffentlicht wurde. Bestimmte Initialisierer lösen nicht das Problem des OP, nämlich die Trennung der Deklaration von der Initialisierung. Für C vor 1999 besteht die einzige wirkliche Lösung darin, jedem Mitglied etwas zuzuweisen. Für C99 und höher ist ein zusammengesetztes Literal, wie in der Antwort von CesarB, die Lösung. (Natürlich wäre ein tatsächlicher Initialisierer mit oder ohne Bezeichner noch besser, aber anscheinend war das OP mit einem wirklich schlechten Codierungsstandard ausgestattet.)
Keith Thompson
32
Genau genommen bezieht sich der Begriff "ANSI C" jetzt auf den ISO-Standard 2011, den ANSI übernommen hat. In der Praxis bezieht sich der Begriff "ANSI C" jedoch üblicherweise auf den (offiziell veralteten) Standard von 1989. Zum Beispiel setzt "gcc -ansi" immer noch den Standard von 1989 durch. Da es ISO ist, die die Standards 1990, 1999 und 2011 veröffentlicht hat, ist es am besten, den Begriff "ANSI C" zu vermeiden und sich auf das Datum des Standards zu beziehen, wenn Verwechslungsgefahr besteht.
Keith Thompson

Antworten:

728

In (ANSI) C99 können Sie einen bestimmten Initialisierer verwenden , um eine Struktur zu initialisieren:

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

Bearbeiten: Andere Elemente werden als Null initialisiert: "Ausgelassene Feldelemente werden implizit genauso initialisiert wie Objekte mit statischer Speicherdauer." ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )

Philant
quelle
87
Super, danke. Sie können sie sogar verschachteln: static oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
Orion Elenzil
4
Dies beantwortet die Frage überhaupt nicht. OP möchte die Initialisierung von der Deklaration trennen.
Osvein
3
C99! = ANSI C - Das funktioniert also weder in C89 noch in C90.
Tim Wißmann
3
C99 ist ANSI C, siehe dies
Cutberto Ocampo
3
@ CutbertoOcampo Ich verstehe, was jetzt passiert ist. Die ursprüngliche Frage bestand darin, nach Lösungen in ANSI C zu fragen. Offensichtlich wollte er nur Antworten für C89. 2016 wurde die Frage bearbeitet und "ANSI C" entfernt, was es jetzt schwer zu verstehen macht, warum in dieser Antwort und in den Kommentaren "(ANSI) C99" erwähnt wird.
Étienne
198

Sie können dies mit einem zusammengesetzten Literal tun . Laut dieser Seite funktioniert es in C99 (was auch als ANSI C zählt ).

MY_TYPE a;

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

Die Bezeichnungen in den Initialisierern sind optional. Sie könnten auch schreiben:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };
CesarB
quelle
Bestimmte Initialisierer sind also für den Fall vorgesehen, dass Sie gleichzeitig deklarieren und definieren, zusammengesetzte Literale jedoch für den Fall, dass Sie eine bereits deklarierte Variable definieren?
Geremia
@Geremia Sie müssen in beiden Fällen zuerst die Struktur definieren, aber mit bestimmten Initialisierern machen Sie den Code lesbarer und fehleranfälliger, wenn sich die Struktur ändert.
Hoijui
2
Das ist etwas knifflig. Viele Male (einige erfolgreich) habe ich versucht, bestimmte Initialisierer zu verwenden. Es ist jedoch erst jetzt klar geworden (dank Ihres und des Beispiels von @ philant), dass eine Besetzung vorhanden sein muss, wenn der Initialisierer zum Zeitpunkt der Objekterstellung nicht verwendet wird. Jetzt habe ich jedoch auch erfahren, dass Initialisierer, die zu einem anderen Zeitpunkt als der Objekterstellung verwendet werden (wie in Ihrem Beispiel), als zusammengesetztes Literal bezeichnet werden und nicht ausschließlich als designierter Initialisierer . ideone.com/Ze3rnZ
sherrellbc
93

Ich sehe, dass Sie bereits eine Antwort zu ANSI C 99 erhalten haben, also werde ich einen Bone über ANSI C 89 werfen. Mit ANSI C 89 können Sie eine Struktur folgendermaßen initialisieren:

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

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

Beachten Sie Folgendes: Wenn Sie auch nur ein Objekt / eine Variable in der Struktur initialisieren, werden alle anderen Variablen auf den Standardwert initialisiert.

Wenn Sie die Werte in Ihrer Struktur nicht initialisieren, enthalten alle Variablen "Garbage-Werte".

Viel Glück!

Ron Nuni
quelle
3
Ist es möglich, Variablen bei der Initialisierung zu verwenden?
Cyan
2
@Cyan Dies gilt für Objekte mit automatischer Speicherdauer. Siehe 6.7.9 13) in open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Für globale Objekte ist so ziemlich alles auf Literale beschränkt. Sie können nicht einmal andere globale Objekte verwenden, selbst wenn sie const sind.
PSkocik
Der einzige Unterschied zu C 99 besteht darin, dass Sie die Bezeichnungen nicht angeben können.
Akaisteph7
42

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

würde in C99 gut tun

qrdl
quelle
1
Ich denke ehrlich, die Antwort ist die nützlichste, aber was auch immer.
user12211554
22

Du hast es fast geschafft ...

MY_TYPE a = { true,15,0.123 };

Die schnelle Suche nach 'struct initialize c' zeigt mir dies

Kieveli
quelle
1
Ich denke, das gilt für Standard C, aber nicht für ANSI C (99?). Außerdem bin ich an Codierungsregeln gebunden, die es mir nicht erlauben, Deklaration und Initialisierung gleichzeitig durchzuführen. Daher muss ich sie aufteilen und jedes einzelne Unterelement initialisieren.
erschaudern
8
Die Initialisierung kann nur zum Zeitpunkt der Deklaration erfolgen. Das ist es, was es bedeutet, zu initialisieren. Andernfalls lassen Sie zu, dass ein undefinierter Wert der Anfangswert Ihrer Variablen / Struktur ist, und weisen später einen Wert zu.
Kieveli
8
ähm ... Sie haben Codierungsregeln, die absichtlich schlecht sind? Vielleicht sollten Sie den Mann, der sie geschrieben hat, "Resource Acquisition Is Initialization" ( en.wikipedia.org/wiki/RAII ) vorstellen
James Curran
Vertrauen Sie mir, ich kann an dieser Stelle wirklich, wirklich, ein bisschen an diesen Regeln nichts ändern. Es fühlt sich schrecklich an, es zu codieren. Und es gibt viele andere Regeln aus den 70er Jahren, wie statische Code-Header mit Verwaltungsinformationen wie die Revisionsnummer. Änderungen für jede Veröffentlichung, auch wenn sich die Quelle nicht geändert hat ...
erschaudert
19

Der C-Programmiersprachenstandard ISO / IEC 9899: 1999 (allgemein bekannt als C99) ermöglicht die Verwendung eines bestimmten Initialisierers , um Mitglieder einer Struktur oder Vereinigung wie folgt zu initialisieren:

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

Es ist in paragraph 7Abschnitt 6.7.8 Initializationder Norm ISO / IEC 9899: 1999 definiert als:

Wenn ein Bezeichner das Formular hat
. Bezeichner,
dann muss das aktuelle Objekt (unten definiert) eine Struktur oder einen Vereinigungstyp haben, und der Bezeichner muss der Name eines Mitglieds dieses Typs sein.

Beachten Sie, dass paragraph 9im selben Abschnitt Folgendes angegeben ist:

Sofern nicht ausdrücklich anders angegeben, nehmen nicht benannte Mitglieder von Objekten der Struktur und des Vereinigungstyps für die Zwecke dieses Unterabschnitts nicht an der Initialisierung teil. Unbenannte Elemente von Strukturobjekten haben auch nach der Initialisierung einen unbestimmten Wert.

In der GNU-GCC-Implementierung werden jedoch ausgelassene Elemente als null- oder nullähnlicher typgerechter Wert initialisiert. Wie in Abschnitt 6.27 angegeben. Designated Initializers der GNU GCC-Dokumentation:

Ausgelassene Feldelemente werden implizit genauso initialisiert wie Objekte mit statischer Speicherdauer.

Der Microsoft Visual C ++ - Compiler sollte laut Version C ++ Conformance Roadmap seit Version 2013 bestimmte Initialisierer unterstützen . Der Absatz Initializing unions and structsdes Initializer- Artikels in der Dokumentation zu MSDN Visual Studio schlägt vor, dass unbenannte Mitglieder ähnlich wie GNU GCC auf nullähnliche Werte initialisiert werden.

Die Norm ISO / IEC 9899: 2011 (allgemein bekannt als C11), die die Norm ISO / IEC 9899: 1999 abgelöst hat, behält die gemäß Abschnitt angegebenen Initialisierer bei 6.7.9 Initialization. Es bleibt auch paragraph 9unverändert.

Die neue Norm ISO / IEC 9899: 2018 (allgemein bekannt als C18), die die Norm ISO / IEC 9899: 2011 abgelöst hat, behält die gemäß Abschnitt angegebenen Initialisierer bei 6.7.9 Initialization. Es bleibt auch paragraph 9unverändert.

PF4Public
quelle
3
Anfangs schlug ich dies als Bearbeitung der akzeptierten Antwort vor, aber es wurde abgelehnt. Daher poste ich es als Antwort.
PF4Public
Ich glaube, "Unbenanntes Mitglied" bedeutet nicht "ausgelassene Felder", sondern "anonyme Mitglieder" wie die Gewerkschaft in struct thing { union { char ch; int i; }; };. Der Standard sagt später: "Wenn eine in Klammern eingeschlossene Liste weniger Initialisierer enthält als Elemente oder Elemente eines Aggregats oder weniger Zeichen in einem Zeichenfolgenliteral, das zum Initialisieren eines Arrays bekannter Größe verwendet wird, als Elemente im Array vorhanden sind, Der Rest des Aggregats wird implizit genauso initialisiert wie Objekte mit statischer Speicherdauer. "
Emersion
14

wie Ron Nuni sagte:

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

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

Ein wichtiger Punkt, den Sie beachten sollten: Wenn Sie auch nur ein Objekt / eine Variable in der Struktur initialisieren, werden alle anderen Variablen auf den Standardwert initialisiert.

Wenn Sie die Werte in Ihrer Struktur nicht initialisieren (dh wenn Sie nur diese Variable deklarieren), variable.membersenthalten alle nur dann "Garbage-Werte", wenn die Deklaration lokal ist!

Wenn die Deklaration global oder statisch ist (wie in diesem Fall), werden alle nicht variable.membersinitialisierten automatisch initialisiert, um:

  • 0 für ganze Zahlen und Gleitkomma
  • '\0'für char(natürlich ist dies genau das gleiche wie 0und charein ganzzahliger Typ)
  • NULL für Zeiger.
r_goyal
quelle
Interessant für das Lokale / Globale gilt dies auch für Strukturzeitwerte. Ich hatte eine lokale und in einem Fall war die Sommerzeit eingeschaltet, in einem anderen nicht, nicht aufgrund von irgendetwas, das ich getan habe. Ich habe keine Sommerzeit (das Feld tm_isdst) verwendet, dies war von strptime, aber als ich zu time_t konvertierte, waren einige eine Stunde frei.
Alan Corey
3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}
Robert
quelle
24
Sobald ich gehört habe, dass die Initialisierung anders ist als die Zuweisung. Ist dies nicht eher eine Aufgabe als eine Initialisierung?
Hayri Uğur Koltuk
2

Wenn MS nicht auf C99 aktualisiert wurde, ist MY_TYPE a = {true, 15,0.123};

eddyq
quelle
2

Ich habe einen anderen Weg gefunden, um Strukturen zu initialisieren.

Die Struktur:

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

Initialisierung:

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

Gemäß der Dokumentation von GCC ist diese Syntax seit GCC 2.5 veraltet .

MichaelWang
quelle
2

Ich mochte keine dieser Antworten, also habe ich meine eigenen gemacht. Ich weiß nicht, ob dies ANSI C ist oder nicht, es ist nur GCC 4.2.1 im Standardmodus. Ich kann mich nie an die Klammer erinnern, also beginne ich mit einer Teilmenge meiner Daten und kämpfe mit Compiler-Fehlermeldungen, bis sie geschlossen werden. Lesbarkeit ist meine oberste Priorität.

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

Die Daten beginnen möglicherweise als tabulatorgetrennte Datei, die Sie suchen und ersetzen, um sie in etwas anderes zu massieren. Ja, das ist Debian-Zeug. Also ein äußeres Paar von {} (das das Array angibt), dann ein weiteres Paar für jede innere Struktur. Mit Kommas zwischen. Das Einfügen von Dingen in einen Header ist nicht unbedingt erforderlich, aber ich habe ungefähr 50 Elemente in meiner Struktur, sodass ich sie in einer separaten Datei haben möchte, um das Durcheinander aus meinem Code herauszuhalten und es einfacher zu ersetzen.

Alan Corey
quelle
3
Es gab keine Frage zur Initialisierung einer Reihe von Strukturen! Ich meine, Ihre Antwort enthält Overhead.
Victor Signaevskyi
Ich denke, mein Punkt war, die Struktur mit bereits darin enthaltenen Werten zu deklarieren, anstatt mit = jeden Wert später festzulegen.
Alan Corey
Dieses Prinzip gilt unabhängig davon, ob es sich um eine Struktur oder ein Array von ihnen handelt. Die Verwendung von = ist keine wirkliche Initialisierung, würde ich nicht denken.
Alan Corey
0

Die Struktur in C kann folgendermaßen deklariert und initialisiert werden:

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
quelle
0

Ich habe die Microsoft Visual Studio 2015-Dokumentation zum Initialisieren von Aggregattypen bereits gelesen. Dort werden alle Formen der Initialisierung mit {...}erläutert, aber die Initialisierung mit Punkt mit dem Namen "Bezeichner" wird dort nicht erwähnt. Es funktioniert auch nicht.

Das C99-Standardkapitel 6.7.8 Initialisierung erklärt die Möglichkeit von Bezeichnern, aber meiner Meinung nach ist es für komplexe Strukturen nicht wirklich klar. Der C99 Standard als pdf .

In meinen Augen ist es vielleicht besser

  1. Verwenden Sie die = {0};Initialisierung für alle statischen Daten. Der Maschinencode ist weniger aufwendig.
  2. Verwenden Sie zum Beispiel Makros zum Initialisieren

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

Das Makro kann angepasst werden, seine Argumentliste kann unabhängig vom geänderten Strukturinhalt sein. Es ist richtig, wenn weniger Elemente initialisiert werden sollen. Es ist auch für verschachtelte Strukturen geeignet. 3. Eine einfache Form ist: In einer Unterroutine initialisieren:

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

Diese Routine sieht aus wie in objekt C. Verwendung thiz, nicht thismit C ++ zu kompilieren auch!

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

Ich habe nach einer guten Möglichkeit gesucht, meine Struktur zu initialisieren, und ich muss die folgende verwenden (C99). Auf diese Weise kann ich entweder eine einzelne Struktur oder ein Array von Strukturen auf die gleiche Weise wie einfache Typen initialisieren.

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

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

Dies kann im Code wie folgt verwendet werden:

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
quelle
Dies initialisiert jedoch kein Array von Strukturen auf die Standardwerte. Es initialisiert die erste Struktur im Array mit den in angegebenen Werten jsmn_ts_default, aber der Rest der Strukturen wird so initialisiert, als ob das Array eine statische Speicherdauer hätte, was bedeutet, dass die Mitglieder mit NULLund initialisiert werden 0. Wenn Sie jedoch zu wechseln, werden #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}Sie feststellen, dass nur das erste Array-Element so initialisiert wird.
Ex Nihilo
Ja, ich bin gerade von weiteren Tests zurückgekommen, wollte den Beitrag bearbeiten :) Übrigens passiert das gleiche Verhalten mit int a[10] = {1};Nur das 1. Element wird sein==1
div0man