Menu

Stringhe in C: array di char, terminatore nullo ed esempi

Il C non ha un tipo stringa: una stringa è un array di char che termina con un byte '\0'. Questa pagina costruisce questo modello mentale e poi tratta i letterali, la stampa, lo scorrimento e perché non puoi copiare una stringa con =.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

La maggior parte dei linguaggi ti dà un tipo string che conosce la propria lunghezza. Il C no. In C una stringa è solo un array di char con una regola in più: il testo è seguito da un byte con valore zero, scritto '\0' e chiamato terminatore nullo. Tutto il resto sulle stringhe C discende da questa unica convenzione.

Una stringa è un array di char più '\0'

Si dichiara una stringa inizializzando un array di char con un letterale:

L'array è fatto di quattro byte, non tre:

name:  +-----+-----+-----+------+
       | 'A' | 'd' | 'a' | '\0' |
       +-----+-----+-----+------+
indice:   0     1     2     3

sizeof(name) vale 4. Il compilatore ha contato i caratteri, ne ha aggiunto uno per il terminatore e ha dimensionato l'array per te. Quel byte in più è il motivo per cui char name[3] = "Ada"; è un bug in agguato: il testo ci sta ma il terminatore no, quindi nessuna funzione a valle può capire dove finisce la stringa.

Quando vuoi spazio per cambiare il contenuto in seguito, indica tu la dimensione e lascia margine:

char name[32] = "Ada";   /* 3 caratteri, un terminatore, 28 byte liberi */

I byte non usati vengono riempiti di zeri dall'inizializzatore, cosa innocua.

Perché il terminatore conta

Nessuna funzione C riceve una lunghezza. printf("%s", s) riceve un indirizzo e cammina in avanti stampando byte finché non incontra '\0'. Quindi un array di char senza terminatore non è una stringa, e trattarlo come tale legge qualunque memoria si trovi dopo.

La regola da memorizzare: sizeof ti dà lo spazio di memoria, strlen ti dà il testo. Per char buf[32] = "Ada", sizeof buf vale 32 e strlen(buf) vale 3. Rispondono a domande diverse, e confonderli è uno dei bug più comuni in C.

Letterali stringa e array di char

Queste due righe sembrano simili e si comportano in modo molto diverso:

char  a[] = "hello";   /* un array tuo, inizializzato dal letterale */
char *p   = "hello";   /* un puntatore rivolto al letterale stesso */

a è un array di 6 byte con una copia privata del testo. Puoi modificarlo. p punta dentro i dati stringa di sola lettura del programma; il letterale è condiviso, e scriverci tramite p è comportamento indefinito che di solito va in crash a runtime, non in compilazione.

Scrivi const char * ogni volta che un puntatore punta a un letterale. Così il compilatore rifiuta la scrittura in fase di compilazione invece di lasciarla diventare un crash. Scegliere tra le due forme è in realtà una questione di puntatori e array, trattata nella pagina su puntatori e array.

Un'altra differenza da conoscere: sizeof a vale 6, mentre sizeof p è la dimensione di un puntatore (8 sulla maggior parte dei sistemi), indipendentemente da quanto è lungo il testo.

Stampare e leggere stringhe

%s stampa una stringa; %c stampa un singolo carattere.

Per l'input, evita del tutto gets: è stata rimossa dal linguaggio perché non si può usare in modo sicuro. Usa fgets, che riceve la dimensione del buffer e non va in overflow:

fgets conserva l'a capo che hai premuto, cosa che nella stringa non vuoi quasi mai. La riga con strcspn trova il primo '\n' e lo sovrascrive con un terminatore: un modo compatto e standard per toglierlo.

Scorrere una stringa carattere per carattere

Dato che la fine è segnata e non contata, il ciclo idiomatico controlla il carattere stesso:

La condizione text[i] != '\0' spesso viene abbreviata in text[i], dato che '\0' vale zero e quindi è falso. Sono corrette entrambe; la forma esplicita è più facile da leggere mentre impari.

Puoi scorrere con un puntatore invece che con un indice, in un C altrettanto idiomatico:

Il cast a unsigned char prima di chiamare toupper non è decorazione: le funzioni di <ctype.h> sono indefinite per valori negativi, e un char semplice può essere negativo per i byte sopra 127.

Non puoi copiare una stringa con =

Questo è il muro contro cui sbatte ogni principiante:

char a[10];
a = "hello";        /* error: assignment to expression with array type */

char b[10] = "hi";
char c[10];
c = b;              /* errore, per lo stesso motivo */

Il nome di un array non è un valore modificabile, quindi = non ha niente da fare. L'unico punto in cui la scorciatoia funziona è l'inizializzazione nella dichiarazione, che è un'operazione diversa eseguita dal compilatore.

Per copiare a runtime, copia i byte con strcpy di <string.h>:

Lo stesso ragionamento spiega perché == non confronta le stringhe. if (a == b) confronta due indirizzi, quindi è falso per due array diversi che contengono lo stesso testo. Usa invece strcmp(a, b) == 0. Entrambe le funzioni e le loro varianti più sicure sono trattate nelle funzioni sulle stringhe.

Array di stringhe

Una lista di stringhe è un array di puntatori (per testo fisso che leggi soltanto) oppure un array bidimensionale di char (quando ogni voce deve essere modificabile):

L'array di puntatori memorizza tre indirizzi verso letterali di sola lettura: compatto, ma non modificabile. La forma char[3][16] riserva 48 byte di memoria tua, quindi ogni riga si può riscrivere.

Errori comuni

  • Dimenticare il byte del terminatore. char s[5] = "hello" non ha spazio per '\0'. Dimensiona per strlen + 1.
  • Usare sizeof quando intendevi strlen. sizeof su un puntatore dà la dimensione del puntatore, non la lunghezza del testo.
  • Scrivere tramite un char * che punta a un letterale. Dichiara questi puntatori come const char *.
  • Confrontare con ==. Confronta gli indirizzi. Usa strcmp.
  • Leggere con scanf("%s", buf). Non ha limiti di dimensione e va allegramente in overflow; preferisci fgets.

Domande frequenti

Come si dichiara una stringa in C?

Come array di char: char name[] = "Ada";. Il C non ha un tipo stringa integrato: quell'array contiene 'A', 'd', 'a' e un quarto byte nascosto, '\0', che segna la fine. Se vuoi spazio per modificare il testo in seguito, dai all'array una dimensione esplicita: char name[32] = "Ada";.

Cos'è il terminatore nullo in C?

Il byte '\0' (valore zero) che segna dove finisce una stringa. Ogni funzione standard, come printf("%s"), strlen o strcpy, legge in avanti finché non trova quel byte. Un array di char senza terminatore non è una stringa, e passarlo a quelle funzioni le fa leggere oltre la fine della tua memoria.

Perché non posso assegnare una stringa con = in C?

Perché un array non è un valore che puoi copiare con =. char a[10]; a = "hi"; non compila. Copia invece i caratteri con strcpy(a, "hi") di <string.h>, oppure inizializza nella dichiarazione, char a[10] = "hi";, che è l'unico punto in cui la scorciatoia funziona.

Qual è la differenza tra char * e char [] in C?

char s[] = "hi" crea un array modificabile con una copia privata del testo. char *p = "hi" crea un puntatore a un letterale stringa, che vive in memoria di sola lettura: leggerlo va bene, scriverci attraverso manda in crash il programma. Usa l'array quando intendi modificare, il puntatore quando devi solo leggere.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA