Qual è la differenza tra fare:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
o:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Quando è una buona idea usare calloc su malloc o viceversa?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Qual è la differenza tra fare:
ptr = (char **) malloc (MAXELEMS * sizeof(char *));
o:
ptr = (char **) calloc (MAXELEMS, sizeof(char*));
Quando è una buona idea usare calloc su malloc o viceversa?
ptr = calloc(MAXELEMS, sizeof(*ptr));
Risposte:
calloc()
ti dà un buffer zero inizializzato, mentre malloc()
lascia la memoria non inizializzata.
Per allocazioni di grandi dimensioni, la maggior parte delle calloc
implementazioni nei sistemi operativi tradizionali otterrà pagine azzerate dal sistema operativo (ad esempio tramite POSIX mmap(MAP_ANONYMOUS)
o Windows VirtualAlloc
), quindi non è necessario scriverle nello spazio utente. Questo è come normale malloc
ottiene anche più pagine dal sistema operativo; calloc
sfrutta solo la garanzia del sistema operativo.
Ciò significa che la calloc
memoria può ancora essere "pulita" e allocata pigramente, e copia su scrittura mappata su una pagina fisica condivisa a livello di sistema di zeri. (Supponendo un sistema con memoria virtuale.)
Alcuni compilatori possono anche ottimizzare malloc + memset (0) in calloc per te, ma dovresti usare calloc esplicitamente se vuoi che la memoria legga come 0
.
Se non leggerai mai la memoria prima di scriverla, usala in malloc
modo che possa (potenzialmente) fornirti memoria sporca dalla sua lista libera interna invece di ottenere nuove pagine dal sistema operativo. (O invece di azzerare un blocco di memoria nella lista libera per una piccola allocazione).
Le implementazioni incorporate calloc
possono lasciare a calloc
se stesso la memoria zero se non c'è un sistema operativo, o non è un sistema operativo multiutente sofisticato che azzera le pagine per bloccare la fuga di informazioni tra i processi.
Su Linux incorporato, malloc potrebbe mmap(MAP_UNINITIALIZED|MAP_ANONYMOUS)
, che è abilitato solo per alcuni kernel incorporati perché non è sicuro su un sistema multiutente.
calloc
non è necessariamente più costoso, poiché il sistema operativo può fare alcuni trucchi per accelerarlo. So che FreeBSD, quando ottiene qualsiasi tempo di inattività della CPU, lo usa per eseguire un semplice processo che va in giro e azzera i blocchi di memoria deallocati, e contrassegna i blocchi così i processi con una bandiera. Quindi, quando lo fai calloc
, cerca prima di trovare uno di questi blocchi pre-azzerati e dartelo, e molto probabilmente lo troverà.
Una differenza meno nota è che nei sistemi operativi con allocazione ottimistica della memoria, come Linux, il puntatore restituito malloc
non è supportato dalla memoria reale fino a quando il programma non lo tocca.
calloc
tocca davvero la memoria (scrive zero su di essa) e quindi sarai sicuro che il sistema operativo stia supportando l'allocazione con RAM effettiva (o swap). Questo è anche il motivo per cui è più lento di malloc (non solo deve azzerarlo, ma anche il sistema operativo deve trovare un'area di memoria adatta scambiando eventualmente altri processi)
Vedi ad esempio questa domanda SO per ulteriori discussioni sul comportamento del malloc
calloc
non è necessario scrivere zeri. Se il blocco allocato è costituito principalmente da nuove pagine zero fornite dal sistema operativo, può lasciare intatte quelle. Questo ovviamente richiede calloc
di essere sintonizzato sul sistema operativo piuttosto che su una funzione di libreria generica malloc
. Oppure, un implementatore potrebbe calloc
confrontare ogni parola con zero prima di azzerarla. Ciò non farebbe risparmiare tempo, ma eviterebbe di sporcare le nuove pagine.
dlmalloc
implementazioni a tutto tondo saltano memset
se il blocco è stato ottenuto tramite mmap
nuove pagine anonime (o equivalenti). Di solito questo tipo di allocazione viene utilizzato per blocchi più grandi, a partire da 256k circa. Non conosco alcuna implementazione che faccia il confronto con zero prima di scrivere zero a parte il mio.
omalloc
salta anche il memset
; calloc
non deve mai toccare pagine che non sono già utilizzate dall'applicazione (cache della pagina). Tuttavia, le calloc
implementazioni estremamente primitive differiscono.
Un vantaggio spesso trascurato calloc
è che (implementazioni conformi di) ti aiuteranno a proteggerti da vulnerabilità di overflow di numeri interi. Confrontare:
size_t count = get_int32(file);
struct foo *bar = malloc(count * sizeof *bar);
vs.
size_t count = get_int32(file);
struct foo *bar = calloc(count, sizeof *bar);
Il primo potrebbe comportare una piccola allocazione e successivi buffer overflow, se count
è maggiore di SIZE_MAX/sizeof *bar
. Quest'ultimo fallirà automaticamente in questo caso poiché non è possibile creare un oggetto così grande.
Ovviamente potresti dover essere alla ricerca di implementazioni non conformi che semplicemente ignorano la possibilità di overflow ... Se questo è un problema sulle piattaforme target, dovrai comunque eseguire un test manuale per overflow.
char
è un overflow ma piuttosto una conversione definita dall'implementazione quando si riassegna il risultato in un oggetto. char
size_t
è a 64 bit, quindi non è un problema", questo è un modo errato di pensare che porterà a bug di sicurezza. size_t
è un tipo astratto che rappresenta le dimensioni e non c'è motivo di pensare che il prodotto arbitrario di un numero a 32 bit e un size_t
(nota: sizeof *bar
potrebbe in linea di principio essere maggiore di 2 ^ 32 su un'implementazione C a 64 bit!) size_t
.
La documentazione fa apparire il calloc come malloc, che azzera la memoria; questa non è la differenza principale! L'idea di calloc è di interrompere la semantica copy-on-write per l'allocazione della memoria. Quando si alloca memoria con calloc, tutto viene mappato sulla stessa pagina fisica inizializzata su zero. Quando una delle pagine della memoria allocata viene scritta in una pagina fisica viene allocata. Questo è spesso usato per creare tabelle hash ENORMI, ad esempio poiché le parti di hash che sono vuote non sono supportate da memoria aggiuntiva (pagine); fanno felicemente riferimento alla singola pagina a inizializzazione zero, che può anche essere condivisa tra i processi.
Qualsiasi scrittura su indirizzo virtuale viene mappata su una pagina, se quella pagina è la pagina zero, viene allocata un'altra pagina fisica, la pagina zero viene copiata lì e il flusso di controllo viene restituito al processo client. Funziona allo stesso modo dei file mappati in memoria, della memoria virtuale, ecc. Funziona con il paging.
Ecco una storia di ottimizzazione sull'argomento: http://blogs.fau.de/hager/2007/05/08/benchmarking-fun-with-calloc-and-zero-pages/
Non vi è alcuna differenza nella dimensione del blocco di memoria allocato. calloc
riempie semplicemente il blocco di memoria con un modello fisico a zero-bit. In pratica si presume spesso che gli oggetti situati nel blocco di memoria allocato con calloc
abbiano valore iniziale come se fossero inizializzati con 0
valori letterali , cioè gli interi dovrebbero avere valore di 0
variabili a virgola mobile - valore di 0.0
, puntatori - il valore puntatore null appropriato , e così via.
Dal punto di vista pedante, però, calloc
(così come memset(..., 0, ...)
) è garantito solo per inizializzare correttamente (con zero) oggetti di tipo unsigned char
. Non è garantito che tutto il resto sia inizializzato correttamente e potrebbe contenere la cosiddetta rappresentazione trap , che provoca un comportamento indefinito. In altre parole, per qualsiasi tipo diverso unsigned char
dal summenzionato patterm a zero bit potrebbe rappresentare un valore illegale, rappresentazione trap.
Successivamente, in una delle rettifiche tecniche conformi allo standard C99, il comportamento è stato definito per tutti i tipi di numeri interi (il che ha senso). Vale a dire formalmente, nel linguaggio C corrente è possibile inizializzare solo i tipi interi con calloc
(e memset(..., 0, ...)
). Usarlo per inizializzare qualsiasi altra cosa nel caso generale porta a un comportamento indefinito, dal punto di vista del linguaggio C.
In pratica, calloc
funziona, come tutti sappiamo :), ma se vuoi usarlo (considerando quanto sopra) dipende da te. Personalmente preferisco evitarlo completamente, usare malloc
invece ed eseguire la mia inizializzazione.
Infine, calloc
è necessario un altro dettaglio importante per calcolare internamente la dimensione finale del blocco , moltiplicando la dimensione dell'elemento per il numero di elementi. Mentre lo fa, calloc
deve fare attenzione al possibile trabocco aritmetico. Si otterrà un'allocazione non riuscita (puntatore nullo) se la dimensione del blocco richiesta non può essere calcolata correttamente. Nel frattempo, la tua malloc
versione non tenta di controllare l'overflow. Allocherà una quantità "imprevedibile" di memoria in caso di overflow.
memset(p, v, n * sizeof type);
un problema perché n * sizeof type
potrebbe traboccare. Immagino che dovrò usare un for(i=0;i<n;i++) p[i]=v;
loop per un codice affidabile.
n
esiste un array con elementi in cui un elemento ha le dimensionisizeof type
, n*sizeof type
non può overflow, poiché la dimensione massima di qualsiasi oggetto deve essere inferiore a SIZE_MAX
.
SIZE_MAX
, ma non ci sono array qui. Il puntatore restituito da calloc()
può puntare alla memoria allocata di quanto superi SIZE_MAX
. Molte implementazioni limitano il prodotto dei 2 argomenti calloc()
a SIZE_MAX
, ma la specifica C non impone quel limite.
da un articolo Benchmarking fun with calloc () e zero pagine sul blog di Georg Hager
Quando si alloca memoria usando calloc (), la quantità di memoria richiesta non viene allocata immediatamente. Invece, tutte le pagine che appartengono al blocco di memoria sono collegate a una singola pagina contenente tutti gli zeri da un po 'di magia MMU (link sotto). Se tali pagine vengono lette solo (il che era vero per gli array b, c e d nella versione originale del benchmark), i dati vengono forniti dalla singola pagina zero, che - ovviamente - si inserisce nella cache. Questo per quanto riguarda i kernel con loop di memoria. Se una pagina viene scritta (indipendentemente da come), si verifica un errore, la pagina "reale" viene mappata e la pagina zero viene copiata in memoria. Questo si chiama copy-on-write, un noto approccio di ottimizzazione (che ho persino insegnato più volte nelle mie lezioni in C ++). Dopo di che,
calloc
è generalmente malloc+memset
a 0
In genere è leggermente meglio usare malloc+memset
esplicitamente, specialmente quando stai facendo qualcosa del tipo:
ptr=malloc(sizeof(Item));
memset(ptr, 0, sizeof(Item));
Questo è meglio perché sizeof(Item)
è noto al compilatore in fase di compilazione e il compilatore nella maggior parte dei casi lo sostituirà con le migliori istruzioni possibili per azzerare la memoria. D'altra parte, se memset
sta accadendo calloc
, la dimensione del parametro dell'allocazione non è compilata nel calloc
codice e memset
spesso viene chiamato real , che in genere conterrebbe codice per riempire byte per byte fino al limite lungo, quindi ciclo per riempire memoria in sizeof(long)
blocchi e infine riempimento byte per byte dello spazio rimanente. Anche se l'allocatore è abbastanza intelligente da chiamarne un po ' aligned_memset
, rimarrà comunque un ciclo generico.
Un'eccezione notevole sarebbe quando si esegue malloc / calloc di un grosso pezzo di memoria (alcuni power_of_two kilobytes) nel qual caso l'allocazione può essere effettuata direttamente dal kernel. Poiché i kernel del sistema operativo in genere azzerano tutta la memoria che forniscono per motivi di sicurezza, calloc abbastanza intelligente potrebbe semplicemente restituirlo senza ulteriore azzeramento. Ancora una volta - se stai semplicemente allocando qualcosa che sai essere piccolo, potresti stare meglio con malloc + memset dal punto di vista delle prestazioni.
calloc()
più lento di malloc()
: la moltiplicazione per la dimensione. calloc()
è necessario utilizzare una moltiplicazione generica (se size_t
è 64 bit anche i 64 bit molto costosi * 64 bit = operazione 64 bit) mentre il malloc () avrà spesso una costante di tempo di compilazione.
struct foo { char a,b,c; };
. calloc
è sempre meglio di malloc
+ memset
, se hai sempre intenzione di cancellare l'intera malloc
regione ed. calloc
ha un controllo attento ma efficace anche per overflow int in elementi size *.
Differenza 1:
malloc()
di solito alloca il blocco di memoria ed è un segmento di memoria inizializzato.
calloc()
alloca il blocco di memoria e inizializza tutto il blocco di memoria su 0.
Differenza 2:
Se consideri la malloc()
sintassi, ci vorrà solo 1 argomento. Considera il seguente esempio di seguito:
data_type ptr = (cast_type *)malloc( sizeof(data_type)*no_of_blocks );
Esempio: se si desidera allocare 10 blocchi di memoria per il tipo int,
int *ptr = (int *) malloc(sizeof(int) * 10 );
Se si considera la calloc()
sintassi, saranno necessari 2 argomenti. Considera il seguente esempio di seguito:
data_type ptr = (cast_type *)calloc(no_of_blocks, (sizeof(data_type)));
Es: se si desidera allocare 10 blocchi di memoria per il tipo int e inizializzare tutto ciò su ZERO,
int *ptr = (int *) calloc(10, (sizeof(int)));
Somiglianza:
Entrambi malloc()
e calloc()
restituiranno void * per impostazione predefinita se non sono stati espressi.
Ci sono due differenze.
Innanzitutto, è il numero di argomenti. malloc()
accetta un singolo argomento (memoria richiesta in byte), mentre calloc()
necessita di due argomenti.
In secondo luogo, malloc()
non inizializza la memoria allocata, mentre calloc()
inizializza la memoria allocata su ZERO.
calloc()
alloca un'area di memoria, la lunghezza sarà il prodotto dei suoi parametri. calloc
riempie la memoria con ZERO e restituisce un puntatore al primo byte. Se non riesce a individuare abbastanza spazio, restituisce un NULL
puntatore.Sintassi: ptr_var=(cast_type *)calloc(no_of_blocks , size_of_each_block);
ieptr_var=(type *)calloc(n,s);
malloc()
alloca un singolo blocco di memoria di REQUSTED SIZE e restituisce un puntatore al primo byte. Se non riesce a individuare la quantità di memoria richiesta, viene restituito un puntatore null.Sintassi: ptr_var=(cast_type *)malloc(Size_in_bytes);
La malloc()
funzione prende un argomento, che è il numero di byte da allocare, mentre la calloc()
funzione prende due argomenti, uno è il numero di elementi, e l'altro è il numero di byte da allocare per ciascuno di tali elementi. Inoltre, calloc()
inizializza lo spazio allocato a zero, mentre malloc()
non lo fa.
La calloc()
funzione dichiarata <stdlib.h>
nell'intestazione offre un paio di vantaggi rispetto alla malloc()
funzione.
malloc()
e calloc()
sono funzioni della libreria standard C che consentono l'allocazione dinamica della memoria, il che significa che entrambi consentono l'allocazione della memoria durante il runtime.
I loro prototipi sono i seguenti:
void *malloc( size_t n);
void *calloc( size_t n, size_t t)
Ci sono principalmente due differenze tra i due:
Comportamento: malloc()
alloca un blocco di memoria, senza inizializzarlo, e la lettura del contenuto da questo blocco comporterà valori inutili. calloc()
d'altra parte, alloca un blocco di memoria e lo inizializza su zero, e ovviamente la lettura del contenuto di questo blocco comporterà zeri.
Sintassi: malloc()
accetta 1 argomento (la dimensione da allocare) e calloc()
accetta due argomenti (numero di blocchi da allocare e dimensione di ciascun blocco).
Il valore restituito da entrambi è un puntatore al blocco di memoria allocato, se ha esito positivo. In caso contrario, verrà restituito NULL indicando l'errore di allocazione della memoria.
Esempio:
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));
Le stesse funzionalità calloc()
ottenibili utilizzando malloc()
e 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));
Si noti che malloc()
è preferibilmente utilizzato calloc()
poiché è più veloce. Se si desidera inizializzare zero i valori, utilizzare calloc()
invece.
Una differenza non ancora menzionata: limite di dimensioni
void *malloc(size_t size)
può allocare solo fino a SIZE_MAX
.
void *calloc(size_t nmemb, size_t size);
può allocare circa SIZE_MAX*SIZE_MAX
.
Questa capacità non viene spesso utilizzata in molte piattaforme con indirizzamento lineare. Tali sistemi limitano calloc()
con nmemb * size <= SIZE_MAX
.
Considera un tipo di 512 byte chiamati disk_sector
e il codice vuole usare molti settori. Qui, il codice può usare solo fino a SIZE_MAX/sizeof disk_sector
settori.
size_t count = SIZE_MAX/sizeof disk_sector;
disk_sector *p = malloc(count * sizeof *p);
Considera quanto segue che consente un'allocazione ancora maggiore.
size_t count = something_in_the_range(SIZE_MAX/sizeof disk_sector + 1, SIZE_MAX)
disk_sector *p = calloc(count, sizeof *p);
Ora, se un tale sistema è in grado di fornire un'allocazione così ampia è un'altra questione. La maggior parte di oggi no. Eppure è successo per molti anni quando SIZE_MAX
era 65535. Data la legge di Moore , si sospetta che ciò accadrà intorno al 2030 con alcuni modelli di SIZE_MAX == 4294967295
memoria e pool di memoria da 100 GByte.
size_t
maggiore di 32 bit. L'unica domanda è se si possa fare affidamento sull'utilizzo calloc
di valori il cui prodotto supera SIZE_MAX
per produrre zero anziché restituire un puntatore a un'allocazione più piccola.
calloc()
superare le allocazioni SIZE_MAX
. È accaduto in passato con 16 bit size_t
e poiché la memoria continua a indebolirsi, non vedo alcun motivo per cui non potrà succedere anche se non è comune .
SIZE_MAX
. Certamente non richiede l'esistenza di alcuna circostanza in cui tale allocazione potrebbe avere successo; Non sono sicuro che ci sia alcun vantaggio particolare nel richiedere che le implementazioni che non sono in grado di gestire tali allocazioni debbano restituire NULL
(soprattutto dato che è comune che alcune implementazioni abbiano malloc
puntatori di ritorno nello spazio non ancora impegnato e potrebbero non essere disponibili quando il codice tenta effettivamente di utilizzare esso).
size_t
a uint64_t
?
Numero di blocchi:
malloc () assegna un singolo blocco della memoria richiesta,
calloc () assegna più blocchi della memoria richiesta
Inizializzazione:
malloc () - non cancella e inizializza la memoria allocata.
calloc () - inizializza la memoria allocata di zero.
Velocità:
malloc () è veloce.
calloc () è più lento di malloc ().
Argomenti e sintassi:
malloc () accetta 1 argomento:
byte
calloc () accetta 2 argomenti:
lunghezza
void *malloc(size_t bytes);
void *calloc(size_t length, size_t bytes);
Modalità di allocazione della memoria:
la funzione malloc assegna la memoria della "dimensione" desiderata dall'heap disponibile.
La funzione calloc assegna una memoria delle dimensioni di "num * size".
Significato sul nome:
il nome malloc significa "allocazione della memoria".
Il nome calloc significa "allocazione contigua".
malloc
famiglia