Pointeri pe caractere si functii
Un sir constant scris astfel
"I am a string "
este un tablou de caractere. In reprezentare interna, compilato-
rul termina un tablou cu caracterul \0 in asa fel incit programele
sa poata detecta sfirsitul. Lungimea in memorie este astfel
mai mare cu 1 decit numarul de caractere cuprinse intre ghilimele.
Poate cea mai comuna aparitie a unui sir de constante
este ca argument al functiei cum ar fi in
char *message;
atunci instructiunea
message = "now is the time";
asigneazaa lui mesage un pointer in functie de caracterele
reale. Aceasta nu este o copie a sirului; nu sint implicati 525f57f decit
pointerii. C nu este inzestrat cu alti operatori care sa trateze
eze un sir de caractere ca o unitate.
Vom ilustra mai multe aspecte in legatura cu pointerii si cu
tablourile stiind ca doua functii cu adevarat utile, din bibliote-
ca standard de I/O, subiect care va fi discutat in capitolul 7.
Prima functie este strcpy(s, t) care copiaza sirul t in
sirul s. Argumentele sint scrise in aceasta ordine prin analogie
cu aranjarea, unde cineva ar putea spune
s = t
pentru a asigna pe t lui s. Versiunea ca tablouri este mai intii.
strcpy(s, t) /*copiaza t in s */
char s[], t[];
Iata o versiune a lui strcpy cu pointeri
strcpy(s, t) /* copiaza t in s, versiunea pointeri 1*/
char *s, *t;
}
Deoarece argumentele sint transmise prin valoare, strcpy
poate utiliza s si t in orice fel se doreste. Aici ei sint
conventional utilizati ca pointeri, care parcurg tablourile pina
in momentul in care s-a copiat \0 sfirsitul lui t, in s.
In practica, strrcpy nu va fi scris asa cum s-a aratat
mai sus. O a doua posilitate ar fi
strcpy(s, t) /* copiaza t in s, versiunea 2*/
char *s, *t;
In aceasta ultima versiune se imita incrementarea lui s si t in
partea de test. Valoarea lui *t++ este caracterul pe care a
pointat inainte ca t sa fi fost incrementat; prefixul ++ nu-l
schimba pe t inainte ca acest caracter sa fi fost adus. In acelasi
fel, caracterul este stocat in vede a pozitie s inainte ca s sa
fie incrementat. Acest caracter este deasemenea valoarea care
se grupeaza cu \0 pentru simboul buclei. Efectul net este ca,
caracterele sint copiate din t in s, inclusiv sfirsitul lui \0.
Ca o ultima abreviere vom observa ca si gruparea cu \0 este
redundanta, astfel ca functia este adesea scrisa ca
strcpy(s, t) /* copiaza t in s; versiunea pointeri 3 */
char *s, *t;
Desi aceasta versiune poate parea compilcata la prima vedere,
aranjamentul ca notatie este considerat suveran daca nu exista
alte ratiuni de a schimba astfel ca il veti intilni frecvent in
programele C.
A doua rutina este strcmp(s, t) care compara sirurile de
caractere s si t si returneaza negativ, zero sau pozitiv in
functie de relatia dintre s si t; care poate fi: s<t, s=t sau
s>t. Valoarea returnata este obtinuta prin scaderea caracterului
de pe prima pozitie unde s difera de t.
strcmp(s, t)
/* returneaza <0 daca s<t, 0 daca s==t, >0 daca s>t */
char s[], t[];
Versiunea pointeri a lui strcmp.
strcmp(s, t) /* returneaza <0 daca s<t, 0 daca s==t,
>0 daca s>t */
char *s, *t;
Daca ++ si -- sint folositi altfel decit operatori prefix sau
postfix pot apare alte combinatii de * si ++ si --, desi mai putin
frecvente. De exemplu:
*++p
incrementeaza pe p inainte de a aduce caracterul pe care pointeaza
p.
*--p
decrementeaza pe p in acelasi conditii.
Exercitiul 5.2. Scrieti o versiune pointeri pentru o functie
strcat expusa in capitolul 2: strcat(s, t) copiaza sirul t la
sfirsitul sirului s.
Exercitiul 5.3. Scrieti un macro pentru strcpy.
Exercitiul 5.4. Rescrieti variantele programelor din capito-
lele anterioare si exercitii cu pointeri in loc de tablouri
indexate. Bune posibilitati ofera: getline, atoi, itoa si vari-
antele lor, reverse, index, getop.
5.6. Pointerii nu sint de tip int
S-a putut observa ca programele C mai vechi au o atitudine mai
toleranta fata de copierea pointerilor. In general a fost adevarat
ca pe majoritatea masinilor un pointer poate fi asignat unui
intreg si invers, fara a-l schimba; nu are loc nici o scalare
sau conversie si nu se pierd biti. In mod regretabil aceasta
stare de lucruri a condus la asumarea unor libertati nepermise
desi partea programatorului in lucru cu rutina ce returneaza
pointeri ce sint transmisi apoi pur si simplu altor rutine -
necesitatea declararii pointerului fiind adesea omisa. De exemplu,
sa luam o functie strsave care copiaza sirul s undeva, intr-o
zona obtinuta printr-un apel la alloc, returnind apoi un pointer
pe ea. Strsave se poate scrie astfel
char *strsave(s) /* salveaza undeva sirul s */
char *s;
In practica, exista o tendinta puternica de a omite declararile:
strsave(s) /* salveaza undeva sirul s */
Acest cod s-ar putea sa mearga pe multe masini deoarece tipul
implicit al functiilor si al argumentelor este int iar atit int-ul
cit si pointerul pot fi asignati la inceput cit si la sfirsit. Cu
toate acestea, acest gen de cod este inerent riscant deoarece el
depinde de detalii de implementare si de arhitectura masinii, care
nu pot fi rezolvate pentru compilatorul particular utilizat de
dvs. Este recomandabil sa se efectueze toate declararile necesare.
(Programul lint va avertiza in legatura cu astfel de restrictii
in cazul in care se vor strecura inadvertente).