Warum initialisiert malloc die Werte in gcc auf 0?

Lesezeit: 8 Minuten

Benutzeravatar von SHH
SCHH

Vielleicht ist es von Plattform zu Plattform unterschiedlich, aber

Wenn ich mit gcc kompiliere und den folgenden Code ausführe, erhalte ich jedes Mal 0 in meinem Ubuntu 11.10.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    double *a = malloc(sizeof(double)*100)
    printf("%f", *a);
}

Warum verhalten sich malloc so, obwohl es calloc gibt?

Bedeutet das nicht, dass es einen unerwünschten Leistungsaufwand gibt, nur um die Werte auf 0 zu initialisieren, auch wenn Sie es manchmal nicht wollen?


BEARBEITEN: Oh, mein vorheriges Beispiel hat nicht initialisiert, sondern zufällig einen “frischen” Block verwendet.

Was ich genau gesucht habe, war, warum es initialisiert wird, wenn es einen großen Block zuweist:

int main()
{
    int *a = malloc(sizeof(int)*200000);
    a[10] = 3;
    printf("%d", *(a+10));

    free(a);

    a = malloc(sizeof(double)*200000);
    printf("%d", *(a+10));
}

OUTPUT: 3
        0 (initialized)

Aber danke für den Hinweis, dass es beim Mallocing einen SICHERHEITSgrund gibt! (Habe niemals drüber nachgedacht). Sicher, es muss auf Null initialisiert werden, wenn ein frischer Block oder der große Block zugewiesen wird.

  • Haben Sie für einen realistischeren Test versucht, zuzuweisen, freizugeben und dann erneut zuzuweisen (möglicherweise jedes Mal mehrmals wiederholen)? Nur weil malloc beim ersten Mal einen nullinitialisierten Speicher zurückgibt, bedeutet das nicht, dass Sie sich im Allgemeinen darauf verlassen können.

    – Benutzer786653

    6. November 2011 um 19:08 Uhr

  • Es könnte auch sein, dass der Speicher vom Betriebssystem oder so auf 0 gesetzt wurde und malloc hatte nichts damit zu tun.

    – Seth Carnegie

    6. November 2011 um 19:09 Uhr


  • Gießen Sie nicht das Ergebnis von malloc in C

    – phuklv

    16. Oktober 2017 um 10:24 Uhr

Benutzeravatar von Mystical
Mystisch

Kurze Antwort:

Das tut es nicht, es ist in Ihrem Fall einfach null.
(Außerdem zeigt Ihr Testfall nicht, dass die Daten Null sind. Es zeigt nur, wenn ein Element Null ist.)


Lange Antwort:

Wenn du anrufst malloc()wird eines von zwei Dingen passieren:

  1. Es recycelt Speicher, der zuvor zugewiesen und von demselben Prozess befreit wurde.
  2. Es fordert neue Seite(n) vom Betriebssystem an.

Im ersten Fall enthält der Speicher Daten, die von früheren Zuordnungen übrig geblieben sind. Es wird also nicht null sein. Dies ist der übliche Fall, wenn kleine Allokationen durchgeführt werden.

Im zweiten Fall stammt der Speicher vom Betriebssystem. Dies geschieht, wenn dem Programm der Speicher ausgeht – oder wenn Sie eine sehr große Zuweisung anfordern. (so wie in deinem Beispiel)

Hier ist der Haken: Speicher, der vom Betriebssystem kommt, wird auf Null gesetzt Sicherheit Gründe dafür.*

Wenn das Betriebssystem Ihnen Speicher zur Verfügung stellt, könnte es von einem anderen Prozess befreit worden sein. Dieser Speicher könnte also sensible Informationen wie ein Passwort enthalten. Um zu verhindern, dass Sie solche Daten lesen, wird das Betriebssystem sie auf Null setzen, bevor es sie Ihnen gibt.

*Ich stelle fest, dass die C-Norm dazu nichts sagt. Dies ist ausschließlich ein Betriebssystemverhalten. Diese Nullstellung kann also auf Systemen vorhanden sein, bei denen die Sicherheit keine Rolle spielt, oder auch nicht.


Um mehr über die Leistung zu erfahren:

Als @R. erwähnt in den Kommentaren, warum Sie diese Nullung immer verwenden sollten calloc() Anstatt von malloc() + memset(). calloc() kann diese Tatsache ausnutzen, um eine Trennung zu vermeiden memset().


Andererseits ist dieses Nullstellen manchmal ein Leistungsengpass. In einigen numerischen Anwendungen (wie z Out-of-place-FFT), müssen Sie einen großen Teil des Scratch-Speichers zuweisen. Verwenden Sie es, um einen beliebigen Algorithmus auszuführen, und geben Sie es dann frei.

In diesen Fällen ist das Nullstellen unnötig und bedeutet reinen Overhead.

Das extremste Beispiel, das ich gesehen habe, ist ein 20-sekündiger Zeroing-Overhead für einen 70-sekündigen Vorgang mit einem 48-GB-Scratch-Puffer. (Etwa 30 % Overhead.)
(Zugegeben: Die Maschine hatte einen Mangel an Speicherbandbreite.)

Die offensichtliche Lösung besteht darin, den Speicher einfach manuell wiederzuverwenden. Doch dafür müssen oft etablierte Schnittstellen durchbrochen werden. (insbesondere wenn es Teil einer Bibliotheksroutine ist)

  • Aber du still Sie können sich nicht darauf verlassen, dass es Null ist, es sei denn, Sie tun dies selbst (oder mit callocdas dies für Sie erledigt, nachdem Sie Speicher vom Betriebssystem erhalten haben).

    – Greg Hewgill

    6. November 2011 um 19:20 Uhr


  • Danke für deine Antwort. Hätte nie gedacht, dass es beim Mallocing ein Sicherheitsproblem geben würde!

    – SCHH

    6. November 2011 um 19:28 Uhr

  • Es ist subtil. Wenn das Betriebssystem Ihnen Speicher zur Verfügung stellt, könnte es von einem anderen Prozess befreit worden sein. Dieser Speicher könnte also sensible Informationen wie ein Passwort enthalten. Um zu verhindern, dass Sie solche Daten lesen, wird das Betriebssystem sie auf Null setzen, bevor es sie Ihnen gibt. Dies ist jedoch ein Implementierungsdetail und kann anders sein, z. B. in einigen eingebetteten Systemen.

    – Mystisch

    6. November 2011 um 19:34 Uhr

  • Dies ist ein bisschen neben der Frage von OP, aber eine Konsequenz dieses Effekts ist, dass Sie immer verwenden sollten calloc statt malloc+memset wenn Sie einen nullinitialisierten Speicher wünschen (zumindest für große Blöcke, bei denen die Zeit bis null eine Rolle spielen könnte). malloc+memset wird immer hohe Kosten für das Schreiben in den gesamten Block entstehen, aber die des Systems calloc kann die Tatsache ausnutzen, dass neuer anonymer Speicher zunächst mit Nullen gefüllt wird.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    6. November 2011 um 21:16 Uhr

  • Die Antworten in dieser Frage können Ihnen helfen, das zu verstehen. Der Kernel kann mit calloc schummeln, indem er nicht wirklich alle genullten Seiten ausschreibt, bis sie verwendet werden. Memset erzwingt (scheinbar) das sofortige Ausschreiben der Seiten. Mehr Infos unter dem Link.

    – Thomas Rutter

    7. November 2011 um 4:50 Uhr


Das Betriebssystem löscht normalerweise neue Speicherseiten, die es an Ihren Prozess sendet, damit es die Daten eines älteren Prozesses nicht einsehen kann. Das bedeutet, dass das erste Mal, wenn Sie eine Variable initialisieren (oder irgendetwas mallocieren), sie oft Null ist, aber wenn Sie diesen Speicher jemals wiederverwenden (indem Sie ihn zum Beispiel freigeben und erneut mallocen), sind alle Wetten ungültig.

Diese Inkonsistenz ist genau der Grund, warum nicht initialisierte Variablen ein so schwer zu findender Fehler sind.


Was die unerwünschten Performance-Overheads angeht, die Vermeidung von unspezifischem Verhalten ist wahrscheinlich wichtiger. Welche kleine Leistungssteigerung Sie in diesem Fall erzielen könnten, wird die schwer zu findenden Fehler nicht kompensieren, mit denen Sie sich auseinandersetzen müssen, wenn jemand den Code leicht modifiziert (vorherige Annahmen bricht) oder ihn auf ein anderes System portiert (wo die Annahmen möglicherweise ungültig waren). an erster Stelle).

  • +1 … nicht sicher, ob “wahrscheinlich” im fettgedruckten Textgedanken erforderlich ist 😉

    Benutzer166390

    6. November 2011 um 19:25 Uhr

Benutzeravatar von Dan Aloni
Dan Aloni

Warum nehmen Sie das an malloc() initialisiert auf null? Es passiert einfach so, dass das der erste Anruf ist malloc() führt zu einem Anruf an sbrk oder mmap Systemaufrufe, die eine Speicherseite vom Betriebssystem zuweisen. Das Betriebssystem ist aus Sicherheitsgründen verpflichtet, nullinitialisierten Speicher bereitzustellen (sonst werden Daten von anderen Prozessen sichtbar!). Sie könnten also denken, dass das Betriebssystem Zeit damit verschwendet, die Seite auf Null zu setzen. Aber nein! In Linux gibt es eine spezielle systemweite Singleton-Seite namens „Nullseite“, und diese Seite wird als Copy-On-Write abgebildet, was bedeutet, dass das Betriebssystem nur dann eine andere Seite zuweist, wenn Sie tatsächlich auf diese Seite schreiben initialisieren Sie es. Ich hoffe, das beantwortet Ihre Frage zur Leistung. Das Speicher-Paging-Modell ermöglicht eine Art träge Nutzung des Speichers, indem es die Fähigkeit der mehrfachen Zuordnung derselben Seite unterstützt, plus die Fähigkeit, den Fall zu handhaben, wenn der erste Schreibvorgang auftritt.

Wenn Sie anrufen free()das glibc allocator wird die Region zu seinen freien Listen zurückgeben, und wann malloc() erneut aufgerufen wird, erhalten Sie möglicherweise dieselbe Region, aber mit den vorherigen Daten. Letztlich, free() kann den Speicher an das Betriebssystem zurückgeben, indem Systemaufrufe erneut aufgerufen werden.

Beachten Sie, dass die glibc Manpage an malloc() besagt streng, dass der Speicher nicht gelöscht wird, sodass Sie aufgrund des “Vertrags” auf der API nicht davon ausgehen können, dass er gelöscht wird. Hier der Originalauszug:

malloc() weist Größenbytes zu und gibt einen Zeiger auf den zugeordneten Speicher zurück.
Der Speicher wird nicht gelöscht. Wenn size 0 ist, gibt malloc() entweder NULL oder einen eindeutigen Zeigerwert zurück, der später erfolgreich an free() übergeben werden kann.

Wenn Sie möchten, können Sie mehr über diese Dokumentation lesen, wenn Sie sich Sorgen über die Leistung oder andere Nebenwirkungen machen.

Ich habe Ihr Beispiel so geändert, dass es 2 identische Zuordnungen enthält. Jetzt ist es leicht zu sehen malloc initialisiert den Speicher nicht mit Null.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    {
      double *a = malloc(sizeof(double)*100);
      *a = 100;
      printf("%f\n", *a);
      free(a);
    }
    {
      double *a = malloc(sizeof(double)*100);
      printf("%f\n", *a);
      free(a);
    }

    return 0;
}

Ausgabe mit gcc 4.3.4

100.000000
100.000000

Aus gnu.org:

Sehr große Blöcke (viel größer als eine Seite) werden mit mmap (anonymous oder über /dev/zero) durch diese Implementierung.

  • Das OP mallociert jedoch in kleinen Schritten. Hat die Referenz, die Sie gefunden haben, auch etwas darüber?

    – Umarmung

    6. November 2011 um 19:18 Uhr

Das schreibt die Norm nicht vor malloc() sollte die Werte auf Null initialisieren. Es passiert einfach auf Ihrer Plattform, dass es auf Null gesetzt ist oder in dem bestimmten Moment, in dem Sie diesen Wert lesen, Null war.

  • Das OP mallociert jedoch in kleinen Schritten. Hat die Referenz, die Sie gefunden haben, auch etwas darüber?

    – Umarmung

    6. November 2011 um 19:18 Uhr

Benutzeravatar von TonyK
ToniK

Dein Code zeigt das nicht malloc initialisiert seinen Speicher auf 0. Das könnte vom Betriebssystem erledigt werden, bevor das Programm startet. Um zu sehen, ob dies der Fall ist, schreiben Sie einen anderen Wert in den Speicher, geben Sie ihn frei und rufen Sie malloc erneut auf. Sie werden wahrscheinlich dieselbe Adresse erhalten, aber Sie müssen dies überprüfen. Wenn ja, können Sie nachsehen, was es enthält. Lass uns wissen!

1419380cookie-checkWarum initialisiert malloc die Werte in gcc auf 0?

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

Privacy policy