Determinare la lunghezza di una stringa in linguaggio C
In linguaggio C le stringhe sono array di caratteri terminati dal carattere speciale \0
. Per determinare la lunghezza di una stringa, cioè il numero di caratteri che la compongono, non possiamo usare l'operatore sizeof
, ma dobbiamo usare delle funzioni di libreria apposite definite in string.h
.
Tali funzioni sono strlen
, strnlen
e strnlen_s
. La funzione strlen
restituisce la lunghezza di una stringa, strnlen
restituisce la lunghezza di una stringa fino ad un limite superiore e strnlen_s
è una versione sicura di strnlen
introdotta nello standard C11.
In questa lezione studieremo queste funzioni nel dettaglio e ne analizzeremo una possibile implementazione.
Funzione strlen
- Lunghezza di una stringa
Sappiamo che una stringa in linguaggio C è una sequenza di caratteri che termina con il carattere terminatore \0
.
In C non possiamo usare l'operatore sizeof
per determinare la lunghezza di una stringa per due motivi:
-
Se applicato ad un array di caratteri,
sizeof
restituisce la dimensione dell'array, non la lunghezza della stringa:char s[128] = "Hello"; printf("%zu\n", sizeof(s)); // stampa "128"
In questo caso
sizeof
restituisce la dimensione dell'arrays
, che è 128 byte, ma la stringas
contiene solo 5 caratteri più il terminatore\0
. -
Se applicato ad un puntatore,
sizeof
restituisce la dimensione del puntatore, non la lunghezza della stringa:char *s = "Hello"; printf("%zu\n", sizeof(s)); // stampa "8" su un sistema a 64 bit
In questo caso
sizeof
restituisce la dimensione del puntatores
, che è 8 byte su un sistema a 64 bit.
Per determinare la lunghezza di una stringa, dobbiamo usare la funzione strlen
della libreria standard string.h
.
La funzione strlen
accetta un puntatore a una stringa e restituisce il numero di caratteri della stringa, escluso il terminatore \0
. La sua firma è:
size_t strlen(const char *s);
Vediamo qualche esempio:
#include <stdio.h>
#include <string.h>
int main() {
char s[128] = "Hello";
char *t = "World";
char *v = "";
printf("La lunghezza di s è %zu\n", strlen(s)); // stampa "5"
printf("La lunghezza di t è %zu\n", strlen(t)); // stampa "5"
printf("La lunghezza di v è %zu\n", strlen(v)); // stampa "0"
return 0;
}
Questo esempio ci consente di effettuare alcune importanti osservazioni:
- Nel primo caso,
strlen(s)
, la funzione restituisce 5, che è la lunghezza della stringas
escluso il terminatore\0
. Non restituisce 128, che è la dimensione dell'arrays
. - Nel secondo caso,
strlen(t)
, la funzione restituisce 5, che è sempre la lunghezza della stringat
escluso il terminatore\0
. Non restituisce 8, che è la dimensione del puntatoret
. - Nell'ultimo caso, abbiamo invocato
strlen
su una stringa vuota,v
, che è composta esclusivamente dal terminatore\0
. La funzione restituisce 0, dato che la stringa è composta dal singolo terminatore\0
.
Funzione strlen
La funzione strlen
accetta un puntatore a una stringa e restituisce il numero di caratteri della stringa, escluso il terminatore \0
.
Essa è definita nel file di intestazione string.h
:
#include <string.h>
La sua firma è:
size_t strlen(const char *s);
La funzione strlen
non è sicura
La funzione strlen
ha due gravi problemi:
- Non verifica se il puntatore passato è valido. Se il puntatore non punta ad una stringa valida, il comportamento è indefinito.
- Non verifica se la stringa è effettivamente terminata con il carattere
\0
. Se la stringa non è terminata correttamente, il comportamento è indefinito.
Funzione strnlen
- Lunghezza di una stringa con limite
Per evitare il problema di strlen
, possiamo usare la funzione strnlen
. Questa funzione, in realtà, non è una funzione standard del linguaggio C. Ma molti compilatori, tra cui GCC, Clang e Microsoft Visual C++, la forniscono come estensione della libreria standard.
La funzione strnlen
accetta un puntatore a una stringa e un limite superiore e restituisce il numero di caratteri della stringa, escluso il terminatore \0
, fino al limite superiore. La sua firma è:
size_t strnlen(const char *s, size_t maxlen);
In particolare, se la stringa s
è terminata prima di raggiungere maxlen
, la funzione restituisce la lunghezza della stringa. Se la stringa non è terminata prima di raggiungere maxlen
, la funzione restituisce maxlen
.
Vediamo un esempio:
#include <stdio.h>
#include <string.h>
int main() {
char s[128] = "Hello, World!";
/* Stampa 13 */
printf("La lunghezza di s è %zu\n", strnlen(s, 128));
/* Stampa 5 */
printf("La lunghezza di s è %zu\n", strnlen(s, 5));
return 0;
}
In questo esempio, la stringa s
è "Hello, World!", che ha una lunghezza di 13 caratteri. Tuttavia, quando invochiamo strnlen
con limiti differenti, otteniamo risultati diversi:
- Quando invochiamo
strnlen(s, 128)
, la funzione restituisce 13, che è la lunghezza della stringas
escluso il terminatore\0
. - Quando invochiamo
strnlen(s, 5)
, la funzione restituisce 5, che è il limite superiore passato come argomento.
Funzione strnlen
La funzione strnlen
accetta un puntatore a una stringa e un limite superiore e restituisce il numero di caratteri della stringa, escluso il terminatore \0
, fino al limite superiore.
Essa, tipicamente, è definita come estensione della libreria standard in molti compilatori, tra cui GCC, Clang e Microsoft Visual C++ ed è definita nel file di intestazione string.h
:
#include <string.h>
La sua firma è:
size_t strnlen(const char *s, size_t maxlen);
La funzione strnlen
non è standard
La funzione strnlen
non è definita nello standard C, ma è una estensione di alcune librerie standard come GNU libc e Microsoft Visual C++.
Funzione strnlen_s
- Lunghezza di una stringa con limite e controllo di errore
A partire dallo standard C11, è stata introdotta la funzione strnlen_s
che è una versione sicura di strnlen
. La funzione strnlen_s
accetta un puntatore a una stringa e il limite superiore e restituisce il numero di caratteri della stringa, escluso il terminatore \0
, fino al limite superiore. In questo è simile in tutto e per tutto a strnlen
.
Tuttavia, strnlen_s
controlla anche se il puntatore passato è valido. Se il puntatore non punta ad una stringa valida restituisce 0.
La sua firma è:
size_t strnlen_s(const char *s, size_t maxlen);
Funzione strnlen_s
La funzione strnlen_s
, definita nello standard C11, accetta un puntatore a una stringa e un limite superiore e restituisce il numero di caratteri della stringa, escluso il terminatore \0
, fino al limite superiore. Se la stringa non è valida, restituisce 0.
Essa è definita nel file di intestazione string.h
:
#include <string.h>
La sua firma è:
size_t strnlen_s(const char *s, size_t maxlen);
Vediamo un esempio:
#include <stdio.h>
#include <string.h>
int main() {
char s[128] = "Hello, World!";
/* Stampa 13 */
printf("La lunghezza di s è %zu\n", strnlen_s(s, 128));
/* Stampa 5 */
printf("La lunghezza di s è %zu\n", strnlen_s(s, 5));
/* Stampa 0 */
printf("La lunghezza di s è %zu\n", strnlen_s(NULL, 128));
return 0;
}
Come si vede da questo esempio, la funzione strnlen_s
si comporta come strnlen
. Tuttavia, se il puntatore passato non è valido, la funzione restituisce 0 come si vede nell'ultimo caso.
Implementazione di strlen
, strnlen
e strnlen_s
A scopo didattico, è interessante vedere come possiamo implementare le funzioni strlen
, strnlen
e strnlen_s
in linguaggio C. Ciò ci consentirà di vedere, inoltre, quali siano i problemi di sicurezza che queste funzioni hanno.
Partiamo da una semplice implementazione di strlen
che chiameremo mia_strlen
:
1 2 3 4 5 6 7 8 9 |
|
Analizziamo il codice:
- Riga 1: Definiamo la funzione
mia_strlen
che accetta un puntatore a una stringas
e restituisce la lunghezza della stringa; - Riga 2: Definiamo un puntatore
p
e lo inizializziamo con il puntatores
, ossia con la stringa passata come argomento; - Riga 4-6: Iteriamo finché non raggiungiamo il terminatore
\0
. Ad ogni iterazione incrementiamo il puntatorep
; - Riga 8: Restituiamo la differenza tra il puntatore
p
e il puntatores
, che è la lunghezza della stringa.
In particolare, quest'ultimo punto è interessante: la differenza tra due puntatori è il numero di elementi tra i due puntatori. In questo caso, la differenza tra p
e s
è il numero di caratteri tra p
e s
, ossia la lunghezza della stringa.
Nel codice di sopra, possiamo riscrivere il ciclo alle righe 4-6 in un modo più conciso:
while (*p++);
Questo ciclo incrementa il puntatore p
e valuta il valore puntato da p
finché non raggiunge il terminatore \0
che, essendo pari al codice ASCII 0, è valutato come falso e quindi chiude il ciclo.
Se osserviamo bene il codice, possiamo capire i problemi che questa funzione presenta:
- Non controlla se il puntatore
s
è valido. Ses
èNULL
, il comportamento è indefinito; - Non controlla se la stringa è effettivamente terminata con il carattere
\0
. Se la stringa non è terminata correttamente, il comportamento è indefinito. Infatti in questo caso, il ciclo while alla riga 4 potrebbe andare avanti all'infinito.
La funzione strnlen
, invece, accetta un limite superiore e risolve il secondo problema.
Vediamo una possibile implementazione di strnlen
che chiameremo mia_strnlen
:
1 2 3 4 5 6 7 8 9 |
|
L'implementazione è molto simile a quella di strlen
, con una differenza: dobbiamo controllare che non superiamo il limite maxlen
. Per fare ciò, aggiungiamo una condizione al ciclo while
, riga 4, che controlla sia il terminatore \0
che la differenza tra p
e s
.
In questo modo, la funzione mia_strnlen
restituisce la lunghezza della stringa fino al limite maxlen
.
Questa funzione è, certamente, più sicura di strlen
, ma presenta ancora un problema: non controlla se il puntatore s
è valido. Se s
è NULL
, il comportamento è indefinito.
La funzione strnlen_s
, infine, risolve anche il primo problema, ossia controlla se il puntatore s
è valido.
Proviamo, infine, a implementare la funzione strnlen_s
. Realizziamone una versione che chiameremo mia_strnlen_s
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
In questo caso, la funzione mia_strnlen_s
è identica a mia_strnlen
, con l'aggiunta di una condizione iniziale, righe 2-4, che controlla se il puntatore s
è valido. Se il puntatore s
è NULL
, la funzione restituisce 0.
In questo modo, la funzione mia_strnlen_s
è sicura sia rispetto al puntatore s
che al limite maxlen
.
In Sintesi
In questa lezione abbiamo visto come determinare la lunghezza di una stringa in linguaggio C. Abbiamo visto che non possiamo usare l'operatore sizeof
per determinare la lunghezza di una stringa e che dobbiamo usare la funzione strlen
della libreria standard string.h
.
Abbiamo visto che la funzione strlen
non è sicura, in quanto non controlla se il puntatore passato è valido e se la stringa è effettivamente terminata con il carattere \0
.
Per evitare questi problemi, abbiamo visto la funzione strnlen
, che accetta un limite superiore e restituisce la lunghezza della stringa fino al limite superiore. Inoltre, abbiamo visto la funzione strnlen_s
, introdotta nello standard C11, che controlla anche se il puntatore passato è valido.
Infine, abbiamo visto come implementare le funzioni strlen
, strnlen
e strnlen_s
in linguaggio C e perché le prime due funzioni non sono sicure.