Allocazione dinamica della memoria in C: malloc(), calloc()

⚡ Riepilogo intelligente

L'allocazione dinamica della memoria in C riserva e rilascia memoria heap in fase di esecuzione utilizzando le funzioni malloc(), calloc(), realloc() e free() della libreria stdlib.h, consentendo ai programmi di dimensionare array e strutture in modo flessibile quando i requisiti non sono noti prima dell'esecuzione.

  • 🧠 Stack contro heap: Le variabili di base utilizzano lo stack automatico, mentre la memoria dinamica risiede nell'heap ed è gestita manualmente tramite puntatori.
  • 📦 malloc(): Riserva un blocco non inizializzato della dimensione in byte richiesta e restituisce un puntatore void, oppure NULL quando la memoria è insufficiente.
  • 🔢 calloc(): Alloca più blocchi per gli array, imposta ogni byte a zero e restituisce un puntatore al primo blocco.
  • 🔄 realloc(): Consente di ridimensionare un blocco esistente, aumentandone o diminuendone le dimensioni, preservandone il contenuto originale e posizionandolo eventualmente in una nuova posizione.
  • 🧹 gratuito(): Restituisce la memoria all'heap; ogni malloc() o calloc() necessita di una corrispondente free() per evitare perdite di memoria.
  • 🤖 Aiuto tramite intelligenza artificiale: Gli assistenti IA e GitHub Copilot bozzano il codice di allocazione e segnalano le chiamate free() mancanti e le perdite di memoria.

Allocazione dinamica della memoria in C

Come funziona la gestione della memoria in C?

Quando si dichiara una variabile utilizzando un tipo di dati di base, il compilatore C alloca automaticamente spazio di memoria per la variabile in un pool di memoria chiamato stack.

Ad esempio, una variabile float occupa in genere 4 byte (a seconda della piattaforma) quando viene dichiarata. Possiamo verificare questa informazione utilizzando l'operatore sizeof, come mostrato nell'esempio seguente.

#include <stdio.h>
int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}

L'output sarà:

 The size of float is 4 bytes

Inoltre, un array con una dimensione specificata viene allocato in blocchi di memoria contigui, ogni blocco ha la dimensione di un elemento:

#include <stdio.h>
int main() { float arr[10];
printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}

Il risultato è:

 The size of the float array with 10 element is 40

Come appreso finora, quando si dichiara un tipo di dato di base o un array, la memoria viene gestita automaticamente. Tuttavia, in C esiste un processo di allocazione della memoria che permette di implementare un programma in cui la dimensione dell'array non viene determinata fino all'esecuzione del programma stesso (runtime). Questo processo è chiamato "allocazione dinamica della memoria".

Allocazione dinamica della memoria in C

L'allocazione dinamica della memoria (DAM) è l'allocazione e la liberazione manuale della memoria in base alle esigenze di programmazione. La memoria dinamica viene gestita e resa disponibile tramite puntatori che puntano al nuovo spazio di memoria allocato in un'area che chiamiamo heap.

Ora puoi creare e distruggere una serie di elementi dinamicamente in fase di esecuzione senza problemi. Per riassumere, la gestione automatica della memoria utilizza lo stack e l'allocazione dinamica della memoria C utilizza l'heap.

IL la libreria ha funzioni responsabili della gestione dinamica della memoria.

Funzione Missione
malloc () Alloca la memoria della dimensione richiesta e restituisce il puntatore al primo byte di
spazio assegnato.
calloc() Alloca lo spazio per gli elementi di un array. Inizializza gli elementi a zero e restituisce un puntatore alla memoria.
riallocare() Viene utilizzato per modificare la dimensione dello spazio di memoria precedentemente allocato.
Gratuito() Libera o svuota lo spazio di memoria precedentemente allocato.

Parliamo delle funzioni di cui sopra con la loro applicazione

Funzione malloc() in C

La funzione C malloc() indica l'allocazione della memoria. È una funzione che viene utilizzata per allocare dinamicamente un blocco di memoria. Riserva lo spazio di memoria della dimensione specificata e restituisce il puntatore nullo che punta alla posizione di memoria. Il puntatore restituito è solitamente di tipo void. Significa che possiamo assegnare la funzione C malloc() a qualsiasi puntatore.

Sintassi della funzione malloc():

ptr = (cast_type *) malloc (byte_size);

Qui,

  • ptr è un puntatore di cast_type.
  • La funzione C malloc() restituisce un puntatore alla memoria allocata di byte_size.

Esempio di malloc():

Example: ptr = (int *) malloc (50)

Quando questa istruzione viene eseguita con successo, viene riservato uno spazio di memoria di 50 byte. L'indirizzo del primo byte di spazio riservato è assegnato al puntatore ptr di tipo int.

Consideriamo un altro esempio:

#include <stdlib.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */
    if (ptr != NULL) {
      *(ptr + 5) = 480; /* assign 480 to sixth integer */
      printf("Value of the 6th integer is %d",*(ptr + 5));
    }
}

Produzione:

Value of the 6th integer is 480

Funzione malloc() di C

  1. Si noti che sizeof(*ptr) è stato utilizzato al posto di sizeof(int) per rendere il codice più robusto nel caso in cui la dichiarazione di *ptr venga successivamente convertita in un tipo di dati diverso.
  2. L'allocazione potrebbe non riuscire se la memoria non è sufficiente. In questo caso restituisce un puntatore NULL. Quindi, dovresti includere il codice per verificare la presenza di un puntatore NULL.
  3. Tieni presente che la memoria allocata è contigua e può essere trattata come un array. Possiamo usare l'aritmetica dei puntatori per accedere agli elementi dell'array anziché usare le parentesi [ ]. Consigliamo di usare + per fare riferimento agli elementi dell'array perché usare l'incremento ++ o += cambia l'indirizzo memorizzato dal pointer.

La funzione Malloc() può essere utilizzata anche con il tipo di dati carattere e con tipi di dati complessi come le strutture.

funzione free() in C

La memoria per variabili viene deallocato automaticamente in fase di compilazione. Nell'allocazione dinamica della memoria, è necessario deallocare la memoria in modo esplicito. In caso contrario, potresti riscontrare un errore di memoria insufficiente.

La funzione free() viene chiamata per rilasciare/deallocare la memoria in C. Liberando la memoria nel programma, se ne rende disponibile altra per un utilizzo successivo.

Per esempio:

#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL){
  *(ptr + 2) = 50;
  printf("Value of the 2nd integer is %d",*(ptr + 2));
}
free(ptr);
}

Produzione:

 Value of the 2nd integer is 50

funzione calloc() in C

La funzione C calloc() sta per contiguous allocation. Questa funzione è usata per allocare più blocchi di memoria. È una funzione di allocazione dinamica della memoria che è usata per allocare la memoria a strutture dati complesse come array e strutture.

La funzione Malloc() viene utilizzata per allocare un singolo blocco di spazio di memoria mentre calloc() in C viene utilizzata per allocare più blocchi di spazio di memoria. Ogni blocco allocato dalla funzione calloc() ha la stessa dimensione.

Sintassi della funzione calloc():

ptr = (cast_type *) calloc (n, size);

L'istruzione precedente viene utilizzata per allocare n blocchi di memoria della stessa dimensione.

Dopo che lo spazio di memoria è stato allocato, tutti i byte vengono inizializzati a zero.

Viene restituito il puntatore che attualmente si trova sul primo byte dello spazio di memoria allocato.

Ogni volta che si verifica un errore nell'allocazione dello spazio di memoria, ad esempio per carenza di memoria, viene restituito un puntatore nullo.

Esempio di calloc():

Il programma seguente calcola la somma di una sequenza aritmetica.

#include <stdio.h>
    int main() {
        int i, * ptr, sum = 0;
        ptr = calloc(10, sizeof(int));
        if (ptr == NULL) {
            printf("Error! memory not allocated.");
            exit(0);
        }
        printf("Building and calculating the sequence sum of the first 10 terms \ n ");
        for (i = 0; i < 10; ++i) { * (ptr + i) = i;
            sum += * (ptr + i);
        }
        printf("Sum = %d", sum);
        free(ptr);
        return 0;
    }

Risultato:

Building and calculating the sequence sum of the first 10 terms
Sum = 45

calloc() vs. malloc(): differenze chiave

Di seguito è riportata la differenza fondamentale tra malloc() contro calloc() in do:

La funzione calloc() è generalmente più adatta ed efficiente di quella malloc(). Mentre entrambe le funzioni sono usate per allocare spazio di memoria, calloc() può allocare più blocchi in una sola volta. Non devi richiedere un blocco di memoria ogni volta. La funzione calloc() è usata in strutture dati complesse che richiedono più spazio di memoria.

Il blocco di memoria allocato da calloc() in C viene sempre inizializzato a zero mentre nella funzione malloc() in C contiene sempre un valore spazzatura.

funzione realloc() in C

Utilizzando la funzione realloc() del linguaggio C, è possibile aggiungere ulteriore memoria a una porzione di memoria già allocata. Questa funzione espande il blocco corrente lasciando inalterato il contenuto originale. In C, realloc() sta per "reallocazione di memoria".

realloc() può essere utilizzato anche per ridurre la dimensione della memoria precedentemente allocata.

Sintassi della funzione realloc():

ptr = realloc (ptr,newsize);

L'istruzione precedente alloca un nuovo spazio di memoria con una dimensione specificata nella variabile newsize. Dopo aver eseguito la funzione, il puntatore verrà riportato al primo byte del blocco di memoria. La nuova dimensione può essere maggiore o minore della memoria precedente. Non possiamo essere sicuri che il blocco appena allocato punterà alla stessa posizione di quella del blocco di memoria precedente. Questa funzione copierà tutti i dati precedenti nella nuova regione. Garantisce che i dati rimangano al sicuro.

Esempio di realloc():

#include <stdio.h>
int main () {
   char *ptr;
   ptr = (char *) malloc(10);
   strcpy(ptr, "Programming");
   printf(" %s,  Address = %u\n", ptr, ptr);

   ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size
   strcat(ptr, " In 'C'");
   printf(" %s,  Address = %u\n", ptr, ptr);
   free(ptr);
   return 0;
}

Ogni volta che realloc() in C risulta in un'operazione non riuscita, restituisce un puntatore null e anche i dati precedenti vengono liberati.

Array dinamici in C

Un array dinamico in C consente al numero di elementi di crescere secondo necessità. Gli array dinamici C sono ampiamente utilizzati negli algoritmi informatici.

Nel programma seguente, abbiamo creato e ridimensionato un array dinamico in C

#include <stdio.h>
    int main() {
        int * arr_dynamic = NULL;
        int elements = 2, i;
        arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks
        for (i = 0; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        elements = 4;
        arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements
        printf("After realloc\n");
        for (i = 2; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        free(arr_dynamic);
    }

Risultato del programma C Dynamic array sullo schermo:

 
arr_dynamic[0]=0
arr_dynamic[1]=1
After realloc
arr_dynamic[0]=0
arr_dynamic[1]=1
arr_dynamic[2]=2
arr_dynamic[3]=3

DOMANDE FREQUENTI

Si verifica una perdita di memoria quando la memoria heap allocata da malloc() o calloc() non viene mai rilasciata. Per evitarla, associa a ogni allocazione una corrispondente chiamata a free() non appena il blocco non è più necessario.

Un puntatore pendente mantiene ancora l'indirizzo di memoria che free() ha già rilasciato. Dereferenziarlo causa un comportamento indefinito, quindi per sicurezza assegna NULL al puntatore subito dopo averlo liberato.

No. Il linguaggio C non ha un garbage collector, quindi la memoria heap non viene mai recuperata automaticamente. Il programmatore deve rilasciare esplicitamente ogni blocco con free(); altrimenti l'allocazione persiste fino alla terminazione del programma.

No. Chiamare free() due volte sullo stesso indirizzo equivale a una doppia deallocazione e provoca un comportamento indefinito o un arresto anomalo. Impostare il puntatore a NULL dopo la prima free() rende innocue le successive chiamate a free().

realloc(NULL, size) si comporta esattamente come malloc(size) e alloca un nuovo blocco. realloc(ptr, 0) libera il blocco, quindi cattura sempre il puntatore restituito invece di sovrascrivere quello originale.

Strumenti come Valgrind, AddressSanitizer e Dr. Memory track ogni allocazione e segnala i blocchi che non sono mai stati liberati, insieme alla riga esatta, aiutaping individua le perdite prima del rilascio.

Sì. Un assistente di programmazione basato sull'IA genera sequenze di malloc(), calloc() e free() a partire da un prompt, spiega gli errori di puntatore e segnala le chiamate free() mancanti. Compila e testa sempre il codice generato prima di considerarlo attendibile.

Copilota GitHub suggerisce blocchi di allocazione, controlli NULL e chiamate free() corrispondenti da un commento; rivedi ogni completamento per perdite e dimensioni corrette prima di rilasciarlo.

Riassumi questo post con: