Was ist der richtige Weg, um eine sehr große Struktur zu initialisieren?

Lesezeit: 6 Minuten

Benutzer-Avatar
Chris Argin

In unserem Code hatten wir früher so etwas:

   *(controller->bigstruct) = ( struct bigstruct ){ 0 };

Früher hat dies hervorragend funktioniert, und dann haben wir Versionen von GCC aktualisiert und plötzlich angefangen, Stapelüberläufe zu sehen. Wenn man sich die Assembly ansieht, hat der alte GCC-Code (2.x) im Grunde Folgendes getan:

memset(controller->bigstruct, 0, sizeof(struct bigstruct));

Das neue GCC (3.4.x) tat dies

   struct bigstruct temp = { 0 };
   controller->bigstruct = temp;

Nachdem ich die C99-Spezifikation überprüft hatte, konnte ich sehen, warum; C99 erfordert grundsätzlich, dass anonyme Strukturen auf dem Stack vorhanden sind. Es ist ein gutes Konzept, aber diese Struktur war 4 Megabyte groß und sollte immer nur auf dem Haufen existieren!

Wir haben darauf zurückgegriffen, unsere eigene „Initialize“-Funktion zu erstellen, die die Mitglieder explizit festlegt, aber das ist hässlich und verursacht Kopfschmerzen bei der Wartung. Ich halte memset nicht für eine geeignete Lösung, da ich nicht wissen kann, dass ein Bitwert von 0 ein geeigneter Nullwert für den Typ ist (Spitzensucht, ich weiß, aber da sind Sie; das macht mir nichts aus der Compiler macht es, weil es kann kennt )

Was ist der “richtige” oder zumindest beste Weg, um eine große Struktur wie diese zu initialisieren?

Um weiter zu verdeutlichen, warum memset meiner Meinung nach keine Lösung ist: Die Initialisierungsregeln für nicht explizit initialisierte Member sind die gleichen wie bei der statischen Initialisierung und lauten wie folgt: – Wenn es einen Zeigertyp hat, wird es mit einem Nullzeiger initialisiert; – Wenn es vom arithmetischen Typ ist, wird es auf (positiv oder vorzeichenlos) Null initialisiert; …

‘memset’ setzt den Speicher auf das Bitmuster Null, was nicht unbedingt dasselbe ist. Stellen Sie sich ein System vor, das keine IEEE-Gleitkommazahlen verwendet. Ungewöhnlich, aber von C unterstützt. Die Darstellung von 0.0 muss nicht “alle Bits Null” bedeuten, es könnte alles sein, was für den Prozessor bequem ist.

  • Wenn ich fragen darf, was genau steht in Ihrer Struktur, dass es eine Definition hat, die 4 MB belegt? :/

    – Matthew Scharley

    7. Oktober 2008 um 6:38 Uhr

  • Welche Art von Plattform unterstützt kein IEEE-Gleitkomma UND lässt Sie eine 4-MB-Struktur zuweisen?

    – John Millikin

    7. Oktober 2008 um 6:56 Uhr

  • Wie wäre es mit Statik? statische Struktur bigstruct Zero_bigstruct = { 0 }; und *(controller->bigstruct) = Zero_bigstruct; ?

    – Nyan

    5. Oktober 2010 um 4:08 Uhr

memset ist der richtige Weg. Sie haben nicht viele Alternativen.

Mach so etwas wie:

#define InitStruct(var, type) type var; memset(&var, 0, sizeof(type))

Damit Sie nur noch:

InitStruct(st, BigStruct);

Und dann st wie gewohnt verwenden …

Ich verstehe nicht, dass “0” kein gültiger “0”-Typ für eine Struktur ist. Die einzige Möglichkeit, eine Struktur “massenweise zu initialisieren”, besteht darin, ihren gesamten Speicher auf einen Wert zu setzen; Andernfalls müssten Sie zusätzliche Logik erstellen, um ihm mitzuteilen, dass er ein bestimmtes Bitmuster pro Mitglied verwenden soll. Das beste “generische” Bitmuster ist 0.

Außerdem – das ist die gleiche Logik, die Sie dabei verwendet haben

*(controller->bigstruct) = *( struct bigstruct ){ 0 };

Daher verstehe ich deine Zurückhaltung nicht, es zu benutzen 🙂

Der erste Kommentar zu diesem Beitrag veranlasste mich, einige Nachforschungen anzustellen, bevor ich ihn und Idioten anrief, und ich fand Folgendes:

http://www.lysator.liu.se/c/c-faq/c-1.html

Sehr interessant; Wenn ich einen Kommentar abstimmen könnte, würde ich es tun 🙂

Abgesehen davon – Ihre einzige Option, wenn Sie auf archaische Architekturen mit Nicht-0-Nullwerten abzielen möchten, besteht immer noch darin, bestimmte Mitglieder manuell zu initialisieren.

Danke Thomas Padron-McCarthy! Ich habe heute etwas Neues gelernt 🙂

  • Die { 0 } im Quellcode lässt sich nicht unbedingt in ein Nur-Null-Bitmuster übersetzen. Zum Beispiel wird eine 0 im Quellcode in einem Zeigerkontext als ein NULL-Zeiger interpretiert, aber der NULL-Zeiger muss nicht nur als Null-Bits dargestellt werden.

    – Thomas Padron-McCarthy

    7. Oktober 2008 um 6:51 Uhr

  • Oder 0 kann ein gültiger Wert für einen Index sein und -1 steht für einen Anfangswert von „nicht gefunden“. Deshalb hat C++ einen ctor.

    – Martin Beckett

    7. Oktober 2008 um 13:24 Uhr

  • @dmckee: Würde das nicht den Zweck ruinieren, tatsächlich eine neue Variable zu deklarieren, indem die Deklaration in einen neuen Gültigkeitsbereich eingeschlossen wird?

    – abschalten

    23. Januar 2009 um 7:50 Uhr

  • Und bei einzeiligen Anweisungen macht es sowieso keinen Sinn, eine var zu deklarieren – was bedeutet, dass der Kompilierfehler, den Sie erhalten, Sie nur daran erinnert, dass Sie etwas falsch machen 🙂

    Benutzer19302

    23. Januar 2009 um 9:34 Uhr

Wenn Sie memset nicht verwenden möchten, können Sie jederzeit eine statische Kopie Ihrer Struktur deklarieren und memcpy verwenden, was eine ähnliche Leistung bietet. Dies fügt Ihrem Programm 4 MB hinzu, ist aber wahrscheinlich besser als das Setzen einzelner Elemente.

Das heißt, wenn GCC Memset verwendet hat und es vorher gut genug war, würde ich vorschlagen, dass es jetzt gut genug ist.

  • Als ein {0} struct ist im Wesentlichen eine Folge von Null-Bytes (es sei denn, Sie befinden sich auf einer wirklich verrückten Plattform), dies fügt dem Programm KEINE 4 MB hinzu. Globale Objekte, die mit Null initialisiert sind, gehen in einen anderen Abschnitt und werden nicht in das Programm-Image eingefügt – was bringt es, wenn der gesamte Abschnitt nur Null ist? Außerdem verbraucht diese statische Struktur zur Laufzeit nicht einmal Speicher, da das VM-Subsystem des Betriebssystems den gesamten Speicherbereich auf eine einzige systemweite Nullseite mit Copy-on-Write-Semantik abbildet.

    – lockerer

    8. April 2010 um 13:30 Uhr

Benutzer-Avatar
Graem Perrow

Wie andere gesagt haben, ist Memset der richtige Weg. Jedoch, nicht Verwenden Sie memset für C++-Objekte, insbesondere solche mit virtuellen Methoden. Das sizeof( foo ) enthält die Tabelle der virtuellen Funktionszeiger, und ein Memset darauf zu erstellen, wird ernsthaften Kummer verursachen.

Wenn memset das Problem nicht von selbst löst, machen Sie einfach ein memset und dann Initialisieren Sie alle Mitglieder, die nicht Null sein sollten (dh Ihre Nicht-IEEE-Gleitkommawerte).

Die private Initialisierungsfunktion ist nicht hässlich, sondern eine gute OO-Methode zum Initialisieren von Objekten (Strukturen). Ich gehe davon aus, dass Ihre Struktur nicht aus 4 MB Zeigern besteht, also würde ich davon ausgehen, dass die Lösung so aussehen sollte:

void init_big_struct(struct bigstruct *s)  
{  
    memset(s, 0, sizeof(struct bigstruct));  
    s->some_pointer = NULL; // Multiply this as needed  
}

Andererseits läuft unser Code auf mehr als 20 eingebetteten Betriebssystemen und einer großen Anzahl unterschiedlicher Hardware. Niemals ein Problem mit nur dem Memset der Struktur.

Viele sprechen über Memset, ohne darüber zu sprechen calloc. Ich würde lieber calloc verwenden, das für diesen Anwendungsfall entwickelt wurde (bitte kommentieren, wenn ich falsch liege):

Die Funktion calloc() weist Speicher für ein Array von nmemb-Elementen mit der Größe von Bytes zu und gibt einen Zeiger auf den zugeordneten Speicher zurück. Der Speicher wird auf Null gesetzt. Wenn nmemb oder size 0 ist, gibt calloc() entweder NULL oder einen eindeutigen Zeigerwert zurück, der später erfolgreich an free() übergeben werden kann.

Beispiel:

#include <stdlib.h> // calloc header
#include <stdio.h> // printf header

void    *init_heap_array(int elem_nb, int elem_size) {
    void *ptr;

    if (!(ptr = calloc(elem_nb, elem_size)))
        return NULL;

    return ptr;
}

void    set_int_value_at_index(int *ptr, int value, int i) {
    ptr[i] = value;
}

void    print_int_array_until(int *ptr, const int until) {
    for (int i = 0; i < until; i++)
        printf("%02d ", ptr[i]);
    putchar('\n');
}

int     main(void) {
    const int array_len = 300000;
    int *n;

    if (!(n = init_heap_array(array_len, sizeof(int))))
        return 1;    

    print_int_array_until(n, 5);
    set_int_value_at_index(n, 42, 1);
    print_int_array_until(n, 5);

    return 0;
}

hmm – zuerst einmal eine Init-Funktion zu erstellen und jedes Mitglied explizit zu setzen, IST DAS RICHTIGE – so arbeiten Konstruktoren in OO-Sprachen.

und zweitens – kennt jemand eine Hardware, die Nicht-IEEE-Gleitkommazahlen implementiert? – vielleicht Commodore 64 oder so 😉

1299190cookie-checkWas ist der richtige Weg, um eine sehr große Struktur zu initialisieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy