Vielleicht ist es von Plattform zu Plattform unterschiedlich, aber
Wenn ich mit gcc kompiliere und den folgenden Code ausführe, erhalte ich in meinem Ubuntu 11.10 jedes Mal 0.
#include <stdio.h>
#include <stdlib.h>
int main()
{
double *a = (double*) malloc(sizeof(double)*100)
printf("%f", *a);
}
Warum verhält 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 dies manchmal nicht möchten?
EDIT: Oh, mein vorheriges Beispiel war nicht Initiazling, sondern verwendete zufällig "frischen" Block.
Was ich genau gesucht habe, war, warum es initialisiert wird, wenn es einen großen Block zuweist:
int main()
{
int *a = (int*) malloc(sizeof(int)*200000);
a[10] = 3;
printf("%d", *(a+10));
free(a);
a = (double*) malloc(sizeof(double)*200000);
printf("%d", *(a+10));
}
OUTPUT: 3
0 (initialized)
Aber danke, dass Sie darauf hingewiesen haben, dass es beim Mallocing einen SICHERHEITSGRUND gibt! (Habe niemals drüber nachgedacht). Sicher muss es auf Null initialisiert werden, wenn ein neuer Block oder der große Block zugewiesen wird.
malloc
nichts damit zu tun hat.malloc
Antworten:
Kurze Antwort:
Es ist nicht so, es ist einfach Null in Ihrem Fall.
(Auch Ihr Testfall zeigt nicht, dass die Daten Null sind. Er zeigt nur, wenn ein Element Null ist.)
Lange Antwort:
Wenn Sie anrufen
malloc()
, geschieht eines von zwei Dingen:Im ersten Fall enthält der Speicher Datenreste aus früheren Zuordnungen. Es wird also nicht Null sein. Dies ist der übliche Fall bei kleinen Zuordnungen.
Im zweiten Fall stammt der Speicher vom Betriebssystem. Dies geschieht, wenn dem Programm der Speicher ausgeht - oder wenn Sie eine sehr große Zuordnung anfordern. (wie in Ihrem Beispiel)
Hier ist der Haken: Der vom Betriebssystem kommende Speicher wird aus Sicherheitsgründen auf Null gesetzt . *
Wenn das Betriebssystem Speicher bereitstellt, wurde es möglicherweise von einem anderen Prozess befreit. Dieser Speicher kann also vertrauliche Informationen wie ein Kennwort enthalten. Um zu verhindern, dass Sie solche Daten lesen, setzt das Betriebssystem sie auf Null, bevor es sie Ihnen gibt.
* Ich stelle fest, dass der C-Standard nichts darüber aussagt. Dies ist ausschließlich ein Betriebssystemverhalten. Diese Nullung kann also auf Systemen vorhanden sein, auf denen die Sicherheit keine Rolle spielt.
Um mehr Performance-Hintergrund zu geben:
Als @R. den Kommentaren erwähnt in dieser Nullung Deshalb sollten Sie immer verwenden
calloc()
anstelle vonmalloc()
+memset()
.calloc()
kann diese Tatsache ausnutzen, um eine separate zu vermeidenmemset()
.Andererseits ist diese Nullung manchmal ein Leistungsengpass. In einigen numerischen Anwendungen (z. B. der fehlenden FFT ) müssen Sie einen großen Teil des Arbeitsspeichers zuweisen. Verwenden Sie es, um einen beliebigen Algorithmus auszuführen, und geben Sie ihn dann frei.
In diesen Fällen ist das Nullstellen nicht erforderlich und entspricht einem reinen Overhead.
Das extremste Beispiel, das ich gesehen habe, ist ein Null-Overhead von 20 Sekunden für einen 70-Sekunden-Vorgang mit einem 48-GB-Arbeitspuffer. (Ungefähr 30% Overhead.) (Zugegeben: Der Computer hatte einen Mangel an Speicherbandbreite.)
Die naheliegende Lösung besteht darin, den Speicher einfach manuell wiederzuverwenden. Dies erfordert jedoch häufig das Durchbrechen etablierter Schnittstellen. (besonders wenn es Teil einer Bibliotheksroutine ist)
quelle
calloc
, was dies für Sie erledigt, nachdem Sie Speicher vom Betriebssystem erhalten haben).calloc
eher alsmalloc
+ verwenden sollten,memset
wenn Sie null initialisierten Speicher möchten (zumindest für große Blöcke, bei denen die Zeit bis Null von Bedeutung sein könnte).malloc
+memset
verursacht immer hohe Kosten für das Schreiben in den gesamten Block, aber das Systemcalloc
kann die Tatsache ausnutzen, dass der neue anonyme Speicher zunächst mit Null gefüllt wird.Das Betriebssystem löscht normalerweise neue Speicherseiten, die es an Ihren Prozess sendet, damit es die Daten eines älteren Prozesses nicht anzeigen kann. Dies bedeutet, dass beim ersten Initialisieren einer Variablen (oder eines Mallocs) diese häufig Null ist. Wenn Sie diesen Speicher jedoch jemals wiederverwenden (z. B. durch Freigeben und erneutes Mallocing), sind alle Wetten ungültig.
Diese Inkonsistenz ist genau der Grund, warum nicht initialisierte Variablen so schwer zu finden sind.
In Bezug auf den unerwünschten Leistungsaufwand ist es wahrscheinlich wichtiger, nicht angegebenes Verhalten zu vermeiden . Unabhängig davon, welchen kleinen Leistungsschub Sie in diesem Fall erzielen könnten, werden die schwer zu findenden Fehler, mit denen Sie sich befassen müssen, nicht kompensiert, wenn jemand die Codes geringfügig ändert (frühere Annahmen verletzt) oder sie auf ein anderes System portiert (bei dem die Annahmen möglicherweise ungültig waren) an erster Stelle).
quelle
Warum wird das
malloc()
auf Null initialisiert? Es ist einfach so, dass der erste Aufruf vonmalloc()
zu einem Aufruf vonsbrk
oder führtmmap
Systemaufrufe, die eine Seite des Speichers vom Betriebssystem zuweisen. Das Betriebssystem ist aus Sicherheitsgründen verpflichtet, nullinitialisierten Speicher bereitzustellen (andernfalls werden Daten aus anderen Prozessen sichtbar!). Sie könnten also denken, dass das Betriebssystem Zeit damit verschwendet, die Seite auf Null zu setzen. Aber nein! Unter Linux gibt es eine spezielle systemweite Singleton-Seite namens "Zero Page". Diese Seite wird als "Copy-On-Write" zugeordnet. Dies bedeutet, dass das Betriebssystem nur dann eine weitere Seite und zuweist, wenn Sie tatsächlich auf diese Seite schreiben initialisiere es. Ich hoffe, dies beantwortet Ihre Frage zur Leistung. Das Speicher-Paging-Modell ermöglicht eine verzögerte Speichernutzung, indem es die Möglichkeit der Mehrfachzuordnung derselben Seite sowie die Möglichkeit unterstützt, den Fall beim ersten Schreiben zu behandeln.Wenn Sie anrufen
free()
,glibc
kehrt der Allokator die Region zu seinen freien Listen zurück. Wenn Siemalloc()
erneut aufgerufen werden, erhalten Sie möglicherweise dieselbe Region, die jedoch mit den vorherigen Daten verschmutzt ist. Schließlichfree()
könnte die Erinnerung an das Betriebssystem zurückkehren , indem erneut Systemaufrufe aufruft.Beachten Sie, dass der
glibc
Mann Seite aufmalloc()
streng sagt , dass der Speicher nicht gelöscht wird, so durch den „Vertrag“ auf der API können Sie nicht davon ausgehen , dass es gelöscht wird erhalten. Hier ist der Originalauszug:Wenn Sie möchten, können Sie mehr über diese Dokumentation lesen, wenn Sie sich Sorgen über die Leistung oder andere Nebenwirkungen machen.
quelle
Ich habe Ihr Beispiel so geändert, dass es zwei identische Zuordnungen enthält. Jetzt ist es leicht zu erkennen,
malloc
dass der Speicher nicht auf Null gesetzt wird.#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
quelle
Von gnu.org :
quelle
Der Standard schreibt nicht vor, dass
malloc()
die Werte auf Null initialisiert werden sollen. Auf Ihrer Plattform kann es nur vorkommen, dass sie auf Null gesetzt wird oder zu dem Zeitpunkt, zu dem Sie diesen Wert gelesen haben, auf Null gesetzt wurde.quelle
Ihr Code zeigt nicht an, dass
malloc
sein Speicher auf 0 initialisiert wird. Dies kann vom Betriebssystem durchgeführt werden, bevor das Programm gestartet wird. Um zu sehen, was der Fall ist, schreiben Sie einen anderen Wert in den Speicher, geben Sie ihn frei und rufen Sie malloc erneut auf. Sie werden wahrscheinlich die gleiche Adresse erhalten, müssen dies jedoch überprüfen. Wenn ja, können Sie sehen, was es enthält. Lass uns wissen!quelle
Wissen Sie, dass es definitiv initialisiert wird? Ist es möglich, dass der von malloc () zurückgegebene Bereich am Anfang nur häufig 0 hat?
quelle
Verlassen Sie sich niemals darauf, dass ein Compiler Code generiert, der den Speicher für irgendetwas initialisiert. malloc gibt einfach einen Zeiger auf n Bytes Speicher an einem Ort zurück, an dem es sich möglicherweise sogar um einen Swap handelt.
Wenn der Inhalt des Speichers kritisch ist, initialisieren Sie ihn selbst.
quelle