@ddddavidee Auch ich habe diesen Blog gefunden, nachdem ich mit so vielen Antworten im Internet unzufrieden war. Nathaniel J. Smith verdient mehr als 100 SO-Punkte für seine Analyse.
calloc() gibt Ihnen einen Null-initialisierten Puffer, während malloc() der Speicher nicht initialisiert wird.
Bei großen Zuweisungen erhalten die meisten callocImplementierungen unter Mainstream-Betriebssystemen Seiten mit bekannten Nullen vom Betriebssystem (z. B. über POSIX mmap(MAP_ANONYMOUS)oder Windows VirtualAlloc), sodass sie nicht im Benutzerbereich geschrieben werden müssen. Auf diese Weise mallocerhält normal auch mehr Seiten vom Betriebssystem. callocnutzt nur die Garantie des Betriebssystems.
Dies bedeutet, dass der callocSpeicher weiterhin "sauber" und träge zugewiesen werden kann und das Kopieren beim Schreiben einer systemweit gemeinsam genutzten physischen Seite mit Nullen zugeordnet werden kann. (Angenommen, ein System mit virtuellem Speicher.)
Einige Compiler können sogar malloc + memset (0) für Sie in calloc optimieren. Sie sollten calloc jedoch explizit verwenden, wenn der Speicher als gelesen werden soll 0 .
Wenn Sie vor dem Schreiben nie Speicher lesen werden, verwenden mallocSie ihn, damit Sie (möglicherweise) schmutzigen Speicher aus der internen freien Liste erhalten, anstatt neue Seiten vom Betriebssystem abzurufen. (Oder anstatt einen Speicherblock auf der freien Liste für eine kleine Zuordnung auf Null zu setzen).
Eingebettete Implementierungen von callockönnen es auf sich callocselbst belassen, wenn kein Betriebssystem vorhanden ist, oder es ist kein ausgefallenes Mehrbenutzer-Betriebssystem, das Seiten auf Null setzt, um Informationslecks zwischen Prozessen zu stoppen.
Unter eingebettetem Linux könnte malloc mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS), das nur für einige eingebettete Kernel aktiviert ist, da es auf einem Mehrbenutzersystem unsicher ist.
Die * Alloc-Varianten sind ziemlich mnemonisch - Clear-Alloc, Memory-Alloc, Re-Alloc.
Cascabel
43
Verwenden Sie malloc (), wenn Sie alles festlegen möchten, was Sie im zugewiesenen Speicherplatz verwenden. Verwenden Sie calloc (), wenn Sie Teile der Daten nicht initialisieren möchten - und es wäre vorteilhaft, wenn die nicht gesetzten Teile auf Null gesetzt würden.
Jonathan Leffler
268
callocist nicht unbedingt teurer, da das Betriebssystem einige Tricks ausführen kann, um es zu beschleunigen. Ich weiß, dass FreeBSD, wenn es eine Leerlauf-CPU-Zeit erhält, diese verwendet, um einen einfachen Prozess auszuführen, der nur herumgeht und freigegebene Speicherblöcke auf Null setzt und Blöcke, die Prozesse verarbeiten, mit einem Flag markiert. Wenn Sie dies tun calloc, wird zuerst versucht, einen solchen vor Null gesetzten Block zu finden und ihn Ihnen einfach zu geben - und höchstwahrscheinlich wird er einen finden.
Pavel Minaev
28
Ich bin der Meinung, dass Ihr Code nicht sicher genug ist, wenn Sie Malloc oder Calloc verwenden, wenn Ihr Code aufgrund von Zuweisungen ohne Initing standardmäßig "sicherer" wird. Die Verwendung von malloc ist ein guter Indikator dafür, dass die Daten initialisiert werden müssen. Ich verwende calloc nur in Fällen, in denen diese 0 Bytes tatsächlich von Bedeutung sind. Beachten Sie auch, dass calloc nicht unbedingt das tut, was Sie für Nicht-Zeichen-Typen denken. Niemand verwendet mehr Trap-Darstellungen oder Nicht-IEEE-Floats, aber das ist keine Entschuldigung dafür, dass Ihr Code wirklich portabel ist, wenn dies nicht der Fall ist.
Steve Jessop
18
@SteveJessop "Safer" ist nicht das richtige Wort. Ich denke, "Deterministisch" ist der bessere Begriff. Code, der deterministischer ist als Fehler, die vom Timing und den Datensequenzen abhängen, lässt sich leichter isolieren. Calloc ist manchmal ein einfacher Weg, um diesen Determinismus gegenüber einer expliziten Initialisierung zu erhalten.
Dennis
362
Ein weniger bekannter Unterschied besteht darin, dass in Betriebssystemen mit optimistischer Speicherzuweisung wie Linux der von zurückgegebene Zeiger mallocnicht durch echten Speicher gesichert wird, bis das Programm ihn tatsächlich berührt.
callocBerührt tatsächlich den Speicher (es schreibt Nullen darauf) und Sie werden sicher sein, dass das Betriebssystem die Zuordnung mit tatsächlichem RAM (oder Swap) unterstützt. Dies ist auch der Grund, warum es langsamer als malloc ist (es muss nicht nur auf Null gesetzt werden, das Betriebssystem muss auch einen geeigneten Speicherbereich finden, indem es möglicherweise andere Prozesse austauscht).
Siehe zum Beispiel diese SO-Frage für weitere Diskussionen über das Verhalten von Malloc
callocmuss keine Nullen schreiben. Wenn der zugewiesene Block hauptsächlich aus neuen Nullseiten besteht, die vom Betriebssystem bereitgestellt werden, können diese unberührt bleiben. Dies muss natürlich callocauf das Betriebssystem abgestimmt sein und nicht auf eine generische Bibliotheksfunktion malloc. Oder ein Implementierer könnte callocjedes Wort mit Null vergleichen, bevor er es auf Null setzt. Dies würde keine Zeit sparen, aber es würde vermeiden, die neuen Seiten zu verschmutzen.
R .. GitHub STOP HELPING ICE
3
@ R .. interessante Anmerkung. Aber gibt es solche Implementierungen in der Praxis in freier Wildbahn?
Isak Savo
10
Bei allen dlmallocähnlichen Implementierungen wird übersprungen, memsetob der Block über mmapneue anonyme Seiten (oder gleichwertige Seiten) abgerufen wurde . Normalerweise wird diese Art der Zuweisung für größere Chunks verwendet, beginnend bei 256 KB oder so. Ich kenne keine Implementierungen, die den Vergleich mit Null durchführen, bevor ich neben meiner eigenen Null schreibe.
R .. GitHub STOP HELPING ICE
1
omallocüberspringt auch die memset; callocmuss niemals Seiten berühren, die noch nicht von der Anwendung verwendet wurden (Seiten-Cache). Obwohl äußerst primitive callocImplementierungen unterscheiden.
Mirabilos
10
Der Calloc von glibc prüft, ob das Betriebssystem neuen Speicher erhält. Wenn ja, weiß es, dass es nicht geschrieben werden muss, da mmap (..., MAP_ANONYMOUS) Speicher zurückgibt, der bereits auf Null gesetzt ist.
Peter Cordes
112
Ein häufig übersehener Vorteil callocist, dass (konforme Implementierungen von) Sie vor Sicherheitslücken durch ganzzahligen Überlauf schützen. Vergleichen Sie:
Ersteres kann zu einer winzigen Zuordnung und nachfolgenden Pufferüberläufen führen, wenn countes größer als ist SIZE_MAX/sizeof *bar. Letzteres schlägt in diesem Fall automatisch fehl, da ein so großes Objekt nicht erstellt werden kann.
Natürlich müssen Sie möglicherweise nach nicht konformen Implementierungen Ausschau halten, bei denen die Möglichkeit eines Überlaufs einfach ignoriert wird. Wenn dies auf Plattformen, auf die Sie abzielen, ein Problem darstellt, müssen Sie ohnehin einen manuellen Test auf Überlauf durchführen.
Anscheinend war der arithmetische Überlauf der Grund für das OpenSSH-Loch im Jahr 2002. Guter Artikel von OpenBSD über die Gefahren dieses Problems mit speicherbezogenen Funktionen: undeadly.org/cgi?action=article&sid=20060330071917
Philip P.
4
@KomradeP.: Interessant. Leider enthält der Artikel, den Sie verlinkt haben, gleich zu Beginn Fehlinformationen. Das Beispiel mit charist kein Überlauf, sondern eine implementierungsdefinierte Konvertierung, wenn das Ergebnis wieder einem charObjekt zugewiesen wird.
R .. GitHub STOP HELPING ICE
Es ist wahrscheinlich nur zu Illustrationszwecken da. Weil der Compiler das wahrscheinlich sowieso weg optimieren wird. Meins kompiliert in diesen Asm: Push 1.
Philip P.
1
@tristopia: Der Punkt ist nicht, dass der Code bei allen Implementierungen ausnutzbar ist, sondern dass er ohne zusätzliche Annahmen falsch ist und daher nicht korrekt / portabel verwendet wird.
R .. GitHub STOP HELPING ICE
3
@tristopia: Wenn Ihre Denkweise " size_t64-Bit ist, das ist also kein Problem" ist, ist dies eine fehlerhafte Denkweise, die zu Sicherheitslücken führen wird. size_tist ein abstrakter Typ, der Größen darstellt, und es gibt keinen Grund zu der Annahme, dass das beliebige Produkt einer 32-Bit-Zahl und eines size_t(Hinweis: sizeof *barkönnte bei einer 64-Bit-C-Implementierung im Prinzip größer als 2 ^ 32 sein!) passt size_t.
R .. GitHub STOP HELPING ICE
37
Die Dokumentation lässt das Calloc wie ein Malloc aussehen, das den Speicher nur auf Null initialisiert. Das ist nicht der Hauptunterschied! Die Idee von Calloc besteht darin, auf die Copy-on-Write-Semantik für die Speicherzuweisung zu verzichten. Wenn Sie mit calloc Speicher zuweisen, werden alle derselben physischen Seite zugeordnet, die auf Null initialisiert wird. Wenn eine der Seiten des zugewiesenen Speichers in eine physische Seite geschrieben wird, wird sie zugewiesen. Dies wird häufig verwendet, um RIESIGE Hash-Tabellen zu erstellen, da beispielsweise die leeren Hash-Teile nicht durch zusätzlichen Speicher (Seiten) gesichert werden. Sie verweisen glücklich auf die einzelne Seite mit der Nullinitialisierung, die sogar von Prozessen gemeinsam genutzt werden kann.
Jedes Schreiben in eine virtuelle Adresse wird einer Seite zugeordnet. Wenn diese Seite die Nullseite ist, wird eine andere physische Seite zugewiesen, die Nullseite wird dort kopiert und der Steuerungsfluss wird an den Clientprozess zurückgegeben. Dies funktioniert genauso wie Speicherzuordnungsdateien, virtueller Speicher usw. Es wird Paging verwendet.
Es gibt keinen Unterschied in der Größe des zugewiesenen Speicherblocks. callocFüllt einfach den Speicherblock mit einem physischen All-Null-Bit-Muster. In der Praxis wird häufig angenommen, dass die Objekte in dem mit zugewiesenen Speicherblock calloceinen Anfangswert haben, als ob sie mit einem Literal initialisiert worden wären 0, dh Ganzzahlen sollten den Wert von 0Gleitkommavariablen - Wert von 0.0Zeigern - den entsprechenden Nullzeigerwert haben , und so weiter.
Aus pedantischer Sicht wird jedoch calloc(wie auch memset(..., 0, ...)) nur garantiert, dass Objekte vom Typ (mit Nullen) ordnungsgemäß initialisiert werden unsigned char. Es wird nicht garantiert, dass alles andere ordnungsgemäß initialisiert wird, und es kann eine sogenannte Trap-Darstellung enthalten , die zu undefiniertem Verhalten führt. Mit anderen Worten, für jeden anderen Typ als unsigned chardas oben erwähnte All-Null-Bit-Muster könnte ein unzulässiger Wert eine Trap-Darstellung darstellen.
Später wurde in einer der technischen Berichtigungen zum C99-Standard das Verhalten für alle Ganzzahltypen definiert (was sinnvoll ist). Das heißt, formal können Sie in der aktuellen C-Sprache nur ganzzahlige Typen mit calloc(und memset(..., 0, ...)) initialisieren . Die Verwendung zum Initialisieren von anderen Elementen im allgemeinen Fall führt aus Sicht der C-Sprache zu undefiniertem Verhalten.
In der Praxis callocfunktioniert, wie wir alle wissen :), aber ob Sie es verwenden möchten (unter Berücksichtigung der oben genannten), liegt bei Ihnen. Ich persönlich bevorzuge es, es vollständig zu vermeiden, mallocstattdessen zu verwenden und meine eigene Initialisierung durchzuführen.
Schließlich ist ein weiteres wichtiges Detail callocerforderlich, um die endgültige Blockgröße intern zu berechnen , indem die Elementgröße mit der Anzahl der Elemente multipliziert wird. Achten Sie dabei callocauf einen möglichen arithmetischen Überlauf. Dies führt zu einer nicht erfolgreichen Zuordnung (Nullzeiger), wenn die angeforderte Blockgröße nicht korrekt berechnet werden kann. In der Zwischenzeit mallocunternimmt Ihre Version keinen Versuch, auf Überlauf zu achten. Für den Fall eines Überlaufs wird eine "unvorhersehbare" Speichermenge zugewiesen.
Gemäß dem Absatz "Ein weiteres wichtiges Detail": Das scheint memset(p, v, n * sizeof type);ein Problem zu sein, da es n * sizeof typemöglicherweise überlaufen kann. Ich denke, ich muss eine for(i=0;i<n;i++) p[i]=v;Schleife für robusten Code verwenden.
chux
Es wäre hilfreich, wenn es ein Standardmittel gäbe, mit dem Code behaupten könnte, dass eine Implementierung All-Bits-Null als Nullzeiger verwenden muss (andernfalls wird die Kompilierung abgelehnt), da es Implementierungen gibt, die andere Nullzeigerdarstellungen verwenden, dies jedoch sind vergleichsweise selten; Code, der auf solchen Implementierungen nicht ausgeführt werden muss, kann schneller sein, wenn Calloc () oder Memset zum Initialisieren von Zeigerarrays verwendet werden können.
Supercat
@chux Nein, wenn ein Array mit nElementen vorhanden ist, bei denen ein Element die Größe hat sizeof type, n*sizeof typekann es nicht überlaufen, da die maximale Größe eines Objekts kleiner als sein muss SIZE_MAX.
12431234123412341234123
@ 12431234123412341234123 Wahr über eine Array Größe <= SIZE_MAX, doch gibt es keine Arrays hier. Der von calloc()can zurückgegebene Zeiger zeigt auf den zugewiesenen Speicher als überschreitet SIZE_MAX. Viele Implementierungen beschränken das Produkt der 2 Argumente auf calloc()aufSIZE_MAX , aber die C - Spezifikation verhängen nicht , dass die Grenze.
Bei der Zuweisung von Speicher mit calloc () wird die angeforderte Speichermenge nicht sofort zugewiesen. Stattdessen werden alle Seiten, die zum Speicherblock gehören, durch eine MMU-Magie mit einer einzelnen Seite verbunden, die alle Nullen enthält (Links unten). Wenn solche Seiten nur gelesen werden (was in der Originalversion des Benchmarks für die Arrays b, c und d zutraf), werden die Daten von der einzelnen Nullseite bereitgestellt, die natürlich in den Cache passt. Soviel zu speichergebundenen Loop-Kerneln. Wenn eine Seite beschrieben wird (egal wie), tritt ein Fehler auf, die "echte" Seite wird zugeordnet und die Nullseite wird in den Speicher kopiert. Dies wird als Copy-on-Write bezeichnet, ein bekannter Optimierungsansatz (den ich in meinen C ++ - Vorlesungen sogar mehrmals unterrichtet habe). Nachdem,
Das ist besser, weil sizeof(Item)es dem Compiler zur Kompilierungszeit bekannt ist und der Compiler es in den meisten Fällen durch die bestmöglichen Anweisungen auf Null Speicher ersetzt. Auf der anderen Seite wird, wenn dies memsetgeschieht calloc, die Parametergröße der Zuordnung nicht im callocCode kompiliert , und es memsetwird häufig real aufgerufen, das normalerweise Code enthält, um Byte für Byte bis zur langen Grenze aufzufüllen, als Zyklus zum Füllen Speicherplatz insizeof(long) Blöcken auffüllen und schließlich byteweise den verbleibenden Speicherplatz ausfüllen. Selbst wenn der Allokator intelligent genug ist, um einige aufzurufen, handelt aligned_memsetes sich immer noch um eine generische Schleife.
Eine bemerkenswerte Ausnahme wäre, wenn Sie malloc / calloc eines sehr großen Speicherblocks (einige Potenzen von zwei Kilobyte) ausführen. In diesem Fall kann die Zuweisung direkt vom Kernel aus erfolgen. Da Betriebssystem-Kernel normalerweise den gesamten Speicher, den sie aus Sicherheitsgründen freigeben, auf Null setzen, gibt Calloc ihn möglicherweise ohne zusätzliche Nullung zurück. Nochmals: Wenn Sie nur etwas zuweisen, von dem Sie wissen, dass es klein ist, sind Sie mit malloc + memset in Bezug auf die Leistung möglicherweise besser dran.
+1 für die Erinnerung, dass eine generische Implementierung einer Funktionalität in einer Systembibliothek nicht unbedingt schneller ist als dieselbe Operation im Benutzercode.
Patrick Schlüter
1
Es gibt auch einen zweiten Punkt, der calloc()langsamer macht als malloc(): die Multiplikation für die Größe. calloc()ist erforderlich, um eine generische Multiplikation zu verwenden (wenn size_t64 Bit sind, sogar die sehr kostspielige 64-Bit * 64-Bit = 64-Bit-Operation), während das malloc () häufig eine Kompilierungszeitkonstante hat.
Patrick Schlüter
4
glibc calloc hat einige Klugheiten, um zu entscheiden, wie der zurückgegebene Block am effizientesten gelöscht werden soll, z. B. muss manchmal nur ein Teil davon gelöscht werden, und es muss auch eine nicht gerollte Löschung bis zu 9 * sizeof (size_t) gelöscht werden. Speicher ist Speicher, das Löschen von 3 Bytes gleichzeitig wird nicht schneller sein, nur weil Sie ihn dann zum Halten verwenden werden struct foo { char a,b,c; };. callocist immer besser als malloc+ memset, wenn Sie immer die gesamte malloced-Region löschen wollen . callochat auch eine sorgfältige, aber effiziente Überprüfung auf int-Überlauf bei Elementen der Größe *.
Peter Cordes
8
Unterschied 1:
malloc() Ordnet normalerweise den Speicherblock zu und es wird ein Speichersegment initialisiert.
calloc() ordnet den Speicherblock zu und initialisiert den gesamten Speicherblock auf 0.
Unterschied 2:
Wenn Sie die malloc()Syntax berücksichtigen , wird nur 1 Argument benötigt. Betrachten Sie das folgende Beispiel:
Und warum unterscheiden Sie data_type und cast_type?
Ausverkauft
7
Es gibt zwei Unterschiede.
Erstens ist in der Anzahl der Argumente. malloc()Nimmt ein einzelnes Argument (Speicher in Bytes erforderlich), während calloc()zwei Argumente benötigt werden.
Zweitens wird der zugewiesene Speicher malloc()nicht initialisiert, während calloc()der zugewiesene Speicher auf NULL initialisiert wird.
calloc()Wenn ein Speicherbereich zugewiesen wird, ist die Länge das Produkt seiner Parameter. callocfüllt den Speicher mit NULL und gibt einen Zeiger auf das erste Byte zurück. Wenn nicht genügend Speicherplatz gefunden wird, wird ein NULLZeiger zurückgegeben.
malloc()ordnet einen einzelnen Speicherblock mit der Größe REQUSTED SIZE zu und gibt einen Zeiger auf das erste Byte zurück. Wenn die erforderliche Speichermenge nicht gefunden werden kann, wird ein Nullzeiger zurückgegeben.
Syntax: ptr_var=(cast_type *)malloc(Size_in_bytes);
Die malloc()Funktion verwendet ein Argument, dh die Anzahl der zuzuordnenden Bytes, während die calloc()Funktion zwei Argumente verwendet, eines die Anzahl der Elemente und das andere die Anzahl der zuzuordnenden Bytes für jedes dieser Elemente. Außerdem calloc()initialisiert den zugewiesenen Speicherplatz auf Nullen, während malloc()dies nicht tut.
malloc()und calloc()sind Funktionen aus der C-Standardbibliothek, die eine dynamische Speicherzuweisung ermöglichen, was bedeutet, dass beide die Speicherzuweisung zur Laufzeit ermöglichen.
Es gibt hauptsächlich zwei Unterschiede zwischen den beiden:
Verhalten: malloc()Weist einen Speicherblock zu, ohne ihn zu initialisieren, und das Lesen des Inhalts dieses Blocks führt zu Müllwerten. calloc()ordnet andererseits einen Speicherblock zu und initialisiert ihn mit Nullen, und das Lesen des Inhalts dieses Blocks führt offensichtlich zu Nullen.
Syntax: malloc()Nimmt 1 Argument (die zuzuweisende Größe) und calloc()zwei Argumente (Anzahl der zuzuordnenden Blöcke und Größe jedes Blocks).
Der Rückgabewert von beiden ist bei Erfolg ein Zeiger auf den zugewiesenen Speicherblock. Andernfalls wird NULL zurückgegeben, um den Speicherzuordnungsfehler anzuzeigen.
Beispiel:
int*arr;// allocate memory for 10 integers with garbage values
arr =(int*)malloc(10*sizeof(int));// allocate memory for 10 integers and sets all of them to 0
arr =(int*)calloc(10,sizeof(int));
Die gleiche Funktionalität wie calloc()mit malloc()und erreicht werden kann memset():
// allocate memory for 10 integers with garbage values
arr=(int*)malloc(10*sizeof(int));// set all of them to 0
memset(arr,0,10*sizeof(int));
Beachten Sie, dass dies malloc()vorzugsweise verwendet wird, calloc()da es schneller ist. Wenn die Werte auf Null initialisiert werden sollen, verwenden Sie calloc()stattdessen.
Ein noch nicht erwähnter Unterschied: Größenbeschränkung
void *malloc(size_t size)kann nur bis zuordnen SIZE_MAX.
void *calloc(size_t nmemb, size_t size);kann etwa zuordnen SIZE_MAX*SIZE_MAX.
Diese Fähigkeit wird auf vielen Plattformen mit linearer Adressierung nicht häufig verwendet. Solche Systeme begrenzen calloc()mitnmemb * size <= SIZE_MAX .
Stellen Sie sich einen Typ von 512 Bytes vor, der aufgerufen wird, disk_sectorund Code möchte viele Sektoren verwenden. Hier kann Code nur bis zu SIZE_MAX/sizeof disk_sectorSektoren verwenden.
Ob ein solches System eine so große Zuordnung liefern kann, ist eine andere Sache. Die meisten heute werden nicht. Es ist jedoch seit vielen Jahren aufgetreten, als SIZE_MAXes 65535 war. Angesichts des Gesetzes von Moore besteht der Verdacht, dass dies um 2030 bei bestimmten Speichermodellen mit SIZE_MAX == 4294967295und Speicherpools in 100 GByte auftreten wird.
Im Allgemeinen kann size_t die Größe der größten Art von Objekt halten, die ein Programm verarbeiten kann. Es ist unwahrscheinlich, dass ein System, bei dem size_t 32 Bit beträgt, eine Zuordnung von mehr als 4294967295 Byte verarbeiten kann, und ein System, das Zuweisungen dieser Größe verarbeiten kann, würde mit ziemlicher Sicherheit size_tgrößer als 32 Bit sein. Die Frage ist nur, ob die Verwendung callocmit Werten, deren Produkt überschreitet SIZE_MAX, zu Null führen kann, anstatt einen Zeiger auf eine kleinere Zuordnung zurückzugeben.
Supercat
Stimmen Sie Ihrer Verallgemeinerung zu , aber die C-Spezifikation erlaubt calloc()Zuordnungen, die überschritten werden SIZE_MAX. Es ist in der Vergangenheit mit 16-Bit size_tpassiert, und da sich der Speicher weiter verbilligt, sehe ich keinen Grund, warum dies in Zukunft nicht passieren kann, auch wenn es nicht üblich ist .
Chux - Wiedereinsetzung Monica
1
Der C-Standard ermöglicht es dem Code, eine Zuordnung anzufordern, deren Größe größer ist SIZE_MAX. Es ist sicherlich nicht erforderlich, dass es einen Umstand gibt, unter dem eine solche Zuweisung erfolgreich sein könnte. Ich bin mir nicht sicher, ob es einen besonderen Vorteil hat, wenn Implementierungen, die solche Zuweisungen nicht verarbeiten können, zurückgegeben werden müssen NULL(insbesondere angesichts der Tatsache, dass einige Implementierungen häufig mallocRückgabezeiger auf Speicherplatz haben, der noch nicht festgeschrieben ist und möglicherweise nicht verfügbar ist, wenn Code tatsächlich versucht, ihn zu verwenden es).
Supercat
Wenn es in der Vergangenheit Systeme gegeben hat, deren verfügbarer Adressierungsbereich die größte darstellbare Ganzzahl überschritten hat, sehe ich keine realistische Möglichkeit, dass dies jemals wieder auftritt, da dies eine Speicherkapazität von Milliarden Gigabyte erfordern würde. Selbst wenn Moores Gesetz weiterhin gelten würde, würde es doppelt so lange dauern, von dem Punkt, an dem 32 Bit nicht mehr ausreichen, von dem Punkt, an dem 64 Bit nicht mehr ausreichen, zu dem Punkt zu gelangen, an dem 32 Bit nicht mehr ausreichen 't.
Supercat
1
Warum sollte eine Implementierung , die eine einzige Zuweisung von mehr als 4G aufnehmen kann nicht definieren size_tzu uint64_t?
Supercat
2
Anzahl der Blöcke:
malloc () weist einen einzelnen Block des angeforderten Speichers zu,
calloc () weist mehrere Blöcke des angeforderten Speichers zu
Initialisierung:
malloc () - löscht und initialisiert den zugewiesenen Speicher nicht.
calloc () - initialisiert den zugewiesenen Speicher mit Null.
Geschwindigkeit:
malloc () ist schnell.
calloc () ist langsamer als malloc ().
Art der Speicherzuordnung:
Die Malloc-Funktion weist dem verfügbaren Heap Speicher der gewünschten 'Größe' zu.
Die Calloc-Funktion weist einen Speicher zu, der der Größe von 'num * size' entspricht.
Bedeutung auf Name:
Der Name Malloc bedeutet "Speicherzuordnung".
Der Name calloc bedeutet "zusammenhängende Zuordnung".
malloc
Familieptr = calloc(MAXELEMS, sizeof(*ptr));
Antworten:
calloc()
gibt Ihnen einen Null-initialisierten Puffer, währendmalloc()
der Speicher nicht initialisiert wird.Bei großen Zuweisungen erhalten die meisten
calloc
Implementierungen unter Mainstream-Betriebssystemen Seiten mit bekannten Nullen vom Betriebssystem (z. B. über POSIXmmap(MAP_ANONYMOUS)
oder WindowsVirtualAlloc
), sodass sie nicht im Benutzerbereich geschrieben werden müssen. Auf diese Weisemalloc
erhält normal auch mehr Seiten vom Betriebssystem.calloc
nutzt nur die Garantie des Betriebssystems.Dies bedeutet, dass der
calloc
Speicher weiterhin "sauber" und träge zugewiesen werden kann und das Kopieren beim Schreiben einer systemweit gemeinsam genutzten physischen Seite mit Nullen zugeordnet werden kann. (Angenommen, ein System mit virtuellem Speicher.)Einige Compiler können sogar malloc + memset (0) für Sie in calloc optimieren. Sie sollten calloc jedoch explizit verwenden, wenn der Speicher als gelesen werden soll
0
.Wenn Sie vor dem Schreiben nie Speicher lesen werden, verwenden
malloc
Sie ihn, damit Sie (möglicherweise) schmutzigen Speicher aus der internen freien Liste erhalten, anstatt neue Seiten vom Betriebssystem abzurufen. (Oder anstatt einen Speicherblock auf der freien Liste für eine kleine Zuordnung auf Null zu setzen).Eingebettete Implementierungen von
calloc
können es auf sichcalloc
selbst belassen, wenn kein Betriebssystem vorhanden ist, oder es ist kein ausgefallenes Mehrbenutzer-Betriebssystem, das Seiten auf Null setzt, um Informationslecks zwischen Prozessen zu stoppen.Unter eingebettetem Linux könnte malloc
mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)
, das nur für einige eingebettete Kernel aktiviert ist, da es auf einem Mehrbenutzersystem unsicher ist.quelle
calloc
ist nicht unbedingt teurer, da das Betriebssystem einige Tricks ausführen kann, um es zu beschleunigen. Ich weiß, dass FreeBSD, wenn es eine Leerlauf-CPU-Zeit erhält, diese verwendet, um einen einfachen Prozess auszuführen, der nur herumgeht und freigegebene Speicherblöcke auf Null setzt und Blöcke, die Prozesse verarbeiten, mit einem Flag markiert. Wenn Sie dies tuncalloc
, wird zuerst versucht, einen solchen vor Null gesetzten Block zu finden und ihn Ihnen einfach zu geben - und höchstwahrscheinlich wird er einen finden.Ein weniger bekannter Unterschied besteht darin, dass in Betriebssystemen mit optimistischer Speicherzuweisung wie Linux der von zurückgegebene Zeiger
malloc
nicht durch echten Speicher gesichert wird, bis das Programm ihn tatsächlich berührt.calloc
Berührt tatsächlich den Speicher (es schreibt Nullen darauf) und Sie werden sicher sein, dass das Betriebssystem die Zuordnung mit tatsächlichem RAM (oder Swap) unterstützt. Dies ist auch der Grund, warum es langsamer als malloc ist (es muss nicht nur auf Null gesetzt werden, das Betriebssystem muss auch einen geeigneten Speicherbereich finden, indem es möglicherweise andere Prozesse austauscht).Siehe zum Beispiel diese SO-Frage für weitere Diskussionen über das Verhalten von Malloc
quelle
calloc
muss keine Nullen schreiben. Wenn der zugewiesene Block hauptsächlich aus neuen Nullseiten besteht, die vom Betriebssystem bereitgestellt werden, können diese unberührt bleiben. Dies muss natürlichcalloc
auf das Betriebssystem abgestimmt sein und nicht auf eine generische Bibliotheksfunktionmalloc
. Oder ein Implementierer könntecalloc
jedes Wort mit Null vergleichen, bevor er es auf Null setzt. Dies würde keine Zeit sparen, aber es würde vermeiden, die neuen Seiten zu verschmutzen.dlmalloc
ähnlichen Implementierungen wird übersprungen,memset
ob der Block übermmap
neue anonyme Seiten (oder gleichwertige Seiten) abgerufen wurde . Normalerweise wird diese Art der Zuweisung für größere Chunks verwendet, beginnend bei 256 KB oder so. Ich kenne keine Implementierungen, die den Vergleich mit Null durchführen, bevor ich neben meiner eigenen Null schreibe.omalloc
überspringt auch diememset
;calloc
muss niemals Seiten berühren, die noch nicht von der Anwendung verwendet wurden (Seiten-Cache). Obwohl äußerst primitivecalloc
Implementierungen unterscheiden.Ein häufig übersehener Vorteil
calloc
ist, dass (konforme Implementierungen von) Sie vor Sicherheitslücken durch ganzzahligen Überlauf schützen. Vergleichen Sie:vs.
Ersteres kann zu einer winzigen Zuordnung und nachfolgenden Pufferüberläufen führen, wenn
count
es größer als istSIZE_MAX/sizeof *bar
. Letzteres schlägt in diesem Fall automatisch fehl, da ein so großes Objekt nicht erstellt werden kann.Natürlich müssen Sie möglicherweise nach nicht konformen Implementierungen Ausschau halten, bei denen die Möglichkeit eines Überlaufs einfach ignoriert wird. Wenn dies auf Plattformen, auf die Sie abzielen, ein Problem darstellt, müssen Sie ohnehin einen manuellen Test auf Überlauf durchführen.
quelle
char
ist kein Überlauf, sondern eine implementierungsdefinierte Konvertierung, wenn das Ergebnis wieder einemchar
Objekt zugewiesen wird.size_t
64-Bit ist, das ist also kein Problem" ist, ist dies eine fehlerhafte Denkweise, die zu Sicherheitslücken führen wird.size_t
ist ein abstrakter Typ, der Größen darstellt, und es gibt keinen Grund zu der Annahme, dass das beliebige Produkt einer 32-Bit-Zahl und einessize_t
(Hinweis:sizeof *bar
könnte bei einer 64-Bit-C-Implementierung im Prinzip größer als 2 ^ 32 sein!) passtsize_t
.Die Dokumentation lässt das Calloc wie ein Malloc aussehen, das den Speicher nur auf Null initialisiert. Das ist nicht der Hauptunterschied! Die Idee von Calloc besteht darin, auf die Copy-on-Write-Semantik für die Speicherzuweisung zu verzichten. Wenn Sie mit calloc Speicher zuweisen, werden alle derselben physischen Seite zugeordnet, die auf Null initialisiert wird. Wenn eine der Seiten des zugewiesenen Speichers in eine physische Seite geschrieben wird, wird sie zugewiesen. Dies wird häufig verwendet, um RIESIGE Hash-Tabellen zu erstellen, da beispielsweise die leeren Hash-Teile nicht durch zusätzlichen Speicher (Seiten) gesichert werden. Sie verweisen glücklich auf die einzelne Seite mit der Nullinitialisierung, die sogar von Prozessen gemeinsam genutzt werden kann.
Jedes Schreiben in eine virtuelle Adresse wird einer Seite zugeordnet. Wenn diese Seite die Nullseite ist, wird eine andere physische Seite zugewiesen, die Nullseite wird dort kopiert und der Steuerungsfluss wird an den Clientprozess zurückgegeben. Dies funktioniert genauso wie Speicherzuordnungsdateien, virtueller Speicher usw. Es wird Paging verwendet.
Hier ist eine Optimierungsgeschichte zum Thema: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/
quelle
Es gibt keinen Unterschied in der Größe des zugewiesenen Speicherblocks.
calloc
Füllt einfach den Speicherblock mit einem physischen All-Null-Bit-Muster. In der Praxis wird häufig angenommen, dass die Objekte in dem mit zugewiesenen Speicherblockcalloc
einen Anfangswert haben, als ob sie mit einem Literal initialisiert worden wären0
, dh Ganzzahlen sollten den Wert von0
Gleitkommavariablen - Wert von0.0
Zeigern - den entsprechenden Nullzeigerwert haben , und so weiter.Aus pedantischer Sicht wird jedoch
calloc
(wie auchmemset(..., 0, ...)
) nur garantiert, dass Objekte vom Typ (mit Nullen) ordnungsgemäß initialisiert werdenunsigned char
. Es wird nicht garantiert, dass alles andere ordnungsgemäß initialisiert wird, und es kann eine sogenannte Trap-Darstellung enthalten , die zu undefiniertem Verhalten führt. Mit anderen Worten, für jeden anderen Typ alsunsigned char
das oben erwähnte All-Null-Bit-Muster könnte ein unzulässiger Wert eine Trap-Darstellung darstellen.Später wurde in einer der technischen Berichtigungen zum C99-Standard das Verhalten für alle Ganzzahltypen definiert (was sinnvoll ist). Das heißt, formal können Sie in der aktuellen C-Sprache nur ganzzahlige Typen mit
calloc
(undmemset(..., 0, ...)
) initialisieren . Die Verwendung zum Initialisieren von anderen Elementen im allgemeinen Fall führt aus Sicht der C-Sprache zu undefiniertem Verhalten.In der Praxis
calloc
funktioniert, wie wir alle wissen :), aber ob Sie es verwenden möchten (unter Berücksichtigung der oben genannten), liegt bei Ihnen. Ich persönlich bevorzuge es, es vollständig zu vermeiden,malloc
stattdessen zu verwenden und meine eigene Initialisierung durchzuführen.Schließlich ist ein weiteres wichtiges Detail
calloc
erforderlich, um die endgültige Blockgröße intern zu berechnen , indem die Elementgröße mit der Anzahl der Elemente multipliziert wird. Achten Sie dabeicalloc
auf einen möglichen arithmetischen Überlauf. Dies führt zu einer nicht erfolgreichen Zuordnung (Nullzeiger), wenn die angeforderte Blockgröße nicht korrekt berechnet werden kann. In der Zwischenzeitmalloc
unternimmt Ihre Version keinen Versuch, auf Überlauf zu achten. Für den Fall eines Überlaufs wird eine "unvorhersehbare" Speichermenge zugewiesen.quelle
memset(p, v, n * sizeof type);
ein Problem zu sein, da esn * sizeof type
möglicherweise überlaufen kann. Ich denke, ich muss einefor(i=0;i<n;i++) p[i]=v;
Schleife für robusten Code verwenden.n
Elementen vorhanden ist, bei denen ein Element die Größe hatsizeof type
,n*sizeof type
kann es nicht überlaufen, da die maximale Größe eines Objekts kleiner als sein mussSIZE_MAX
.SIZE_MAX
, doch gibt es keine Arrays hier. Der voncalloc()
can zurückgegebene Zeiger zeigt auf den zugewiesenen Speicher als überschreitetSIZE_MAX
. Viele Implementierungen beschränken das Produkt der 2 Argumente aufcalloc()
aufSIZE_MAX
, aber die C - Spezifikation verhängen nicht , dass die Grenze.aus einem Artikel Benchmarking-Spaß mit calloc () und null Seiten in Georg Hagers Blog
quelle
calloc
ist in der Regelmalloc+memset
zu 0Es ist im Allgemeinen etwas besser,
malloc+memset
explizit zu verwenden , insbesondere wenn Sie etwas tun wie:Das ist besser, weil
sizeof(Item)
es dem Compiler zur Kompilierungszeit bekannt ist und der Compiler es in den meisten Fällen durch die bestmöglichen Anweisungen auf Null Speicher ersetzt. Auf der anderen Seite wird, wenn diesmemset
geschiehtcalloc
, die Parametergröße der Zuordnung nicht imcalloc
Code kompiliert , und esmemset
wird häufig real aufgerufen, das normalerweise Code enthält, um Byte für Byte bis zur langen Grenze aufzufüllen, als Zyklus zum Füllen Speicherplatz insizeof(long)
Blöcken auffüllen und schließlich byteweise den verbleibenden Speicherplatz ausfüllen. Selbst wenn der Allokator intelligent genug ist, um einige aufzurufen, handeltaligned_memset
es sich immer noch um eine generische Schleife.Eine bemerkenswerte Ausnahme wäre, wenn Sie malloc / calloc eines sehr großen Speicherblocks (einige Potenzen von zwei Kilobyte) ausführen. In diesem Fall kann die Zuweisung direkt vom Kernel aus erfolgen. Da Betriebssystem-Kernel normalerweise den gesamten Speicher, den sie aus Sicherheitsgründen freigeben, auf Null setzen, gibt Calloc ihn möglicherweise ohne zusätzliche Nullung zurück. Nochmals: Wenn Sie nur etwas zuweisen, von dem Sie wissen, dass es klein ist, sind Sie mit malloc + memset in Bezug auf die Leistung möglicherweise besser dran.
quelle
calloc()
langsamer macht alsmalloc()
: die Multiplikation für die Größe.calloc()
ist erforderlich, um eine generische Multiplikation zu verwenden (wennsize_t
64 Bit sind, sogar die sehr kostspielige 64-Bit * 64-Bit = 64-Bit-Operation), während das malloc () häufig eine Kompilierungszeitkonstante hat.struct foo { char a,b,c; };
.calloc
ist immer besser alsmalloc
+memset
, wenn Sie immer die gesamtemalloc
ed-Region löschen wollen .calloc
hat auch eine sorgfältige, aber effiziente Überprüfung auf int-Überlauf bei Elementen der Größe *.Unterschied 1:
malloc()
Ordnet normalerweise den Speicherblock zu und es wird ein Speichersegment initialisiert.calloc()
ordnet den Speicherblock zu und initialisiert den gesamten Speicherblock auf 0.Unterschied 2:
Wenn Sie die
malloc()
Syntax berücksichtigen , wird nur 1 Argument benötigt. Betrachten Sie das folgende Beispiel:Beispiel: Wenn Sie 10 Speicherblöcke für den Typ int zuweisen möchten,
Wenn Sie die
calloc()
Syntax berücksichtigen , werden 2 Argumente benötigt. Betrachten Sie das folgende Beispiel:Beispiel: Wenn Sie 10 Speicherblöcke für den Typ int zuweisen und all das auf NULL initialisieren möchten,
Ähnlichkeit:
Beide
malloc()
undcalloc()
geben standardmäßig void * zurück, wenn sie nicht typisiert sind.!quelle
Es gibt zwei Unterschiede.
Erstens ist in der Anzahl der Argumente.
malloc()
Nimmt ein einzelnes Argument (Speicher in Bytes erforderlich), währendcalloc()
zwei Argumente benötigt werden.Zweitens wird der zugewiesene Speicher
malloc()
nicht initialisiert, währendcalloc()
der zugewiesene Speicher auf NULL initialisiert wird.calloc()
Wenn ein Speicherbereich zugewiesen wird, ist die Länge das Produkt seiner Parameter.calloc
füllt den Speicher mit NULL und gibt einen Zeiger auf das erste Byte zurück. Wenn nicht genügend Speicherplatz gefunden wird, wird einNULL
Zeiger zurückgegeben.Syntax:
ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block);
dhptr_var=(type *)calloc(n,s);
malloc()
ordnet einen einzelnen Speicherblock mit der Größe REQUSTED SIZE zu und gibt einen Zeiger auf das erste Byte zurück. Wenn die erforderliche Speichermenge nicht gefunden werden kann, wird ein Nullzeiger zurückgegeben.Syntax:
ptr_var=(cast_type *)malloc(Size_in_bytes);
Diemalloc()
Funktion verwendet ein Argument, dh die Anzahl der zuzuordnenden Bytes, während diecalloc()
Funktion zwei Argumente verwendet, eines die Anzahl der Elemente und das andere die Anzahl der zuzuordnenden Bytes für jedes dieser Elemente. Außerdemcalloc()
initialisiert den zugewiesenen Speicherplatz auf Nullen, währendmalloc()
dies nicht tut.quelle
Die
calloc()
im<stdlib.h>
Header deklarierte Funktion bietet gegenüber dermalloc()
Funktion einige Vorteile .quelle
malloc()
undcalloc()
sind Funktionen aus der C-Standardbibliothek, die eine dynamische Speicherzuweisung ermöglichen, was bedeutet, dass beide die Speicherzuweisung zur Laufzeit ermöglichen.Ihre Prototypen sind wie folgt:
Es gibt hauptsächlich zwei Unterschiede zwischen den beiden:
Verhalten:
malloc()
Weist einen Speicherblock zu, ohne ihn zu initialisieren, und das Lesen des Inhalts dieses Blocks führt zu Müllwerten.calloc()
ordnet andererseits einen Speicherblock zu und initialisiert ihn mit Nullen, und das Lesen des Inhalts dieses Blocks führt offensichtlich zu Nullen.Syntax:
malloc()
Nimmt 1 Argument (die zuzuweisende Größe) undcalloc()
zwei Argumente (Anzahl der zuzuordnenden Blöcke und Größe jedes Blocks).Der Rückgabewert von beiden ist bei Erfolg ein Zeiger auf den zugewiesenen Speicherblock. Andernfalls wird NULL zurückgegeben, um den Speicherzuordnungsfehler anzuzeigen.
Beispiel:
Die gleiche Funktionalität wie
calloc()
mitmalloc()
und erreicht werden kannmemset()
:Beachten Sie, dass dies
malloc()
vorzugsweise verwendet wird,calloc()
da es schneller ist. Wenn die Werte auf Null initialisiert werden sollen, verwenden Siecalloc()
stattdessen.quelle
Ein noch nicht erwähnter Unterschied: Größenbeschränkung
void *malloc(size_t size)
kann nur bis zuordnenSIZE_MAX
.void *calloc(size_t nmemb, size_t size);
kann etwa zuordnenSIZE_MAX*SIZE_MAX
.Diese Fähigkeit wird auf vielen Plattformen mit linearer Adressierung nicht häufig verwendet. Solche Systeme begrenzen
calloc()
mitnmemb * size <= SIZE_MAX
.Stellen Sie sich einen Typ von 512 Bytes vor, der aufgerufen wird,
disk_sector
und Code möchte viele Sektoren verwenden. Hier kann Code nur bis zuSIZE_MAX/sizeof disk_sector
Sektoren verwenden.Beachten Sie Folgendes, was eine noch größere Zuordnung ermöglicht.
Ob ein solches System eine so große Zuordnung liefern kann, ist eine andere Sache. Die meisten heute werden nicht. Es ist jedoch seit vielen Jahren aufgetreten, als
SIZE_MAX
es 65535 war. Angesichts des Gesetzes von Moore besteht der Verdacht, dass dies um 2030 bei bestimmten Speichermodellen mitSIZE_MAX == 4294967295
und Speicherpools in 100 GByte auftreten wird.quelle
size_t
größer als 32 Bit sein. Die Frage ist nur, ob die Verwendungcalloc
mit Werten, deren Produkt überschreitetSIZE_MAX
, zu Null führen kann, anstatt einen Zeiger auf eine kleinere Zuordnung zurückzugeben.calloc()
Zuordnungen, die überschritten werdenSIZE_MAX
. Es ist in der Vergangenheit mit 16-Bitsize_t
passiert, und da sich der Speicher weiter verbilligt, sehe ich keinen Grund, warum dies in Zukunft nicht passieren kann, auch wenn es nicht üblich ist .SIZE_MAX
. Es ist sicherlich nicht erforderlich, dass es einen Umstand gibt, unter dem eine solche Zuweisung erfolgreich sein könnte. Ich bin mir nicht sicher, ob es einen besonderen Vorteil hat, wenn Implementierungen, die solche Zuweisungen nicht verarbeiten können, zurückgegeben werden müssenNULL
(insbesondere angesichts der Tatsache, dass einige Implementierungen häufigmalloc
Rückgabezeiger auf Speicherplatz haben, der noch nicht festgeschrieben ist und möglicherweise nicht verfügbar ist, wenn Code tatsächlich versucht, ihn zu verwenden es).size_t
zuuint64_t
?Anzahl der Blöcke:
malloc () weist einen einzelnen Block des angeforderten Speichers zu,
calloc () weist mehrere Blöcke des angeforderten Speichers zu
Initialisierung:
malloc () - löscht und initialisiert den zugewiesenen Speicher nicht.
calloc () - initialisiert den zugewiesenen Speicher mit Null.
Geschwindigkeit:
malloc () ist schnell.
calloc () ist langsamer als malloc ().
Argumente & Syntax:
malloc () akzeptiert 1 Argument:
Bytes
calloc () akzeptiert 2 Argumente:
Länge
Art der Speicherzuordnung:
Die Malloc-Funktion weist dem verfügbaren Heap Speicher der gewünschten 'Größe' zu.
Die Calloc-Funktion weist einen Speicher zu, der der Größe von 'num * size' entspricht.
Bedeutung auf Name:
Der Name Malloc bedeutet "Speicherzuordnung".
Der Name calloc bedeutet "zusammenhängende Zuordnung".
quelle