Speicher auf Null setzen

Lesezeit: 9 Minuten

Benutzer-Avatar
Ameise2009

gcc 4.4.4 C89

Ich frage mich nur, was die meisten C-Programmierer tun, wenn sie den Speicher auf Null setzen wollen.

Zum Beispiel habe ich einen Puffer von 1024 Bytes. Manchmal mache ich das:

char buffer[1024] = {0};

Dadurch werden alle Bytes auf Null gesetzt.

Sollte ich es jedoch so deklarieren und memset verwenden?

char buffer[1024];
.
.
memset(buffer, 0, sizeof(buffer));

Gibt es einen wirklichen Grund, warum Sie den Speicher auf Null setzen müssen? Was ist das Schlimmste, was passieren kann, wenn man es nicht tut?

  • Sie sind derjenige, der weiß, ob Sie den Speicher auf Null setzen müssen. Erwartet Ihr Code Nullen oder nicht?

    – Kaskabel

    21. Mai 2010 um 17:38 Uhr

  • Für Leute, die denken, dass der erste nicht alle Elemente auf Null setzt: Lesen Sie die Spezifikation.

    – mmx

    21. Mai 2010 um 17:56 Uhr

  • Wow, das sind viele positiv bewertete falsche Antworten!

    – Avakar

    21. Mai 2010 um 18:07 Uhr

  • @Andrey, wo genau ist das Problem mit calloc?

    – JSBձոգչ

    21. Mai 2010 um 18:12 Uhr

  • @Andrey seit wann sind calloc und memset “archaisch und veraltet” geworden!? Bitte poste das als Antwort, damit ich es ablehnen kann 🙂

    – Earlz

    21. Mai 2010 um 18:26 Uhr

Benutzer-Avatar
Tim Post

Das Schlimmste, was passieren kann? Sie enden (unwissentlich) mit einer Zeichenfolge, die nicht NULL-terminiert ist, oder mit einer Ganzzahl, die das erbt, was rechts davon stand, nachdem Sie in einen Teil des Puffers gedruckt haben. Nicht abgeschlossene Zeichenfolgen können jedoch auch auf andere Weise auftreten, selbst wenn Sie den Puffer initialisiert haben.

Bearbeiten (aus Kommentaren) Das Ende der Welt ist auch eine entfernte Möglichkeit, je nachdem, was Sie tun.

Beides ist unerwünscht. Wenn jedoch dynamisch zugewiesener Speicher nicht vollständig vermieden wird, sind die meisten statisch zugewiesenen Puffer normalerweise ziemlich klein, was macht memset() relativ günstig. In der Tat viel billiger als die meisten Anrufe calloc() für dynamische Blöcke, die tendenziell größer als ~2k sind.

c99 enthält Angaben zu Default-Initialisierungswerten, kann ich aber scheinbar nicht machen gcc -std=c99 stimme dem zu, jede Art von Speicherung zu verwenden.

Da immer noch viele ältere Compiler (und Compiler, die nicht ganz c99 sind) verwendet werden, ziehe ich es vor, einfach zu verwenden memset()

  • “Was ist das Schlimmste, was passieren kann”? Ein nicht nullterminierter String oder ein Pufferüberlauf kann Schlüsseldaten überschreiben, Ihre Stack-Zeiger zerstören, zu Sicherheitslücken führen, Sie entlassen, Ihre Kinder fressen, eine Hungersnot in Afrika verursachen, einen Atomkrieg anzetteln und den gefürchteten Cthulhu beschwören. Der weise Programmierer schützt sich mit jeder Waffe, die er hat, vor ihnen. Außerdem bin ich mir ziemlich sicher, dass kein kommerzieller C-Compiler diesen Teil der Spezifikation tatsächlich implementiert, außer vielleicht in Debug-Builds. Nicht initialisierte Variablen erhalten nicht initialisierten Speicher.

    – JSBձոգչ

    21. Mai 2010 um 21:29 Uhr

  • @JS Bangs – Ich kann Ihnen versichern, dass meine Kinder und Sprengköpfe nicht unter diesem Schicksal leiden werden. Unabhängig davon, wie viel Kaffee oder übermäßig koffeinhaltige Getränke (in Litern oder Gallonen) haben Sie in den letzten 24 Stunden konsumiert?

    – Tim Post

    21. Mai 2010 um 21:37 Uhr

  • @JS Bangs kannst du “unbegrenzt” definieren? speziell für die statische Zuweisung?

    – Tim Post

    21. Mai 2010 um 22:12 Uhr

  • @Tim: Meine Kopie von C99 hat keinen Abschnitt 8.5.1 – können Sie einen Ausschnitt davon posten, was es über die Initialisierung auf etwas Konsistentes sagen könnte? Soweit ich weiß, werden nur statisch zugewiesene Objekte auf Null initialisiert; automatische Variablen (die meisten lokalen) erhalten in C keinerlei Initialisierungsversprechen, es sei denn, sie werden ausdrücklich vom Programmierer initialisiert.

    – Michael Burr

    22. Mai 2010 um 7:04 Uhr

  • @Michael Burr – Ich habe falsch zitiert. Ich werde die genaue Passage finden und erneut bearbeiten, sobald ich sie habe. ich kennt Ich habe gelesen, dass nicht initialisierter Speicher (es könnte statisch spezifisch gewesen sein) vom Compiler konsistent initialisiert werden soll. 8.5.1 befasst sich mit der Initialisierung von Mitgliedern, siehe gcc.gnu.org/ml/gcc-bugs/2000-07/msg00639.html aber ich kann die gesuchte Referenz nicht finden.

    – Tim Post

    22. Mai 2010 um 8:14 Uhr

Ich bevorzuge sehr

char buffer[1024] = { 0 };

Es ist kürzer, einfacher zu lesen und weniger fehleranfällig. Benutz nur memset auf dynamisch zugewiesenen Puffern und dann bevorzugen calloc.

  • Es ist kürzer, einfacher zu tippen und weniger fehleranfällig. Es ist jedoch nicht so offensichtlich, wenn Sie die Regeln für nicht explizit initialisierte Elemente von Arrays vergessen (ich gebe zu, einer der Schuldigen zu sein). Die Alternative könnte also zu Fehlern führen, wenn Sie die falschen Grenzen wählen, aber selbst eine Person, die ihren Morgenkaffee noch nicht getrunken hat, wird nicht durch das, was ein memset() tut, in die Irre geführt. Bedeutet dies, auf einen kleineren Nenner des Programmierers zu codieren? Vielleicht, aber mein defensiver Stil erlaubt nie viele “Standard”-Initialisierungen. Wenn Sie die Standardinitialisierung nicht verwenden, vergessen Sie die Regeln.

    – Edwin Buck

    21. Mai 2010 um 18:13 Uhr


  • @Edwin Buck: Wenn Sie die Regeln für nicht explizit initialisierte Elemente vergessen, was ist das Schlimmste, was passieren wird? Das ist nicht wirklich ein Nachteil.

    – jamesdlin

    21. Mai 2010 um 20:53 Uhr

  • Das Schlimmste, was passieren wird, ist, dass jemand anderes mit funktionierendem (und korrektem) Code herumspielt und denkt, dass nur das erste Element initialisiert wird. Es ist nicht so, dass der Code irgendwelche Probleme enthalten wird, während Sie ihn schreiben, es ist so, dass die “andere” Person es vermasseln könnte, indem sie versucht, Ihnen zu “helfen”, etwas zu reparieren, das nicht kaputt ist.

    – Edwin Buck

    21. Mai 2010 um 21:22 Uhr

  • @Edwin Buck: Und selbst dann ist es wahrscheinlich, dass der andere Programmierer eine korrekte Version mit schreibt memset, was wahrscheinlich sowieso in Ordnung ist. Außerdem sollten andere Programmierer im Allgemeinen nichts ändern, was nicht kaputt ist, und wenn Sie Verwenden Sie es nicht in Ihrem Code, dann verewigen Sie das Problem, dass andere Leute mit der Redewendung nicht so vertraut sind.

    – jamesdlin

    22. Mai 2010 um 5:32 Uhr


  • Die Regel ist sehr einfach zu merken, wenn man sie einmal kennt: “Objekte werden in C nie teilweise initialisiert”. Alles (Array, Struct usw.) ist also entweder vollständig initialisiert oder überhaupt nicht initialisiert.

    – Café

    22. Mai 2010 um 9:39 Uhr

Wenn Sie definieren char buffer[1024] Ohne Initialisierung erhalten Sie undefinierte Daten darin. Beispielsweise wird Visual C++ im Debugmodus mit 0xcd initialisiert. Im Release-Modus weist es einfach den Speicher zu und kümmert sich nicht darum, was sich in diesem Block von der vorherigen Verwendung befindet.

Außerdem veranschaulichen Ihre Beispiele die Initialisierung der Laufzeit im Vergleich zur Kompilierzeit. Wenn dein char buffer[1024] = { 0 } eine globale oder statische Deklaration ist, wird sie mit ihren initialisierten Daten im Datensegment der Binärdatei gespeichert, wodurch Ihre Binärdatei um etwa 1024 Bytes (in diesem Fall) erhöht wird. Wenn sich die Definition in einer Funktion befindet, wird sie auf dem Stapel gespeichert und zur Laufzeit zugewiesen und nicht in der Binärdatei gespeichert. Wenn Sie in diesem Fall einen Initialisierer bereitstellen, wird der Initialisierer in der Binärdatei gespeichert und ist ein Äquivalent von a memcpy() wird getan, um zu initialisieren buffer zur Laufzeit.

Hoffentlich hilft Ihnen das bei der Entscheidung, welche Methode für Sie am besten geeignet ist.

  • Genau das was ich posten sollte. Die Differenz ist die erzeugte Binärausgabe da!

    – Robert

    21. Mai 2010 um 19:34 Uhr

  • Für statische und globale Deklarationen buffer würde im BSS-Segment gespeichert werden, richtig? @Spoulson

    – Zaid Khan

    28. September 2017 um 17:17 Uhr


Benutzer-Avatar
jamesdlin

In diesem speziellen Fall gibt es keinen großen Unterschied. ich bevorzuge = { 0 } Über memset Weil memset ist fehleranfälliger:

  • Es bietet die Möglichkeit, die Grenzen falsch zu setzen.
  • Es bietet die Möglichkeit, die Argumente zu vermischen memset (z.B memset(buf, sizeof buf, 0) Anstatt von memset(buf, 0, sizeof buf).

Im Algemeinen, = { 0 } ist besser zum initialisieren structs auch. Es initialisiert effektiv alle Mitglieder, als ob Sie geschrieben hätten = 0 jeweils zu initialisieren. Dies bedeutet, dass Zeigermitglieder garantiert mit dem Nullzeiger initialisiert werden (die möglicherweise nicht alle Bits-Null sindund alle Bits-Null ist das, was Sie bekommen würden, wenn Sie verwendet hätten memset).

Auf der anderen Seite, = { 0 } kann Füllbits in a lassen struct als Müll, daher ist es möglicherweise nicht angemessen, wenn Sie es verwenden möchten memcmp um sie später zu vergleichen.

Das Schlimmste, was passieren kann, wenn Sie dies nicht tun, ist, dass Sie einige Daten Zeichen für Zeichen schreiben und sie später als Zeichenfolge interpretieren (und Sie kein Null-Terminator geschrieben haben). Oder Sie erkennen am Ende nicht, dass ein Teil davon nicht initialisiert wurde, und lesen es so, als wären es gültige Daten. Im Grunde: allerlei Gemeinheiten.

Memset sollte in Ordnung sein (vorausgesetzt, Sie korrigieren die Größe des Tippfehlers :-)). Ich ziehe das Ihrem ersten Beispiel vor, weil ich denke, dass es klarer ist.

Für dynamisch zugewiesenen Speicher verwende ich calloc statt malloc und memset.

Benutzer-Avatar
Programmr

Eines der Dinge, die passieren können, wenn Sie nicht initialisieren, ist, dass Sie Gefahr laufen, vertrauliche Informationen preiszugeben.

Nicht initialisierter Speicher enthält möglicherweise etwas Sensibles aus einer früheren Verwendung dieses Speichers. Vielleicht ein Passwort oder Kryptoschlüssel oder Teil einer privaten E-Mail. Ihr Code kann diesen Puffer oder diese Struktur später irgendwo übertragen oder auf die Festplatte schreiben, und wenn Sie ihn nur teilweise gefüllt haben, enthält der Rest immer noch diese vorherigen Inhalte. Bestimmte sichere Systeme erfordern Nullstellen Puffer, wenn ein Adressraum vertrauliche Informationen enthalten kann.

Ich benutze lieber memset um einen Teil des Speichers zu löschen, insbesondere wenn Sie mit Zeichenfolgen arbeiten. Ich möchte ohne Zweifel wissen, dass nach meiner Zeichenfolge ein Nulltrennzeichen steht. Ja, ich weiß, dass Sie a anhängen können \0 am Ende jeder Zeichenfolge und einige Funktionen erledigen dies für Sie, aber ich möchte keinen Zweifel daran haben, dass dies stattgefunden hat.

Eine Funktion könnte fehlschlagen, wenn Sie Ihren Puffer verwenden, und der Puffer bleibt unverändert. Hätten Sie lieber einen Puffer mit unbekanntem Müll oder gar nichts?

  • Das erste Beispiel ist nicht Compiler-abhängig – es ist Standard-C.

    – Vicky

    21. Mai 2010 um 17:45 Uhr

  • @Vicky – Danke für die Korrektur, ich war mir beim ersten Beispiel nicht 100% sicher.

    Benutzer65628

    21. Mai 2010 um 17:48 Uhr

  • Anonymer Downvoter, wenn etwas, das ich in meiner Antwort gesagt habe, falsch ist und nicht das, was ich bereits korrigiert habe, würde ich wirklich gerne wissen, was es ist.

    Benutzer65628

    21. Mai 2010 um 17:58 Uhr

  • Gibt es einen Grund, den Sie bevorzugen memset zu ={0}?

    – Avakar

    21. Mai 2010 um 18:02 Uhr

  • Es geht nicht um “teuer”/”preiswert”. Es geht um die einfache Tatsache, dass memset ist allgemein ungültig nähern, während = { 0 } ist immer gültig.

    – AnT steht zu Russland

    21. Mai 2010 um 19:16 Uhr

1375000cookie-checkSpeicher auf Null setzen

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

Privacy policy