Ogni funzione di <string.h> è un piccolo ciclo su un array di char che si ferma al terminatore nullo. Una volta chiarito questo, la libreria smette di sembrare un elenco di nomi criptici e diventa la manciata di cicli che avresti scritto tu. Questa pagina copre quelle che fanno quasi tutto il lavoro reale, e le regole sulle dimensioni che impediscono loro di scrivere oltre la fine dei tuoi buffer.
Tutto quello che vedi qui richiede un solo include:
#include <string.h>
strlen: quanti caratteri
strlen conta i byte fino al terminatore, escluso. Restituisce un size_t, quindi stampalo con %zu.
strlen vale 6; sizeof vale 32. Il primo è il testo, il secondo è lo spazio di memoria: nella pagina sulle stringhe trovi perché questa distinzione conta così tanto.
strlen percorre tutta la stringa a ogni chiamata, quindi non metterla nella condizione di un ciclo sulla stessa stringa che non cambia:
/* riconta l'intera stringa a ogni iterazione */
for (size_t i = 0; i < strlen(s); i++) { ... }
/* conta una volta sola */
size_t n = strlen(s);
for (size_t i = 0; i < n; i++) { ... }
strcpy e strncpy: copiare
strcpy(dst, src) copia i caratteri più il terminatore. Non ha idea di quanto sia grande dst, quindi chi chiama deve garantire che dst contenga almeno strlen(src) + 1 byte.
strncpy riceve un numero massimo, ma ha una trappola famosa: quando la sorgente è lunga almeno n byte copia esattamente n caratteri e nessun terminatore. Termina sempre tu la stringa:
sizeof dst - 1 più il terminatore esplicito è lo schema da memorizzare. (strncpy riempie anche le sorgenti corte con zeri fino a n byte, lavoro sprecato per i buffer grandi: è stata progettata per record a larghezza fissa, non per la sicurezza.)
strcat e strncat: unire
strcat(dst, src) accoda src alla fine di ciò che c'è già in dst. La destinazione deve contenere entrambe le stringhe più un terminatore, e deve già contenere una stringa valida: accodare a memoria non inizializzata è comportamento indefinito.
Nota char path[64] = "/home/ada"; invece di char path[64];: è l'inizializzatore a renderla una stringa valida per il primo strcat.
strncat(dst, src, n) accoda al massimo n caratteri e aggiunge sempre un terminatore, quindi n è lo spazio rimanente, non la dimensione totale del buffer:
strncat(dst, src, sizeof dst - strlen(dst) - 1);
Chiamate ripetute a strcat riscansionano ogni volta la destinazione per trovarne la fine. Costruire una stringa lunga in un ciclo in questo modo ha costo quadratico; per qualsiasi cosa di una certa dimensione, tieni traccia tu della posizione di scrittura oppure usa snprintf, trattata nella conversione di stringhe.
strcmp: confrontare
== confronta gli indirizzi, quindi è falso per due array distinti che contengono lo stesso testo. strcmp confronta i caratteri e restituisce il segno della differenza.
Tre regole:
- Per l'uguaglianza controlla
strcmp(a, b) == 0. Scrivereif (strcmp(a, b))significa "se sono diverse", e quasi tutti lo leggono al contrario. - È definito solo il segno. Non confrontare il risultato con
1o-1. - L'ordinamento segue il valore dei byte, quindi in ASCII
"Zebra"viene prima di"apple". Per un confronto che ignori maiuscole e minuscole, porta prima entrambe le copie in minuscolo: la diffusissimastrcasecmpè un'estensione POSIX, non C standard.
strncmp(a, b, n) confronta solo i primi n caratteri, ed è il modo pulito per controllare un prefisso:
strchr e strstr: cercare
strchr(s, ch) trova la prima occorrenza di un carattere; strrchr trova l'ultima. strstr(haystack, needle) trova una sottostringa. Tutte e tre restituiscono un puntatore dentro la stringa originale, oppure NULL quando non c'è corrispondenza.
Sottraendo l'inizio della stringa dal puntatore restituito ottieni l'indice; il suo tipo è ptrdiff_t e si stampa con %td. Dato che il risultato punta dentro l'array originale, at + 1 è il resto della stringa senza alcuna copia: un idioma C molto comune.
Controlla sempre NULL prima di dereferenziare. strchr(email, '@') + 1 su una stringa senza @ calcola un indirizzo a partire da NULL, e il programma smette di essere definito.
Dividere una stringa
Non esiste una funzione split, ma strchr più un terminatore fanno il lavoro sul posto. Questo esempio divide un indirizzo email in utente e dominio:
memcpy copia un numero esatto di byte senza badare ai terminatori, ed è quello che vuoi quando conosci già la lunghezza. Il terminatore viene poi scritto a mano.
Un esempio completo: normalizzare un nome
Mettendo insieme tutto il giro: rifilare, confrontare, copiare e unire.
Ogni scrittura è limitata dal sizeof della destinazione stessa, e ogni strncpy è seguita da un terminatore esplicito. Questa disciplina, applicata con coerenza, è ciò che rende sicuro il codice C sulle stringhe.
Regole sulle dimensioni da ricordare
strcpyrichiedestrlen(src) + 1byte liberi nella destinazione.strcatrichiedestrlen(dst) + strlen(src) + 1.- Dopo
strncpy, scrivi tudst[n - 1] = '\0'. - Per
strncat, il conteggio è lo spazio rimasto:sizeof dst - strlen(dst) - 1. - Controlla che
strchrestrstrnon abbiano restituitoNULLprima di usarne il risultato. - Usa
sizeof dstsolo quandodstè un vero array. Dentro una funzione che ricevechar *dst,sizeofdà la dimensione del puntatore: passa la lunghezza del buffer come parametro separato.
Domande frequenti
Cosa restituisce strcmp in C?
Zero quando le due stringhe sono identiche, un valore negativo quando la prima viene prima della seconda in ordine e un valore positivo quando viene dopo. Conta solo il segno: non dare mai per scontato che restituisca -1 o 1. Il controllo che ti serve è quasi sempre if (strcmp(a, b) == 0).
Perché non posso confrontare le stringhe con == in C?
a == b confronta i due indirizzi, non i caratteri. Due array distinti che contengono "gatto" stanno a indirizzi diversi, quindi il confronto è falso anche se il testo coincide. Usa strcmp(a, b) == 0 di <string.h>.
Qual è la differenza tra strcpy e strncpy?
strcpy copia fino al '\0' della sorgente senza sapere quanto è grande la destinazione. strncpy si ferma dopo al massimo n byte, ma se la sorgente è lunga almeno così non copia alcun terminatore, quindi devi scrivere tu dst[n - 1] = '\0';. Nessuna delle due è sicura in automatico; la disciplina sulle dimensioni spetta a te.
Come si cerca una sottostringa in C?
strstr(haystack, needle) restituisce un puntatore alla prima occorrenza, oppure NULL se non ce n'è nessuna. Dato che restituisce un puntatore dentro la stringa originale, found - haystack ti dà l'indice. Usa strchr quando cerchi un singolo carattere.