char[] sau string? Lungime, capacitate și terminator
Înțelege locul terminatorului nul, diferența dintre strlen și sizeof și ce se schimbă când reprezinți același text cu std::string.
În acest material
Un text de cinci caractere nu încape ca șir C într-un tablou char de numai cinci celule. Mai trebuie o celulă pentru terminatorul nul. Această diferență mică explică multe citiri incomplete și accesări în afara memoriei.
Trei numere care nu trebuie confundate
Capacitatea este spațiul rezervat. Lungimea este numărul caracterelor utile. Pentru un șir C valid, terminatorul \0 apare imediat după ultimul caracter util. Textul mere are lungimea 4, dar reprezentarea sa ca șir C necesită cel puțin 5 celule.
Dacă rezervi char s[101], poți păstra până la 100 de caractere utile și terminatorul. sizeof(s) exprimă dimensiunea tabloului în acel context, nu lungimea textului citit. strlen(s) numără caracterele până la primul terminator și presupune că există un șir C valid.
Textul mere ocupă patru celule pentru litere și încă una pentru terminatorul nul, diferit de cifra zero.Mărește schema ↗
Terminatorul nu este cifra zero
Caracterul '0' este cifra vizibilă zero. \0 este caracterul nul care marchează sfârșitul șirului C. Confundarea lor poate opri un algoritm la primul zero dintr-un cod numeric sau poate face parcurgerea să continue dincolo de text.
Pentru a0b, lungimea este 3. Cifra din mijloc nu încheie șirul. Când construiești manual un rezultat în char[], scrie terminatorul după ultima poziție folosită, inclusiv când rezultatul este gol.
Ce se schimbă la transmiterea într-o funcție
Un parametru de forma char s[] este tratat ca pointer în parametrii funcției. sizeof(s) acolo nu îți mai spune capacitatea tabloului original. Dacă funcția are nevoie de capacitate, transmite-o separat; dacă are nevoie de lungime, stabilește un contract clar pentru aceasta.
Nici un pointer către primul caracter nu garantează automat existența terminatorului. Funcțiile care prelucrează șiruri C trebuie să primească date conforme cu această convenție.
std::string gestionează reprezentarea, nu sensul cerinței
Un string își cunoaște dimensiunea prin size() și își gestionează memoria. Pentru parcurgerea caracterelor utile folosești indicii mai mici decât size sau o buclă peste caractere. Nu trebuie să scrii manual un terminator după fiecare adăugare.
Totuși, string nu transformă orice algoritm într-unul corect. Accesul cu un indice nepotrivit, comparația greșită ori o citire care pierde spațiile rămân probleme. Nici numărul de octeți al unui text UTF-8 nu coincide obligatoriu cu numărul literelor percepute de cititor.
Teste care verifică reprezentarea
Folosește textul gol, un singur caracter, un text cu cifra zero și o linie la lungimea maximă permisă. Pentru citire cu spații, verifică o propoziție, nu doar un cuvânt. Pentru construirea unui rezultat, testează și cazul în care toate caracterele sunt eliminate.
Începe cu char[], string și citirea liniilor, continuă cu funcțiile pentru șiruri și revizuiește transmiterea tablourilor ca parametri înainte să muți prelucrarea într-o funcție.
Întrebări frecvente
Cât spațiu trebuie alocat pentru un șir char de n caractere?
Cel puțin n+1 celule, deoarece după cele n caractere utile trebuie să încapă terminatorul nul \0. De exemplu, textul mere cere minimum cinci celule.
Care este diferența dintre lungime și capacitate la char[]?
Lungimea numără caracterele utile înainte de terminator. Capacitatea reprezintă toate celulele rezervate în tablou. Un tablou de capacitate 101 poate păstra un șir C de cel mult 100 de caractere utile.
Trebuie să adaug manual terminatorul la std::string?
Nu pentru operațiile obișnuite ale lui std::string; acesta își gestionează reprezentarea. Lungimea logică se obține prin size(). La un șir C construit manual, terminatorul rămâne responsabilitatea programului.