2012-05-13 22 views
48

Oggi stavo aiutando un mio amico con un codice C, e ho trovato un comportamento strano che non riuscivo a spiegargli perché stava accadendo. Avevamo il file TSV con una lista di interi, con un int ogni riga. La prima riga era il numero di righe della lista.Differenza tra tipo array e array allocati con malloc

Abbiamo anche un file c con un "file di lettura" molto semplice. La prima riga è stata letta a n, il numero di linee, poi c'era un'inizializzazione:

int list[n] 

ed infine un ciclo di n con un fscanf.

Per piccoli n (fino a ~ 100.000), tutto andava bene. Tuttavia, abbiamo scoperto che quando n era grande (10^6), si verificava un segfault.

Infine, abbiamo cambiato l'inizializzazione elenco per

int *list = malloc(n*sizeof(int)) 

e tutto quando bene, anche con molto grande n.

Qualcuno può spiegare perché è successo? cosa stava causando il segfault con l'elenco int [n], che è stato interrotto quando iniziamo ad usare list = malloc (n * sizeof (int))?

risposta

109

Ci sono diversi pezzi in gioco qui.

La prima è la differenza tra la dichiarazione di una matrice come

int array[n]; 

e

int* array = malloc(n * sizeof(int)); 

Nella prima versione, si dichiara un oggetto con durata di memorizzazione automatica. Ciò significa che l'array vive solo finché esiste la funzione che lo chiama. Nella seconda versione, ricevi memoria con durata di archiviazione dinamica, il che significa che esisterà fino a quando non viene esplicitamente deallocato con free.

Il motivo per cui la seconda versione funziona qui è un dettaglio di implementazione di come C viene solitamente compilato. In genere, la memoria C viene suddivisa in diverse regioni, incluso lo stack (per chiamate di funzione e variabili locali) e l'heap (per gli oggetti malloc ed). Lo stack ha in genere una dimensione molto più piccola dell'heap; di solito è qualcosa come 8 MB. Di conseguenza, se si tenta di allocare una matrice enorme con

int array[n]; 

Poi si potrebbe superare lo spazio di archiviazione dello stack, causando la segfault. D'altra parte, l'heap di solito ha una dimensione enorme (ad esempio, quanto spazio è libero sul sistema), e così l'malloc di un oggetto grande non causerà un errore di memoria esaurita.

In generale, fare attenzione con gli array di lunghezza variabile in C. Possono facilmente superare le dimensioni dello stack. Preferisci malloc a meno che tu non sappia che la dimensione è piccola o che in realtà desideri solo l'array per un breve periodo di tempo.

Spero che questo aiuti!

+1

Risposta molto elucidante ... grazie! –

+1

Ottima risposta! Mi stavo chiedendo se c'è anche una differenza di velocità? –

+1

A causa degli effetti della località di riferimento, sospetto che l'array allocato allo stack sia più veloce per accedere, e 'malloc' stesso è molto più lento di un semplice puntatore dello stack. Ma in realtà, è meglio utilizzare qualsiasi approccio sia più appropriato per l'attività in corso. – templatetypedef

2

int lista [n] memorizza i dati nello stack, mentre malloc lo memorizza nell'heap.

Lo stack è limitato e non c'è molto spazio, mentre l'heap è molto più grande.

1

int list[n] è un VLA, che alloca sullo stack anziché sull'heap. Non è necessario liberarlo (si cancella automaticamente alla fine della chiamata di funzione) e si alloca rapidamente, ma lo spazio di archiviazione è molto limitato, come hai scoperto. È necessario allocare valori più grandi sull'heap.

1

Questa dichiarazione alloca memoria sullo stack

int list[n] 

malloc alloca sul mucchio.

La dimensione dello stack è in genere più piccola dell'heap, quindi se si alloca troppa memoria nello stack si ottiene uno stackoverflow.

Vedi anche this answer for further information

0

Quando si allocano utilizza un malloc, la memoria è allocata dall'heap e non dalla pila, che è molto più limitata nelle dimensioni.

8
int list[n] 

spazio Alloca per n interi sul la pila, che di solito è piuttosto piccola. L'utilizzo della memoria nello stack è molto più veloce dell'alternativa, ma è piuttosto piccola ed è facile sovrasfruttare lo stack (ad esempio allocare troppa memoria) se si fanno cose come allocare array enormi o fare ricorsioni troppo profonde. Non è necessario deallocare manualmente la memoria allocata in questo modo, viene eseguita dal compilatore quando l'array esce dall'ambito.

malloc invece alloca spazio nel mucchio, che di solito è molto grande rispetto alla pila.Dovrai allocare una quantità molto maggiore di memoria nell'heap per esaurirlo, ma è molto più lento allocare memoria nell'heap che nello stack e devi deallocarlo manualmente tramite free quando hai finito di usare esso.

+1

"L'utilizzo della memoria nello stack è molto più veloce dell'alternativa", qui intendi per "allocazione" o "accesso"? AFAIK, l'allocazione dello stack è molto più veloce ma vale anche per l'accesso (lettura/scrittura)? Grazie – dragonxlwang

1

Supponendo di avere una tipica implementazione nella tua implementazione è più probabile che:

int list[n] 

lista allocato sul tuo stack, dove, come:

int *list = malloc(n*sizeof(int)) 

memoria allocata sul mucchio.

Nel caso di uno stack, in genere è presente un limite a quanto possono crescere (se possono crescere). Nel caso di un heap c'è ancora un limite, ma che tende ad essere largamente e (ampiamente) vincolato dallo spazio di indirizzamento RAM + swap + che è tipicamente di almeno un ordine di grandezza più grande, se non di più.

0

Se si è su linux, è possibile impostare ulimit -s su un valore maggiore e questo potrebbe funzionare anche per l'allocazione dello stack. Quando si assegna memoria allo stack, quella memoria rimane fino alla fine dell'esecuzione della propria funzione. Se si alloca memoria su heap (usando malloc), è possibile liberare la memoria ogni volta che lo si desidera (anche prima della fine dell'esecuzione della funzione).

In genere, l'heap deve essere utilizzato per allocazioni di memoria di grandi dimensioni.

0
int array[n]; 

È un esempio di array allocato staticamente e al momento della compilazione sarà nota la dimensione dell'array. E l'array verrà assegnato nello stack.

int *array(malloc(sizeof(int)*n); 

È un esempio di matrice allocata dinamicamente e la dimensione dell'array sarà nota all'utente in fase di esecuzione. E la matrice verrà allocata nell'heap.

Problemi correlati