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.h
per 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
malloc
passando come argomenton + 1
per allocare un byte in più per il carattere terminatore della stringa; - Il valore di ritorno della
malloc
viene convertito tramite un cast esplicito al tipochar *
e assegnato astringa
; - Alla riga 7 si controlla se
malloc
ha 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
s1
e la memorizza inn1
, e la lunghezza della seconda stringas2
e la memorizza inn2
; per fare questo si affida alla funzione di libreriastrlen
; - Alloca dinamicamente una nuova stringa
nuova_stringa
di 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
s1
nella nuova stringanuova_stringa
utilizzando la funzionestrcpy
; - Concatena la seconda stringa
s2
alla nuova stringanuova_stringa
utilizzando 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
malloc
e 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
malloc
ha restituitoNULL
e 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.