Utilizarea sirurilor de caractere in limbajul de programare C++

In limbajul de programare C++, un sir de caractere se defineste in doua moduri:

  • vector de caractere – zona de memorie de lungime prestabilita care se rezerva la momentul compilarii aplicatiei;
  • pointer – contine adresa de memorie unde se stocheaza sirul de caractere; de regula, aceasta zona de memorie este alocata la momentul executiei aplicatiei.


Definire unui vector de caractere si a unui pointer de sir de caractere este:

//definire vector 
char sir[10]; 
//definire pointer 
char *pSir;

Variabila sir are o zona de memorie rezervata la momentul compilarii pe o lungime de 10 simboluri ASCII. Variabila pSir are o zona de memorie rezervata la momentul compilarii de 4 bytes aferenti stocarii unei adrese de memorie de tip far. De asemenea, variabila sir accesata fara operatorul de indexare refera adresa de memorie unde incepe zona de 10 bytes rezervata la compilare.
Gestiunea unui sir de caractere se realizeaza avand in vedere structura de tip vector in care aceste se stocheaza. Vectorul reprezinta o zona de memorie contigua (elemente adiacente din punct de vedere logic si fizic), elementele sale fiind accesate prin operatii de indexare.
Lucrul cu siruri de caractere trebuie sa ia in considerare caracteristicile structurilor de tip vector in care acestea sunt stocate pe durata executiei programului. Fiecare element din vector reprezinta un caracter din sir. Din punct de vedere fizic, un caracter este stocat pe 1 byte avand valoarea aferenta codului asociat caracterului in tabela ASCII. Deci, sirul de caractere reprezinta sirul de bytes in care fiecare byte stocheaza valoarea codului ASCII asociat unui simbol. Byte-ul cu valoarea 0 reprezinta terminatorul de sir de caractere. Functiile de lucru pe siruri de caractere trebuie sa ia in considerarea acest byte pentru implementarea operatiilor pe siruri in mod corect si consistent.
Biblioteca string.h contine functii standard (predefinite in cadul limbajului) de gestiune, manipulare si prelucrare a sirurilor de caractere. De asemenea, programatorul isi poate construi propriile module de lucru pe siruri de caractere.
Initializarea sirului de caractere se realizeaza:

  • la momentul definirii variabilei;
  • dupa definirea variabilei prin preluarea sirului de caractere din alte zone de memorie, din fisiere sau din alte dispozitive de intrare.

Initializarea la definirea vectorului de caractere se realizeaza conform secventei:

char sir[] = "Sir de caractere.";

Variabila vector sir are rezervata o zona de memorie de 18 bytes, inclusiv byte-ul 0 cu rol de terminator de sir. Pe lungimea zonei rezervate, este posibila modificarea continutului sirului de caractere. Exemplu:

sir[0] = 'F';

Initializarea cu un sir de caractere constant a unei variabile pointer la momentul definirii se realizeaza:

char *pSir = "Sir de caractere.";

Variabila pointer pSir contine adresa de memorie pentru sirul constant adus din fisierul executabil si mapat pe zonele de memorie alocate procesului aferent aplicatiei C++.
Secventa de mai sus nu presupune alocare de memorie heap de catre programator la momentul executiei aplicatiei. Orice incercare de modificare a sirului de caractere prin variabila pSir determina aparitia unei erori de executie. Exemplu de modificare a sirului:

pSir[0] = 'F';

Aceasta situatie reprezinta o implementare improprie in cod sursa. Solutia consta in rezervarea explicita (de catre programator) a unei zone de memorie in vederea stocarii sirului de caractere.
O alta posibilitate de initializare a pointerului la definire este:

char sir[] = "Sir de caractere.";
char *pSir = sir;

In acest caz, pointerul pSir contine adresa de inceput a vectorului sir. Orice modificare efectuata pe zona de memorie rezervata pentru sirul de caractere se efectueaza sau gestioneaza atat prin vectorul sir, cat si prin pointerul pSir. Exemplele de mai jos sunt corecte si functionale:

sir[0] = 'F';
pSir[1] = 'O';

Zona de memorie rezervata la compilare este partajata de doua variabile, fiind accesata si prelucrata din doua puncte in cadrul programului.
O alta problema care vizeaza utilizarea sirurilor de caractere este buffer overflow. Aceasta consta in utilizarea zonelor de memorie aferente sirurilor de caractere fara a lua in considerare lungimea maxima a acestora. Se considera urmatoarea secventa de cod sursa C++:

int main (int argc, char const *argv[]){
   char buffer[4] = "ABC";
   strcpy(buffer, argv[1]);
   printf("bufffer: %s\n", buffer);

   return 0;
}

in care:

  • argc – numarul de argumente ale functiei main; utilizat de catre functia main in cazul aplicatiilor C++ rulata la consola DOS; are o valoare cu 1 mai mare decat numarul de argumente deoarece primul argument al functiei main este denumirea fisierului executabil;
  • argv – vector de siruri de caractere care stocheaza argumentele functiei main;
  • buffer – sir de caractere de maxim 4 simboluri, inclusiv byte-ul cu rol de terminator;
  • strcpy – functie standard care copiaza continutul de zona de memorie din argv[1] in vectorul buffer;
  • printf – functie standard definita in stdio.h care afiseaza in fereastra consola un sir de caractere.

Dupa momentul initializarii variabilei buffer, zona de memorie initializata are urmatorul continut binar:

Continut zona de memorie pentru vectorul buffer la initializare
Continut ASCII al zonei de memorie buffer la initializare

Dupa apelul functiei strcpy, zona de memorie aferenta vectorului buffer are urmatorul continut binar:

Continut zona de memorie buffer dupa copierea argumentului
Continut ASCII al zonei de memorie buffer dupa copierea argumentului

Avand in vedere ca aplicatia este rulata la consola conform appMain.exe XYZWWWXYZ, se observa ca sirul XYZWWWXYZ transmis ca argument al functiei main are o lungime mai mare decat cea a vectorului buffer, respectiv 4 bytes. Se constanta ca sirul XYZWWWXYZ va suprascrie bytes-ii incepand cu adresa stocata in variabila buffer si depasind lungimea de 4 bytes ai acesteia. Prin suprascriere, exista riscul de modificare a continutului altor variabile rezervate pe zone de memorie adiacente vectorului buffer.
Solutia la aceasta problema consta in alocarea memoriei la executia aplicatiei pentru asigurarea permanenta a spatiului de memorie necesar stocarii unui sir de caractere.