Allocazione Dinamica delle Stringhe in Linguaggio C
Applichiamo l'allocazione dinamica della memoria per lavorare con le stringhe in linguaggio C.
Le stringhe si prestano bene ad essere allocate dinamicamente in quanto non sempre è possibile prevedere in anticipo la loro lunghezza. In questa lezione, vedremo come allocare dinamicamente una stringa, come inizializzarla, come deallocarla e come realizzare delle funzioni che restituiscono stringhe allocate dinamicamente.
Stringhe e Allocazione Dinamica
L'allocazione dinamica della memoria è utilissima quando nei programmi scritti in C dobbiamo lavorare con le stringhe.
Le stringhe, infatti, in linguaggio C non sono altro che array di caratteri char. Tuttavia, non è semplice anticipare in fase di realizzazione di un programma quanto questi array debbano essere lunghi. Se scegliamo di usare array allocati staticamente, dobbiamo prevedere una lunghezza massima possibile. Tuttavia, questa non è la scelta ottima. In primo luogo, perché scegliendo una lunghezza troppo grande sprechiamo memoria, in secondo luogo perché potrebbe verificarsi il caso in cui la stringa sia più lunga di quanto previsto, causando un buffer overflow.
Allocando le stringhe in maniera dinamica stiamo postponendo la scelta della lunghezza dell'array, e possiamo decidere la lunghezza della stringa in fase di esecuzione del programma in base alle necessità.
Utilizzo della funzione malloc per allocare una stringa
Nella lezione precedente, abbiamo visto che la funzione malloc ci consente di allocare in maniera dinamica un blocco o area di memoria.
Il suo prototipo è il seguente:
#include <stdlib.h>
void *malloc(size_t size);
La funzione prende in ingresso una dimensione specificata dal parametro size che è espresso in byte. Essa restituisce un puntatore void * al blocco di memoria allocato. Nel caso la funzione non riesca ad allocare la memoria richiesta, restituirà il valore NULL.
Utilizzare malloc per allocare una stringa è molto semplice. Infatti, lo standard del linguaggio C impone che la dimensione di un char sia esattamente un byte. In altre parole:
1 == sizeof(char)
Pertanto, se vogliamo allocare una stringa di n caratteri, dobbiamo scrivere il seguente codice:
char *stringa = NULL;
stringa = (char *) malloc(n + 1);
Da notare che, nell'invocare malloc, abbiamo passato come argomento il valore n + 1 anziché n, in quanto dobbiamo allocare un byte in più per il carattere terminatore della stringa, ovvero il carattere '\0'.
Nel codice di sopra abbiamo inserito un cast esplicito al tipo char *. In realtà, lo standard C non richiede un cast esplicito, ma è una pratica comune per evitare warning del compilatore. Inoltre, in C++ il cast è obbligatorio, per cui se dal C si passa al C++ è bene abituarsi a questa pratica.
Un'altra buona pratica, come abbiamo già detto nella lezione precedente, è quella di controllare sempre se malloc ha restituito NULL. Se malloc restituisce NULL, significa che non è riuscita ad allocare la memoria richiesta. In tal caso, è buona norma terminare il programma o gestire l'errore in maniera opportuna.
if (stringa == NULL) {
printf("Errore: impossibile allocare la memoria richiesta\n");
exit(EXIT_FAILURE);
}
Ricapitolando:
Procedimento per allocare una stringa in modo dinamico in linguaggio C
Per allocare una stringa in modo dinamico in linguaggio C, il codice tipico da adoperare è il seguente:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Dove:
- Bisogna includere l'header file
stdlib.hper poter utilizzare la funzionemalloc; stringaè un puntatore a carattere che punta all'area di memoria allocata;nè la dimensione della stringa da allocare;- Alla riga 5 si invoca la
mallocpassando come argomenton + 1per allocare un byte in più per il carattere terminatore della stringa; - Il valore di ritorno della
mallocviene convertito tramite un cast esplicito al tipochar *e assegnato astringa; - Alla riga 7 si controlla se
mallocha restituitoNULL. In tal caso, si stampa un messaggio di errore e si termina il programma (oppure si gestisce l'errore in maniera opportuna).
Una volta allocata la stringa, la si può adoperare come qualsiasi altra stringa in C. Ad esempio, si può copiare una stringa in un'altra, si può concatenare una stringa a un'altra, si può stampare la stringa a video, ecc.
Ovviamente, quando non se ne ha più bisogno, è buona norma deallocare la memoria utilizzata. Per deallocare la memoria di una stringa allocata dinamicamente, si può utilizzare la funzione free.
In tal caso la deallocazione è molto semplice. Basta invocare la funzione free sul puntatore alla stringa:
free(stringa);
Deallocare una stringa allocata dinamicamente in linguaggio C
Per deallocare una stringa allocata dinamicamente, basta invocare la funzione free passando come argomento il puntatore alla stringa:
free(stringa);
Inizializzazione di una stringa allocata dinamicamente
La funzione malloc non inizializza la memoria che alloca. Pertanto, quando allochiamo dinamicamente una stringa, il contenuto della stringa è indefinito. In altre parole, non possiamo sapere cosa c'è all'interno della stringa.
Tornando al codice di sopra, il puntatore stringa punterà ad un array non inizializzato di n + 1 caratteri:
Un modo semplice di inizializzare una stringa allocata dinamicamente è quello di adoperare la funzione strcpy per copiare una stringa letterale al suo interno.
Ad esempio, volendo inizializzare la stringa con la parola "ciao", possiamo scrivere:
#include <string.h>
/* ... */
strcpy(stringa, "ciao");
Adesso, i primi cinque caratteri dell'array saranno:
Inizializzare una stringa allocata dinamicamente in linguaggio C
Per inizializzare una stringa allocata dinamicamente, si può adoperare la funzione strcpy per copiare una stringa letterale al suo interno:
#include <string.h>
/* ... */
strcpy(stringa, "stringa di inizializzazione");
Esempio di Allocazione Dinamica di una Stringa
Di seguito, mostriamo un esempio completo di allocazione dinamica di una stringa in linguaggio C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
Il programma chiede all'utente di inserire la lunghezza della stringa da allocare. Successivamente, alloca dinamicamente la stringa e richiede all'utente di inserire la stringa. Infine, stampa la stringa allocata dinamicamente e dealloca la memoria.
Per leggere la stringa, abbiamo definito una funzione leggi_riga molto simile a quella che abbiamo mostrato nella lezione sulla lettura di stringhe da console.
Funzioni e Stringhe allocate dinamicamente
Attraverso l'allocazione dinamica, possiamo realizzare delle funzioni che creano e restituiscono stringhe allocate dinamicamente. Ossia, funzioni che restituiscono nuove stringhe che non esistevano prima dell'invocazione della funzione stessa.
Proviamo, ad esempio, a realizzare una funzione che prende due stringhe in ingresso e restituisce una nuova stringa che è la concatenazione delle due.
La libreria standard del C fornisce la funzione strcat che abbiamo già visto, ma essa funziona in modo diverso: modifica la stringa di partenza aggiungendo la seconda stringa passata. Noi vogliamo realizzare una funzione che restituisca una nuova stringa.
Per fare ciò, dobbiamo allocare dinamicamente una nuova stringa, copiare le due stringhe in essa e restituire il puntatore alla nuova stringa.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
La funzione concatena_stringhe prende due stringhe in ingresso e procede in questo modo:
- Calcola la lunghezza della prima stringa
s1e la memorizza inn1, e la lunghezza della seconda stringas2e la memorizza inn2; per fare questo si affida alla funzione di libreriastrlen; - Alloca dinamicamente una nuova stringa
nuova_stringadi dimensionen1 + n2 + 1(perché dobbiamo allocare un byte in più per il carattere terminatore della stringa); per fare questo, usa la funzionemalloc; - Controlla se la malloc ha avuto successo; in caso contrario, restituisce
NULL; - Copia la prima stringa
s1nella nuova stringanuova_stringautilizzando la funzionestrcpy; - Concatena la seconda stringa
s2alla nuova stringanuova_stringautilizzando la funzionestrcat; - Restituisce il puntatore alla nuova stringa.
Di seguito, mostriamo un esempio di utilizzo della funzione concatena_stringhe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Il programma definisce due stringhe s1 e s2, e invoca la funzione concatena_stringhe.
Un dettaglio fondamentale è che la funzione concatena_stringhe restituisce un puntatore a una stringa allocata dinamicamente. Pertanto, è necessario deallocare la memoria utilizzata dalla stringa restituita invocando la funzione free.
Stringhe allocate dinamicamente e restituite da funzioni
Quando una funzione restituisce una stringa allocata dinamicamente, è necessario deallocare la memoria utilizzata dalla stringa invocando la funzione free.
Conclusioni
In questa lezione abbiamo applicato l'allocazione dinamica della memoria per lavorare con le stringhe.
I concetti chiave che abbiamo imparato sono:
- L'allocazione dinamica delle stringhe in C ci permette di posticipare la scelta della lunghezza della stringa;
- Per allocare una stringa dinamicamente, dobbiamo utilizzare la funzione
malloce passare come argomento la dimensione della stringa più uno; questo perché dobbiamo allocare un byte in più per il carattere terminatore della stringa; - È buona norma controllare se
mallocha restituitoNULLe gestire l'errore in maniera opportuna; - Per deallocare la memoria utilizzata da una stringa allocata dinamicamente, possiamo utilizzare la funzione
free; - Per inizializzare una stringa allocata dinamicamente, possiamo utilizzare la funzione
strcpy; - Possiamo realizzare delle funzioni che restituiscono stringhe allocate dinamicamente.
Inoltre, abbiamo mostrato un esempio completo di allocazione dinamica di una stringa e come realizzare una funzione che restituisce una nuova stringa che è la concatenazione di due stringhe.
Nella prossima lezione ci concentreremo sull'allocazione dinamica di array. Le stringhe sono array di caratteri, ma possiamo allocare dinamicamente anche array di altri tipi di dati.