Ich bin ein Anfänger-C-Programmierer und habe angenommen, dass dies der Fall wäre, möchte aber, wenn möglich, eine Bestätigung.
Wenn sie gleich sind, warum nehmen Sie stattdessen nicht einfach ein Argument?
Ali
Ich bin ein Anfänger-C-Programmierer und habe angenommen, dass dies der Fall wäre, möchte aber, wenn möglich, eine Bestätigung.
Wenn sie gleich sind, warum nehmen Sie stattdessen nicht einfach ein Argument?
paxdiablo
Die Leute verwenden meistens Zuweisungsroutinen, um Speicherplatz für eine festgelegte Anzahl von Elementen zuzuweisen calloc()
lässt sich gut spezifizieren. Wenn Sie also beispielsweise Platz für 100 Integer oder 20 Ihrer eigenen Struktur benötigen:
int *pInt = calloc (100, sizeof(int));
tMyStruct *pMyStruct = calloc (20, sizeof(tMyStruct));
Dieser Code sieht tatsächlich etwas “schöner” aus als das Äquivalent malloc()
Anrufe:
int *pInt = malloc (100 * sizeof(int));
tMyStruct *pMyStruct = malloc (20 * sizeof(tMyStruct));
obwohl es für erfahrene C-Programmierer keinen wirklichen Unterschied gibt (außer natürlich der Nullinitialisierung).
Ich muss sagen, ich habe noch nie verwendet calloc in freier Wildbahn, da ich fast immer eine erstelle struct
wo Nullen keinen Sinn machen. Ich ziehe es vor, alle Felder manuell zu initialisieren, um sicherzustellen, dass ich die gewünschten Werte erhalte.
Vielen Dank. Übrigens müssen Sie den zweiten Calloc im unteren Code in einen Malloc ändern.
– Ali
2. Februar 2009 um 0:00 Uhr
Ta, verdammte Cut’n’Paste-Operationen! 🙂
– paxdiablo
2. Februar 2009 um 0:14 Uhr
Ich stimme zu – ich habe calloc() fast nie verwendet. Manchmal erstelle ich eine statische Struktur, die standardmäßig korrekt initialisiert ist, und weise diese dann neu zugewiesenen Strukturen zu – und führe alle verbleibenden Korrekturen durch.
– Jonathan Leffler
2. Februar 2009 um 1:37 Uhr
ich habe es auch noch nie benutzt. Ich finde es besser, Elemente auch explizit zu initialisieren 🙂
– Johannes Schaub – litb
2. Februar 2009 um 1:57 Uhr
Zu den ausgezeichneten Antworten möchte ich einen weiteren Unterschied zwischen der Verwendung hinzufügen calloc(nelem, elsize)
gegen malloc(nelem * elsize)
: hochwertige Implementierungen von calloc
wird dafür sorgen, dass wenn Ihr nelem
und elsize
groß genug waren, um beim Multiplizieren einen ganzzahligen Überlauf zu verursachen, wird dies eher fehlschlagen als eine zu kleine Zuordnung verursachen, da es naiv ist malloc
Aufruf würde.
Allein diese Eigenschaft würde mir schon ausreichen calloc
zu malloc
. Hintergrund lesen.
mat_geek
calloc(4, 6) und calloc(6, 4) sind NICHT das Gleiche:
Auf einem typischen 32-Bit/64-Bit-System würde das erste 32 Bytes und das zweite 24 Bytes zuweisen.
void *calloc(size_t nelem, size_t elsize);
Der entscheidende Punkt ist, dass calloc den Speicher so zurückgeben muss, als ob er korrekt als Array ausgerichtet wäre. Es soll ein Array zuweisen und wie folgt verwendet werden:
A *p = (A*)calloc(count, sizeof(A));
for (int i = 0; i < count; ++i)
{
f(&(p[i]));
// f(p + i) is also valid
}
oder
A *p = (A*)calloc(count, sizeof(A));
for (A *q = p; q != p + count; ++q)
{
f(q);
}
calloc soll das Array unter Berücksichtigung von Padding und anderen Betriebsanforderungen des Zielsystems zuweisen. Auf den meisten 32-Bit-Rechnern, wo eine 6-Byte-Struktur auf 8 Bytes aufgefüllt werden müsste, würde es 4 Lose von 8 Bytes zuweisen.
calloc, wo das erste Argument ein sizeof() ist, ist höchstwahrscheinlich ein Fehler und sollte untersucht werden.
calloc, wo das zweite Argument nicht sizeof(atype) ist, ist undefiniert. Es stinkt nach versteckten Annahmen und ist gefährlich im Hafen.
Klärung:
Auf einem typischen 32-Bit/64-Bit-System wird eine Struktur wahrscheinlich aufgefüllt und auf ein Vielfaches von 32 Bit ausgerichtet. Als solche auf diesen Systemen sizeof würde nicht gibt 6 Byte zurück. Tatsächlich gibt es keine Garantie dafür, dass der Compiler nicht auf ein Vielfaches von 16 Bytes auffüllt und ausrichtet, wenn dies der Compiler/die Plattform erfordert.
Meine Antwort basiert auf der Tatsache, dass Sie keine Annahmen über die Strukturgröße treffen sollten. Sie können sich mit Compileroptionen oder der Zielplattform ändern. Stellen Sie einfach sicher, dass Ihr zweites Argument ein sizeof-Ausdruck und ist keine Vermutungen anstellen.
Von dem Standard:
Die calloc()-Funktion soll ungenutzten Platz für ein Array von Nelem-Elementen zuweisen, deren Größe in Bytes jeweils elsize ist. Der Raum soll auf alle Bits 0 initialisiert werden.
Der zurückgegebene Zeiger, wenn die Zuweisung erfolgreich ist, muss geeignet ausgerichtet werden, so dass er einem Zeiger auf einen beliebigen Objekttyp zugewiesen und dann verwendet werden kann, um auf ein solches Objekt oder ein Array solcher Objekte in dem zugewiesenen Bereich zuzugreifen (bis der Bereich explizit freigegeben wird oder neu zugeordnet). Jede solche Zuordnung soll einen Zeiger auf ein Objekt ergeben, das von jedem anderen Objekt disjunkt ist. Der zurückgegebene Zeiger soll auf den Anfang (niedrigste Byte-Adresse) des zugewiesenen Raums zeigen.
ich glaube du liegst falsch. Sowohl malloc als auch calloc müssen einen Zeiger zurückgeben, der für jeden Objekttyp geeignet ausgerichtet ist. das ist übrigens auch genau das, was du dort aus der Norm zitiert hast. Angenommen, mein Glaube ist falsch, können Sie bitte einen Link zum Weiterlesen darüber bereitstellen?
– Johannes Schaub – litb
2. Februar 2009 um 1:13 Uhr
sowieso, wenn Sie die Reihenfolge der Argumente ändern, können Sie niemals für irgendeine Funktion garantieren, dass genau dasselbe passiert. Implementierungen könnten immer Optimierungen für sie vornehmen – sofern die vom Standard gesetzten Einschränkungen nicht verletzt werden, so dass die Verwendung von Code nicht funktioniert.
– Johannes Schaub – litb
2. Februar 2009 um 1:15 Uhr
Ich stimme litb zu; Das ist eine interessante Theorie, aber nicht richtig. Der Operator sizeof() befasst sich mit Ausrichtungsproblemen in Arrays. Wenn sizeof() 6 zurückgibt, kann der Typ an aufeinanderfolgenden 6-Byte-Slots zugewiesen werden; Wenn dies nicht möglich ist (aufgrund einer Ausrichtungsbeschränkung für einen Float), sagt sizeof() 8 …
– Jonathan Leffler
2. Februar 2009 um 1:28 Uhr
-1 Die Begründung dafür calloc(4, 6)
6 auf 8 aufrunden könnte, ist völlig falsch. Auch auf 64bit-Systemen kann man problemlos zugreifen sizeof foo
6 sein, zB für char foo[6];
und calloc (4,6)
ist erforderlich, um Ihnen einen Speicher zu geben, der als behandelt werden kann char bar[4][6]
.
– Jens
18. Mai 2012 um 15:28 Uhr
@Joh Das bedeutet das de.cppreference.com/w/cpp/memory/c/calloc gibt Fehlinformationen zu calloc() aus, da es besagt: “Aufgrund der Ausrichtungsanforderungen ist die Anzahl der zugewiesenen Bytes nicht unbedingt gleich num * size.”, nicht wahr?
– Kaiserludi
12. Februar 2015 um 19:07 Uhr
Vieldenker
Trotz der akzeptierten Antwort (die ich für richtig halte) scheint es Verwirrung darüber zu geben, wie viele Bytes aufgrund der Ausrichtung zugewiesen werden. Hier also ein kleiner Test auf meinem 32-Bit-Linux mit gcc-4.3:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char* p1 = calloc(6, 4);
char* p2 = calloc(4, 6);
char* p3 = calloc(1,1);
printf("%p, %p, %p\n", p1, p2, p3);
return 0;
}
Das Ergebnis ist:
0x826b008, 0x826b028, 0x826b048
was beides zeigt calloc(6,4)
und calloc(4,6)
Weisen Sie die gleiche Menge an Speicher zu, das heißt gerundet auf 32 Bytes auf meinem System. Ändern der Nummern in calloc(3,4)
und calloc(4,3)
wird folgendes Ergebnis liefern:
0x95d2008, 0x95d2018, 0x95d2028
was zeigt, dass 16 Bytes reserviert sind, wenn 12 angefordert und dem Programm zugewiesen werden. In jedem Fall beides calloc(a,b)
und calloc(b,a)
Aufrufe haben die gleiche Auswirkung auf die Speichernutzung.
Hinzugefügt von Jonathan Leffler, weil 300 Zeichen niemals ausreichen werden.
Betrachten Sie dieses Programm, das Speicher wie ein wahres Sieb leckt, aber einen Punkt demonstriert:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int i, j, k;
for (i = 1; i < 17; i++)
for (j = 1; j < 9; j++)
for (k = 0; k < 4; k++)
printf("(%2d,%d)%d: %p\n", i, j, k, calloc(i, j));
return(0);
}
Unter Windows beginnt dies unter Cygwin mit der Zuweisung von Blöcken, die 16 Bytes voneinander entfernt sind (tatsächlich ist der zweite Block 24 Bytes nach dem ersten, aber danach sind sie 16 Bytes voneinander entfernt). Wenn (2,7) zugewiesen wird, beginnen die Blockadressen um 24 Bytes zu inkrementieren; ebenso weist (3,4) Blöcke im Abstand von 16 Bytes zu, aber (3,5) weist Blöcke im Abstand von 24 Bytes zu. Und für den Datensatz geben sowohl (4,6) als auch (6,4) Zeiger zurück, die 32 Bytes voneinander entfernt sind.
Dies zeigt einfach, dass mit einem Zuordnungsaufruf ein gewisser Overhead verbunden ist. Wenn Sie sich die archetypische Implementierung von malloc() et al. in K&R ansehen, werden Sie sehen, dass die Größe des Blocks vor dem Speicher gespeichert wird, zu dessen Verwendung Sie berechtigt sind. Unterschiedliche Implementierungen machen diese Dinge unterschiedlich; Diejenigen, die sich Sorgen über Speichertrampling machen, werden es vermeiden, Steuerdaten in der Nähe zu speichern, wo der Benutzer Chaos anrichten kann.
Wenn Sie calloc(4,6) aufrufen, haben Sie nur zuverlässigen Zugriff auf 24 Byte Daten. Selbst wenn Ihre Implementierung Rückgabewerte liefert, die 32 Bytes voneinander entfernt sind, können Sie sicher nicht mehr als die angeforderten 24 Bytes verwenden. Und Debugging-Versionen von malloc() werden beobachten, ob Sie außerhalb der angeforderten Grenzen schreiben.
Georg Scholly
Es gibt einen kleinen Unterschied: Calloc kann entscheiden, den Speicher nur bei Bedarf auf Null zu setzen, und dann gibt es noch den Vorteil, die Größe der Elemente zu kennen.
Ich kann keine Implementierungen nennen, die dies tun, aber es wurde dafür gedacht.
Als Beispiel:
Man ruft 4 GB Speicher ab, aber das System hat nur 2 GB: Es wäre nicht sinnvoll, 2 GB Null in den virtuellen Speicher zu schreiben, daher könnte das System ein Dirty-Flag auf diesen Speicher setzen, um ihn beim Laden auf Null zu setzen Erinnerung.
Ich wäre überrascht, wenn überhaupt real Implementierungen tatsächlich implementieren calloc
Als ein malloc
und ein memzero
für alle außer kleinen Speichergrößen. Eine sehr triviale Optimierung wäre, die Seite(n) zuzuordnen /dev/zero
.
– Sanjoyd
3. November 2010 um 15:05 Uhr
@theDigtialEngel: Ich habe noch nie eine Calloc-Implementierung von innen gesehen, also kann ich es nicht sagen.
– Georg Scholly
3. November 2010 um 16:59 Uhr
Adrian Frühwirth
Es ist das Gleiche. Der Grund dafür ist, dass Sie meistens a verwenden möchten sizeof
Operator als eines der Argumente. Wenn Sie die Übergabe von zwei Parametern stört, rufen Sie an malloc()
die ein einziges Argument hat.
Ich wäre überrascht, wenn überhaupt real Implementierungen tatsächlich implementieren calloc
Als ein malloc
und ein memzero
für alle außer kleinen Speichergrößen. Eine sehr triviale Optimierung wäre, die Seite(n) zuzuordnen /dev/zero
.
– Sanjoyd
3. November 2010 um 15:05 Uhr
@theDigtialEngel: Ich habe noch nie eine Calloc-Implementierung von innen gesehen, also kann ich es nicht sagen.
– Georg Scholly
3. November 2010 um 16:59 Uhr
dbrank0
Es gibt eine andere Möglichkeit, diese Frage zu untersuchen.
Die GNU C Library definiert calloc wie folgt:
void * __libc_calloc (size_t n, size_t elem_size)
{
// ... (declarations)
/* size_t is unsigned so the behavior on overflow is defined. */
bytes = n * elem_size;
#define HALF_INTERNAL_SIZE_T \
(((INTERNAL_SIZE_T) 1) << (8 * sizeof (INTERNAL_SIZE_T) / 2))
if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0))
{
if (elem_size != 0 && bytes / elem_size != n)
{
__set_errno (ENOMEM);
return 0;
}
}
void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
{
sz = bytes;
mem = (*hook)(sz, RETURN_ADDRESS (0));
if (mem == 0)
return 0;
return memset (mem, 0, sz);
}
sz = bytes;
// ...more stuff, but no mention of n & elem_size anymore
}
Zumindest in glibc haben diese beiden Aufrufe also identische Auswirkungen.
Mögliches Duplikat von Was ist der Unterschied zwischen calloc(10,4) und calloc(1,40)?
– SS Anne
19. September 2019 um 13:10 Uhr