Tipul char
— Recapitulare
- Tipul
char
este folosit pentru stocarea caracterelor. - Caracterele ce pot fi reprezentate pe tipul
char
sunt cele din codul ASCII.
- În mod intern, tipul
char
stochează de fapt întregi (coduri ASCII, nu caractere propriu-zise). - Fiind un tip întreg cu semn, pe biți,
char
stochează valori cuprinse între și (în total ), însă pe noi ne interesează doar cele pozitive.
Operații pe Tipul char
- În codul ASCII, literele ocupă poziții consecutive, în ordinea dată de alfabet.
- Acest lucru este valabil atât pentru cele mici, cât și pentru cele mari; inclusiv pentru cifre.
- Astfel, putem efectua adunări, scăderi și comparații.
'c' < 'p' // true
'F' > 'X' // false
'd' + 3 // 'g'
'E' - 'A' + 'a' // 4 + 'a' == 'e'
- Operațiile aritmetice ce implică un
char
transformă automat rezultatul într-unint
. - Prin urmare, ocazional, la afișări, vom avea nevoie de o conversie explicită la
char
.
cout << 'A' + 3 << '\n'; // 68
cout << char('A' + 3) << '\n'; // D
Pointeri — Noțiuni de Bază
- Pointerii sunt un tip (de tipuri) de date special, ce stochează adrese de memorie către variabile de diverse tipuri.
- Orice tip de date obișnuit
T
are asociat un tip de date pointerT*
ce stochează adrese către variabile de tipulT
. - Un pointer poate să nu indice către nicio variabilă, caz în care are valoarea
NULL
.
- Operatorul
&
se numește operatorul de referențiere și returnează un pointer către o variabilă dată (nu neapărat obișnuită, poate fi și ea la rândul ei pointer). - Operatorul
*
se numește operatorul de dereferențiere și returnează variabila de la o adresă dată.
int var, *ptr;
var = 618;
ptr = &var;
cout << *ptr << '\n'; // 618
var = 314;
cout << *ptr << '\n'; // 314
*ptr = 111;
cout << var << '\n'; // 111
cout << (&*ptr == ptr) << '\n'; // 1
- Motivația inițială din spatele pointerilor:
- Abilitatea de a modifica variabile stocate în afara funcției curente.
- Lucrul cu variabile alocate dinamic.
- În continuare, pe noi ne va interesa legătura dintre pointeri și vectori, chiar dacă C++ ne permite să facem chestii mult mai complexe cu ajutorul pointerilor.
Legătura dintre Pointeri și Vectori
- În cod, numele unui vector este de fapt un pointer constant către primul element din vectorul respectiv.
int vec[] = {6, 1, 8, 3, 1, 4}, var = 10;
cout << *vec << '\n'; // 6
*vec = 2;
cout << vec[0] << '\n'; // 2
vec = &var; // eroare
- La un pointer
p
de tipulT*
, putem aduna (sau scădea) un scalar întregi
. - Rezultatul va fi un pointer către zona de memorie aflată cu
i
poziții de tipulT
(decii * sizeof(T)
bytes) în fața (sau spatele) luip
. - Având în vedere că vectorii sunt secvențe continue de memorie, deducem că
vec[i]
este de fapt doar un alias pentru*(vec + i)
.
int vec[] = {6, 1, 8, 3, 1, 4}, *ptr;
ptr = vec;
cout << *ptr << '\n'; // 6
cout << *(ptr + 2) << '\n'; // 8
*(ptr + 3) = 7;
cout << vec[3] << '\n'; // 7
cout << *(vec + 3) << '\n'; // 7
Șiruri de Caractere în C
- În C, un șir de caractere este o secvență de variabile de tip
char
, situate pe poziții consecutive în memorie (deci care, adesea, formează un întreg vector), terminată prin caracterul\0
(cu codul ASCII0
).
char str1[100] = "InfoGym";
char str2[] = "InfoGym"; // 7 + 1 == 8 elemente
cout << (str2[7] == '\0') << '\n'; // 1
Citire și Afișare
- Pentru șirurile ce nu conțin caractere albe, folosim operatorii
>>
și<<
.
cin >> str;
cout << str;
- Pentru a citi o linie întreagă, fără stocarea caracterului
\n
de la final, folosim funcțiagetline
.
cin.getline(str, maxLen + 1);
- Reamintim modalitățile de citire a unui caracter.
cin >> chr; // se sare peste caracterele albe
cin.get(chr); // se citește următorul caracter în chr
cin.get(); // se sare peste următorul caracter
Funcții Predefinite
- Biblioteca
<cstring>
ne pune la dispoziție diverse funcții pentru a efectua operații elementare pe șiruri de caractere.
1. Funcția strlen
int strlen(const char* str);
- Funcția
strlen
returnează lungimea șiruluistr
. - Complexitatea este , deoarece se parcurg caracterele lui
str
până la întâlnirea lui\0
.
char str[] = "C++";
cout << strlen(str) << '\n'; // 3
cout << strlen(str + 2) << '\n'; // 1
cout << strlen("string") << '\n'; // 6
Întrebare: De ce nu este OK să parcurgem un șir în modul următor?
for (int i = 0; i < strlen(str); i++)
2. Funcția strcpy
char* strcpy(char* dst, const char* src);
- Funcția
strcpy
copiază conținutul șiruluisrc
în șiruldst
și returnează un pointer către rezultat.
char a[10], b[10] = "InfoGym";
strcpy(a, b + 4);
cout << a << '\n'; // Gym
Întrebare: De ce nu putem folosi operatorul
=
pentru copierea unui șir în alt șir?
- Cazul în care cei doi parametri pointează către zone din același vector poate produce unexpected behavior. Așadar, nu putem șterge o secvență dintr-un șir ca în primul exemplu, ci ca în al doilea.
strcpy(str + 3, str + 10);
strcpy(tmp, str + 10);
strcpy(str + 3, tmp);
3. Funcția strcat
char* strcat(char* dst, const char* src);
- Funcția
strcat
concatenează șiruriledst
șisrc
, stocând rezultatul îndst
, și returnează un pointer către acesta.
char a[] = "Info", b[] = "Gym";
strcat(a, b);
cout << a << '\n'; // InfoGym
4. Funcția strcmp
int strcmp(const char* str1, const char* str2);
- Funcția
strcmp
compară șirurilestr1
șistr2
din punct de vedere lexicografic, returnând un număr negativ pentru mai mic, un număr pozitiv pentru mai mare și0
pentru egal.
cout << (strcmp("interval", "integral") > 0) << '\n'; // 1 ('r' > 'g')
cout << (strcmp("C", "C++") < 0) << '\n'; // 1 ('\0' < '+')
cout << !strcmp("string", "string") << '\n'; // 1
5. Funcția strchr
char* strchr(const char* src, char chr);
- Funcția
strchr
returnează un pointer către prima apariție a caracteruluichr
în șirulsrc
, sauNULL
dacă aceasta nu există.
char str[] = "calculator";
cout << (strchr(str, 'l') - str == 2) << '\n'; // 1
cout << !strchr(str, 'x') << '\n'; // 1
cout << strchr(str, 'o') << '\n'; // or
6. Funcția strstr
char* strstr(const char* src, const char* str);
- Funcția
strstr
returnează un pointer către prima apariție a șiruluistr
în șirulsrc
, sauNULL
dacă aceasta nu există.
char *ptr = strstr(a, b);
while (ptr) {
cout << ptr - a << '\n';
ptr = strstr(ptr + 1, b);
}
7. Funcția strtok
char* strtok(char* str, const char* sep);
- Funcția
strtok
este folosită pentru împărți șirulstr
în tokeni, adică în subșiruri separate prin caractere din șirul de separatorisep
. - Mai precis,
strtok
pune\0
pe fiecare poziție dinstr
ce conține un separator și returnează, pe rând, câte un pointer la fiecare token obținut, sauNULL
dacă aceștia s-au terminat.
char str[] = "Cee, asta a fost totul despre string-uri?";
char *ptr = strtok(str, " ,.?!");
while (ptr) {
cout << ptr << '\n';
ptr = strtok(NULL, " ,.?!");
}
// Cee
// asta
// a
// fost
// totul
// despre
// string-uri
Șiruri de Caractere în C++
- În C++, pe lângă șirurile de caractere din C, le putem folosi și pe cele din STL: obiectele de tipul
std::string
din biblioteca<string>
.
string a = "Info";
cout << a.length() << '\n'; // 4
cout << a[3] << '\n'; // f
a += "Gym 7-8";
cout << a << '\n'; // InfoGym 7-8
cout << a.substr(4, 3) << '\n'; // Gym
string b = "Inginer";
cout << (a < b) << '\n'; // 1
Întrebare: De ce codul de mai jos nu compilează?
string str = "abc" + "def";
- String-urile din STL sunt deosebit de utile atunci când trebuie, spre exemplu, să sortăm o listă de string-uri, căci putem scrie pur și simplu
sort(vec, vec + len)
, undevec
este un vector de tipulstring[]
.
Întrebare: Cum putem sorta o listă de string-uri C-style folosind funcția
std::sort
?
-
Singurul mic neajuns al string-urilor din STL este lipsa unei funcții echivalente cu
strtok
. -
Aici ne ajută expresia
str.c_str()
, care returnează un pointer către reprezentarea C-style a luistr
(da, cu tot cu terminatorul nul).