2013-08-13 6 views
15

L'allocazione dinamica della memoria è un argomento molto importante nella programmazione in C. Tuttavia, non sono stato in grado di trovare una buona spiegazione di ciò che questo ci permette di fare, o perché è necessario.Perché, o quando, è necessario allocare dinamicamente la memoria in C?

Non possiamo semplicemente dichiarare variabili e strutture e non utilizzare mai malloc()?

Come nota laterale, qual è la differenza tra:

ptr_one = (int *)malloc(sizeof(int)); 

e

int *ptr_one = malloc(sizeof(int)); 
+2

Provare a scrivere un programma che chiede all'utente un numero (ad esempio, il numero di studenti nella classe), quindi quel numero di volte chiede loro un nome per compilare un elenco di studenti nella classe (e quindi ordina i nomi in ordine alfabetico e li scrive in un file, o qualcosa del genere). In quale array di dimensioni vengono archiviati i nomi? – Michelle

+0

possibile duplicato di [Quando dovrei usare malloc in C e quando no?] (Http://stackoverflow.com/questions/1963780/when-should-i-use-malloc-in-c-and-when -dont-i) – koopajah

+3

Informazioni sulla trasmissione del valore restituito di malloc si consiglia di leggere questo http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – koopajah

risposta

2

Come nota laterale, qual è la differenza tra: ptr_one = (int *)malloc(sizeof(int)) e int *ptr_one = malloc(sizeof(int))

Vedi this.

In primo luogo, so che questa è probabilmente una domanda ridicola, poiché l'allocazione dinamica della memoria è un argomento molto importante nella programmazione in C. Tuttavia, non sono stato in grado di trovare una buona spiegazione di ciò che questo ci permette di fare, o perché è necessario.

Il pool di memoria (o più comunemente l'heap) è molto grande rispetto allo stack. Considerate questi due esempi per questo che è utile utilizzare il pool di memoria sopra la pila:

1. Che cosa succede se si è definito un array e voleva che persistono tra i più stack frame? Certo, potresti dichiararlo come variabile globale e verrà memorizzato nella sezione dati globale della memoria, tuttavia questo diventerà disordinato man mano che il tuo programma diventerà sempre più grande. In alternativa, è possibile memorizzarlo nel pool di memoria.

int *func(int k) { 
    assert(k >= 1); 

    int *ptr_block = malloc(sizeof(int) * k); 

    if (ptr_block == NULL) exit(EXIT_FAILURE); 

    for (int i = 0; i < k; i++) { 
    ptr_block[ i ] = i + 1; 
    } 

    return ptr_block; // Valid. 
} 

... questo sarebbe, tuttavia, non lavoro se si è definito la matrice nello stack. Essendo il motivo, una volta che uno stack frame è spuntato, tutti gli indirizzi di memoria possono essere utilizzati da un altro stack frame (e quindi sovrascritti), mentre l'utilizzo della memoria dal pool di memoria persiste fino a free d dall'utente (utente o client).

2. Cosa fare se si desidera implementare un array dinamico per gestire la lettura di una sequenza numerica arbitraria di numeri? Non si sarebbe in grado di farlo definendo la matrice in pila, sarebbe necessario utilizzare il pool di memoria. Ricordiamo che è estremamente comune (e altamente raccomandato se non si ha bisogno di copiare esplicitamente una struct) per passare un puntatore a una struct, mai la struct stessa (poiché possono essere piuttosto grandi). Considerare questa piccola implementazione di un array dinamico:

struct dyn_array { 
    int *arr; 
    int len; 
    int cap; 
}; 

typedef struct dyn_array *DynArray; 

void insert_item(int const item, DynArray dyn_arr) { 
    // Checks pre conditions. 
    assert(dyn_arr != NULL); 

    // Checks if the capacity is equal to the length. If so, double. 
    if (dyn_arr->cap == dyn_arr->len) { 
    dyn_arr->cap *= 2; 

    DynArray new_dyn_arr = malloc(sizeof(int) * dyn_arr->cap); // [oo] 

    // ... copy, switch pointers and free... 
    } 

    // ... insert, increase length, etc. 
} 

... on line [oo] avviso che se questo fosse definito in pila poi una volta questo stack frame viene estratto tutti gli indirizzi di memoria per l'array non sarebbe più essere attribuiti . Significa che un altro stack frame (probabilmente il prossimo) userà quegli indirizzi di memoria (o qualche sottoinsieme).

Nota: Dal mio frammento di codice, ptr_block è memorizzato sullo stack: da qui &ptr_block è un indirizzo di stack, tuttavia il valore di ptr_block è da qualche parte dal pool di memoria.

4

L'allocazione dinamica è necessaria quando non si conoscono i requisiti peggiori per la memoria. Quindi, è impossibile allocare staticamente la memoria necessaria, perché non sai quanto ti servirà.

Anche se si conoscono i requisiti del caso peggiore, potrebbe comunque essere preferibile utilizzare l'allocazione dinamica della memoria. Consente di utilizzare la memoria di sistema in modo più efficiente da più processi. Tutti i processi potrebbero commettere staticamente i loro peggiori requisiti di memoria, ma ciò mette un limite al numero di processi in esecuzione sul sistema. Se non accade mai che tutti i processi utilizzino il caso peggiore allo stesso tempo, la memoria di sistema funziona costantemente sottoutilizzata, il che è uno spreco di risorse.

Per quanto riguarda la domanda a lato, non si deve trasmettere il risultato di una chiamata a malloc() in C. Può nascondere il bug di una dichiarazione mancante (le dichiarazioni implicite erano consentite prima di C.99) e genera un comportamento non definito . Preferisci sempre prendere il risultato di malloc() senza un cast. malloc() viene dichiarato per restituire void * e in C è sempre consentita una conversione tra void * e un altro tipo di puntatore (qualificatori tipo di modulo come const).

12

È necessario utilizzare memoria dinamica quando:

  • Non è possibile determinare la quantità massima di memoria da utilizzare in fase di compilazione;
  • Si desidera assegnare uno molto grande oggetto;
  • Si desidera creare strutture di dati (contenitori) senza una dimensione superiore fissa;

Non sempre sapere quanta memoria è necessario mettere da parte al momento della compilazione. Immagina di elaborare un file di dati (una serie temporale di temperature, ad esempio), in cui il numero di record nel file non è fisso. Potresti avere solo 10 record o fino a 100000. Se vuoi leggere tutti i dati in memoria per elaborarli, non sai quanta memoria allocare finché non leggi il file. Se il file è strutturato in modo che il primo valore è il numero di record, si potrebbe fare qualcosa di simile:

size_t recs = 0; 
double *temps = NULL; 

FILE *fp = fopen (filename, "r"); 
if (fp) 
{ 
    if (fscanf(fp, "%zu", &recs) == 1) 
    { 
    temps = malloc(sizeof *temps * recs); 
    if (temps) 
    { 
     // read contents of file into temps 
    } 
    } 
} 

A volte è necessario allocare un oggetto di grandi dimensioni molto, qualcosa come

int ginormous[1000][1000][1000]; 

Assumendo un numero intero a 4 byte, questo array richiede 4 GB. Sfortunatamente, i frame di stack (dove le variabili locali sono mantenute sulla maggior parte delle architetture) tendono ad essere molto più piccoli di così, quindi provare a allocare così tanta memoria può portare ad un errore di run-time (e tipicamente lo fa). La memoria dinamica (pseudonimo mucchio) è tipicamente molto maggiore della pila, tanto meno qualsiasi stack frame. così per qualcosa che antipatici avresti bisogno di scrivere qualcosa di simile

int (*ginormous)[1000][1000] = malloc(sizeof *ginormous * 1000); 

E 'ancora possibile per una richiesta del genere a fallire; se il tuo heap è sufficientemente frazionato, potresti non avere un singolo blocco contiguo abbastanza grande da soddisfare la richiesta.Se necessario, potresti eseguire un'allocazione frammentaria; righe non saranno necessariamente essere adiacenti in memoria, ma è più probabile che sarete in grado di afferrare tutta la memoria è necessario:

int ***ginormous = malloc(sizeof *ginormous * 1000); 
if (ginormous) 
{ 
    for (size_t i = 0; i < 1000; i++) 
    { 
    ginormous[i] = malloc(sizeof *ginormous[i] * 1000); 
    if (ginormous[i]) 
    { 
     ginormous[i][j] = malloc (sizeof *ginormous[i][j] * 1000); 
     if (ginormous[i][j]) 
     { 
     // initialize ginormous[i][j][k] 
     } 
    } 
    } 
} 

E, infine, la memoria dinamica consente di creare contenitori che possono crescere e ridursi come aggiungi o rimuovi dati, come elenchi, alberi, code, ecc. Potresti persino creare il tuo vero tipo di dati "stringa" che può crescere man mano che aggiungi caratteri (simile al tipo string in C++).

Problemi correlati