Sunteți pe pagina 1din 8

In exercitiile urmatoare vom incerca sa explicam cateva dintre problemele puse de lucrul cu

siruri de caractere (stringuri).


Asa cum am spus la curs, limbajul C nu are un tip de date dedicat stringurilor. De aceea,
maniera de lucru cu stringuri este facuta in doua moduri :
fie cu un vector (array) de caractere:
char string[];
fie cu pointer la char:
char *string;
In continuare vom insista pe al doilea caz (pointer la char) pentru ca, in general, este mai
utilizat si pentru ca naste cele mai multe probleme.
OBS : Pentru toate exercitiile si eplicatiile de mai jos, vom considera tot timpul urmatoarea
schema : atunci cand se declara un pointer P la un anumit tip T (de exemplu pointer la char
char *string;), anunt compilatorul ca variabila mea pointer P va arata (pointa, va face
referitre) la o zona de memorie in care pot pune informatie de tipul T :

1. Se da aplicatia de mai jos :


#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char *nume;
puts("Inainte de initializare");
strcpy(nume, "ABC"); //aici incerc sa pun o valoarea string intr-un pointer
puts("Dupa initializare, sirul este %s", nume);
return 0;
}

Intrebarea este : ce va afisa aplicatia, la rulare ?


Sa vedem pas cu pas ce se intampla :

char *nume; = atunci cand se declara variabila nume ca pointer la char, de fapt se
anunta compilatorul ca aceasta variabaila va urma sa faca referire (sa referentieze) o
zona din memorie care retine date de tip char;
strcpy(nume, "ABC"); = se incearca sa se pune stringul ABC in zona la care variabila
pointer nume face referire. Numai ca la acest pas, pointerul nume nu face referire la
nicio zona de memorie, deci stringul ABC nu am inca un sa-l pun;

Acesta este un exemplul clasic de eroare de logica (ce va duce si la eroare de rulare) de a
incerca sa pun ceva intr-un pointer neinitializat. Eroarea in acest caz este :

(aceasta eroare este afisata la rularea aplicatiei pe un Windows 7, dar va fi ceva similar pe
orice alta versiune de Windows).
Solutia este de a aloca manual spatiu de memorie catre care, in final, pointerul nume va
referentia. Adica asa cum este aratat in exemplulurmator

2. Aceeasi aplicatie de la punctul 1 cu modificarea ca, de data aceasta, aloc spatiu


pentru pointerul la char
(atentie la fisierul header stdlib.h adaugat, pentru ca am nevoie de el pentru a apela
functia malloc())
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)


{
char *nume;
nume = (char*)malloc(20*sizeof(char));
puts("Inainte de initializare");
strcpy(nume, "ABC");
printf("Dupa initializare sirul este : %s\n\n", nume);
return 0;
}

Diferenta fata de exemplul anterior consta doar in adaugarea liniei de cod:


nume = (char*)malloc(20*sizeof(char));

adica aloc o zona de memorie de 20 de locatii, in fiecare locatie putand sa pun un char.
Sprea aceasta zona de memorie nou alocata va indica variabila mea pointer nume
declarata anterior.
Mai departe, cand incerc sa pun stringul ABC in nume, de fapt acest string este pus in
memoria alocata anterior, catre care nume va referentia. Cum memoria a fost alocata cu
succes, de data aceasta aplicatia va rula cu succes:

In zona de memorie nou alocata cu malloc() nu stiu exact ce voi gasi, inainte sa incep eu sa
pun date in ea. De obicei, imediat dupa alocarea unei astfel de zone in care urmeaza sa pun
ceva, fac o initializare a zonei cu date implicite, in general in functie de tipul de date ce
urmeaza sa pun acolo, si anume :
- Daca urmeaza sa pun valori numerice, atunci initalizez zona cu valoarea 0 (un fel de
element neutru pentru date numerice);
- Daca urmeaza sa pun caractere, initializez zone cu caracterul \0 (spuneam ca acest
caracter este interpretat ca simbol pentru finalul de sir de caractere), sau cu
caracterul spatiu .
Bineinteles, acestea nu sunt decat indicatii, programatorul poate sa initilizeze zona noua
alocata cu ceea ce doreste (functie de necesitati etc).
Un astfel de exemplu in exercitiul urmator :

3. Aceeasi aplicatie de la punctul 2 cu modificarea ca, de data aceasta, initializez


spatiul de memorie nou alocat, inainte de utilizare:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)


{
char *nume;
nume = (char*)malloc(20*sizeof(char));
memset(nume, '\0', 20*sizeof(char));
puts("Inainte de initializare");
strcpy(nume, "ABC");
printf("Dupa initializare sirul este : %s\n\n", nume);
return 0;
}

Diferenta consta in linia (adica apelul functiei memset()):


memset(nume, '\0', 20*sizeof(char));
Functia memset() are urmatorul prototip :

void * memset ( void * ptr, int value, size_t num );


unde :
ptr = poinetr catre zona de memorie pe care urmeaza sa o initializez;
value = caracterul (ca si cod ASCII) cu care voi initializa zone de memoria;
num = dimensiune in octeti ce reprezinta numarul de octeti din zona alocata pe care
urmeaza sa ii initializez cu value. Acest parametru este util cand spre exempu doresc
sa initilizez cu o valoarea doar o parte din memoria nou alocata, spre care ptr va face
referinta.
La executie, rezulatul este exact acelasi cu cel din exemplul 2, deci aparent nu s-a modificat
nimic. Numai ca nu este garantat ca pe alte masini sau pe alte sisteme de operare se
intampla la fel. Pentru siguranta se recomanda ca memoria nou alocata sa fie initializata
inainte de a fi folosita.

4. Urmatorul exercitiu este pentru a arata eliberarea memoriei.


Notiune de eliberare de memorie est practic procesul invers celui de alocare de
memorie. Asa cum uneori am nevoie sa aloc memorie in mod manual, la cerere, pentru a o
utiliza, la fel de bine este nevoie ca memoria alocata sa fie eliberata. Daca vorbim de
pointeri, am vazut cum se face alocarea de memorie apeland functia malloc(). Eliberarea
de memorie alocata se face apeland o functie similara, complementara, - free(). Functia face
ca zona de memorie spre care referentia initial pointerul sa fie eliberata, deci pointerul nu
mai referentiaza catre nimic. Aceasta zone eliberata poate sa fie refolosita, la o alta alocare
(dar acest lucru este facut in mod transparent programatorului, de catre compilator).
Cele doua operatii de alocare si de eliberare de memorie sunt esentiale daca vrem
ca intr-o solutie propusa sa aplicam un mecanism de utilizare eficienta a memoriei.
In exemplul urmator, dupa ce in zona referentiata de pointerul nume am pus un string
pe care l-am afisat, voi elibera acea zona si incerc apoi sa pun in pointer o noua valoare:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)


{
char *nume;
nume = (char*)malloc(20*sizeof(char));
memset(nume,'\0',20*sizeof(char));
puts("Inainte de initializare");
strcpy(nume, "ABC");

printf("Dupa initializare sirul este : %s\n\n", nume);


free(nume);
//eliberez memoria referentiata de "nume"
strcpy(nume, "XYZ"); //incerc sa pun o noua valoare in pointerul "nume"
return 0;
}

La rularea aplicatie de mai sus vom obtine, cum era oarecum de asteptat, o eroare
similara celui prezentate la punctul 1 de mai sus. De ce ? Pentru ca cele doua linii de cod :
free(nume);
strcpy(nume, "XYZ");
reproduc practic cazul punctului 1 si anume incercarea de a pune ceva intr-un pointer
neinitializat.
Asa cum spuneam, daca dorim crearea unei aplicatii care sa utilizeze in mod eficient
memoria, avand aceste doua mecaniste alocare, dezalocarea ne putem imagina
exercitiul de mai jos :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)


{
char *nume;
nume = (char*)malloc(10*sizeof(char)); //prima alocare de memorie
memset(nume, '\0', 20*sizeof(char));
puts("Inainte de initializare");
strcpy(nume, "ABCDEFGH");
printf("Dupa initializare sirul este : %s\n\n", nume);
free(nume);

//dezaloc memoria

nume = (char*)malloc(5*sizeof(char));
strcpy(nume, "XYZ");
memset(nume, '\0', 5*sizeof(char));

//a doua alocare de memorie

printf("Dupa a 2-a initializare sirul este : %s\n\n", nume);


return 0;
}

La rulare se afiseaza :

Exercitiul este unul banal, dar ne putem face o imagine despre ceea ce am spus mai sus, si
anume :
la prima alocare de memorie stiu ca voi pune in pointer un sir de caractere mai lung , de
aceea aloc 10 locatii in care sa pun caractere :
nume = (char*)malloc(10*sizeof(char));
astfel alocata memoria, in ea voi pune sirul "ABCDEFGH"
apoi eliberez memoria referentiata de pointerul nume (free(nume)) si aloc o alta, de data
asta insa de numai 5 caractere, pentru ca stiu ca voi pune un sir mai scurt :
nume = (char*)malloc(5*sizeof(char));
in noua zona voi pune sirul "XYZ"

5. Urmatorul exercitiu exemplifica utilizarea celor mai des intalnite functii de prelucrare
a sirurilor de caractere :
char * strcpy( char * destinatie, const char * sursa );
char * strcat( char * destinatie, const char * sursa );
char * strncpy( char * destinatie, const char * sursa, size_t num );
char * strncat( char * destinatie, const char * sursa, size_t num );
size_t strlen( const char * string );
De asemena, am folosit si functii, tot in spirit didactic.
Pentru ca am avut nevoie sa aloc memorie pentru doua variabile de tip pointer la char si
apoi sa initializez memorie alocata, este mai usor ca creez o functie generala
AlocAndInitSir() care aloca memorie de dimensiune data si o initializeaza, returnand un
pointer la memoria alocata. Atfel, daca voi avea nevoie in aplicatie sa creez si alti pointer la
char, pot utiliza (apela) direct functia creata.
De asemena, pentru ca exercitul este unul didactic, de fiecare data cand modific continutul
unui string ii afisez acest continut precum si (nous) dimensiune a lui. Pentru ca este nevoie
sa afcem asta destul de des, atunci e preferabil crearea unei functii generale care sa aiba in
intrare un pointer la char (string) pentru care afisez continutul si lungimea. Aceast afunctie
se numeste AfisareInfoString().

Codul este :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void AfisareInfoString(char *s);
char *AlocAndInitSir(int dimensiune);
int main(int argc, char **argv)
{
char *sir;
char *sir2;
int c;
int dimens = 100;
sir = AlocAndInitSir(dimens);
sir2 = AlocAndInitSir(dimens);
puts("*** Exemplu de lucru cu stringuri ***");
strcpy(sir, "123ABC"); AfisareInfoString(sir);
strcat(sir, "XYZ"); AfisareInfoString(sir);
strncpy(sir, "098765", 4); AfisareInfoString(sir);
strncat(sir, "ABCDEFGHIJ", 5); AfisareInfoString(sir);
strcpy(sir2, "WERTY"); AfisareInfoString(sir2);
//strcmp() este o functie care compara doua stringuri si care intoarce (sau
returneaza) o valoare intreaga:
//
< 0 => primul string este "mai mic" decat al doilea
// = 0 => cele doua stringuri au acelasi continut
// > 0 => primul string este "mai mare" decat al doilea
//Notiunea de "string mai mare sau mai mic decat un altul" se refera la
comparatia caracter cu caracter,
//incepand din partea stanga a ambelor stringuri, dupa codul ASCII al
caracterelor.
//De exemplu : sir ="abc" si sir2 = "mnport". Cand compar cele doua
stringuri se testeaza :
//
cum este caracterul 'a' fata de caracterul 'm' ?
//
R : este mai mic, deoarece codul ASCII pt caracterul 'a' este mai mic
decat codul ASCII al caarcterului 'm'
// => sir1 < sir2
c = strcmp(sir, sir2);
printf("\nSir 1 = '%s' , sir 2 = '%s'. Rezultatul comparatiei este %d\n ",
sir, sir2, c);
return 0;
}
/*Functie care aloca si initializeaza cu 0 un spatiu de memorie de o dimensiune
data, spatiu in care voi pune caractere.
Functia intoarce un pointer catre acel spatiu alocat.*/
char *AlocAndInitSir(int dimensiune)
{

char *s;
s = (char*)malloc(dimensiune*sizeof(char));
memset(s, '\0', dimensiune);
return s;
}
//Functie care afiseaza continutul unui string si dimensiunea (lungimea) lui.
//Lungimea unui string = numarul de caractere din acel string
void AfisareInfoString(char *s)
{
printf("\nSirul este '%s' si are lungimea = %d\n", s, strlen(s));
}

S-ar putea să vă placă și