L’allocazione dinamica della memoria in C. Quando farne uso?
Indice del Post...
Come è organizzata la memoria in C
Il linguaggio di programmazione C è per natura un linguaggio molto flessibile, tale flessibilità si evidenzia molto nella gestione dell’allocazione dinamica della memoria. Malloc e realloc sono funzioni per l’allocazione dinamica delle memoria in C.
Due aree di memoria per allocare dati: Stack e heap
Il linguaggio gestisce due tipi di aree, dove alloca le variabili, lo stack e l’heap. Se non diversamente specificato dallo sviluppatore, il C memorizza tutti i dati nello stack e la rimozione di tali dati da quest’area avviene automaticamente quando perdono visibilità, o meglio, quando il blocco di codice, che le contiene, perde la visibilità o lo scope.
Tipo di memoria | Descrizione |
---|---|
Stack | È una parte statica, che contiene tutto ciò che sappiamo sarà allocato di certo (tutto ciò che è dichiarato nel codice quindi, come una variabile int ) |
Heap | È una parte dinamica, in cui la dimensione degli elementi può cambiare a “runtime” ovvero durante l’esecuzione del programma |
L’accesso allo stack è veloce ma nello stesso tempo le dimensioni in byte, a lui riservate, sono limitate, quindi, lo stack è un’area ben definita e limitata. Cosa diversa è per l’heap, tutto ciò che è espressamente allocato con malloc va a finire in questa area, e deve essere esplicitamente disallocato dal programmatore con la funzione free in C, pena il famigerato memory leak.
Esempio di utilizzo di malloc in C
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p; //dichiariamo il puntatore p come variabile intera
//allocazione dinamica adatta a contenere un numero intero
p=(int *) malloc(sizeof(int));
*p=50;
printf("*p vale %d\n", *p);
return 0;
}
Se l’allocazione non avviene correttamente il puntatore assume valore NULL.
È consigliabile dunque effettuare un test sul contenuto del puntatore, in modo tale che, se l’allocazione non avviene correttamente (per esempio se non abbiamo sufficiente spazio di memoria), si ha ad esempio un messaggio d’errore o la fuoriuscita dal programma.
Quindi basterà fare un controllo del tipo:
if (p==NULL)
printf("non è possibile allocare");
Gli smart Pointer in C++
Il C++ risolve questo problema con il ricorso ai puntatori intelligenti, chiamati anche Smart Pointer. Si fa ricorso a malloc, quindi, all’allocazione della memoria per le variabili da memorizzare nell’heap, quando si devono gestire, ad esempio, strutture dati o oggetti di modeste dimensioni. Ma per tutto ciò che appartiene ai tipi di dato nativi del C, ad esempio, come interi o stringhe, lo stack è da considerarsi la soluzione migliore.
Se sei interessato allo sviluppo web in generale, alle strutture dati dell’informatica, ed eseguire il codice col linguaggio di programmazione javascript, clicca qui.
Quando è necessario ricorrere ai puntatori in C ?
Come già precisato i puntatori sono un concetto fondamentale nel linguaggio di programmazione C e sono utilizzati in diverse situazioni per svolgere compiti specifici. Ecco alcune situazioni in cui è necessario ricorrere ai puntatori in C:
Gestione della memoria dinamica: Uno dei principali usi dei puntatori in C è la gestione della memoria dinamica. Puoi utilizzare le funzioni come malloc
, calloc
e realloc
per allocare dinamicamente la memoria durante l’esecuzione del programma. I puntatori vengono utilizzati per accedere a questi blocchi di memoria e lavorare con dati dinamici.
int *p = (int *)malloc(sizeof(int)); // Alloca dinamicamente un intero *p = 42; // Assegna un valore all'intero free(p); // Rilascia la memoria allocata
Passaggio di parametri per riferimento: In C, i parametri di funzione vengono di solito passati per valore, il che significa che viene creata una copia locale dei dati all’interno della funzione. Tuttavia, usando i puntatori, è possibile passare i dati per riferimento, consentendo alle funzioni di modificarli direttamente.
void increment(int *x) { (*x)++; // Incrementa il valore di x } int main() { int num = 5; increment(&num); printf("%d\n", num); // Stampa 6 return 0; }
Lavoro con array: Gli array in C sono essenzialmente puntatori alla prima posizione di memoria di una sequenza di dati. Utilizzando i puntatori, puoi accedere agli elementi di un array in modo efficiente e manipolare i dati.
int arr[] = {1, 2, 3, 4, 5}; int *ptr = arr; // Assegna il puntatore all'array printf("%d\n", *ptr); // Stampa 1 printf("%d\n", *(ptr + 2)); // Stampa 3
Strutture dati complesse: I puntatori consentono di creare strutture dati complesse come liste collegate, alberi, grafi e altre strutture dati dinamiche.
Ottimizzazione delle prestazioni: I puntatori possono essere utilizzati per ottimizzare le prestazioni in situazioni in cui è necessario accedere direttamente alla memoria anziché copiare dati. Ad esempio, lavorare con grandi blocchi di dati o manipolare buffer di input/output.
Accesso a funzioni dinamicamente: Puoi utilizzare puntatori a funzioni per fare riferimento e chiamare dinamicamente diverse funzioni in base alle esigenze del programma.
I puntatori possono essere potenti ma complessi da gestire, e un uso scorretto può portare a errori di segmentazione e altri bug gravi. È importante avere una buona comprensione di come funzionano i puntatori e una gestione accurata della memoria per scrivere codice C sicuro ed efficiente.