Capitolul 5.4. Aritmetica adreselor

Vezi subiectul anterior Vezi subiectul urmator In jos

Capitolul 5.4. Aritmetica adreselor

Mesaj  zooky la data de Mier Mar 18, 2009 1:46 pm

Daca p este un pointer, atunci p++ incrementeaza pe p in asa fel incit t acesta sa pointeze pe elementul urmator indiferent de tipul obiectelor pointate, iar p+=i incrementeaza pe p pentru a pointa peste i elemente din locul unde p pointeaza curent.
C este consistent si constant cu aritmetica pointerilor; pointerii, tablourile si aritmetica adresarii constitue punctul forte al limbajului. Sa ilustram citeva dintre proprietatile lui scriind un program pentru alocare de memorie rudimentar (dar este util in ciuda simplitatii sale exista doua rutine:alloc(n) returneaza un pointer p pe n pozitii caracter consecutive care poate fi utilizat de catre apelantul lui alloc pentru alocarea de caractere; free(p) elibereaza memoria facind-o astfel refolosibila mai tirziu. Rutinele sint "rudimentare" deoarece apelurile la free trebuie facute in ordine inversa apelurilor la alloc. Aceasta inseamna ca memoria gestionata de alloc si free este o stiva sau o lista prelucrabila in regim LIFP. Biblioteca standard C este prevazuta in functii analoage care nu au atit de multe restrictii iar in capitolul 8 vom da, pentru demonstratie si alte versiuni. Intre timp se vor ivi multe aplicatii care au realmente nevoie de canalul alloc pentru a dispensa mici portiuni de memorie, de lungimi neprevazute la momente neprevazute.
Cea mai simpla implementare este de a scrie alloc pentru declararea de parti ale unui tablou mare pe care il vom numi allocbuf. Acest tablou este propriu lui alloc si free. Lucrind cu pointeri, nu cu indici in tablou nu este necesar ca vreo alta rutina sa cunoasca numele tabloului, care poate fi declarat static, adica local fisierului sursa care sustine pe alloc si free numele tabloului fiind invizibil in afara acestui fisier.
In implementarile practice tabloul poate chiar sa nu aiba nici un nume el putind fi obtinut prin cererea catre sistemul de operare a unui pointer pe un bloc de memorie fara nume.
O alta informatie necesara este legata de cit anume din allocbuf a fost folosit. Vom utiliza un pointer pe urmatorul element liber, numit allocp. Cind este apelat alloc pentru n caractere, el verifica daca exista suficient loc eliberat in allocbuf. Daca astfel alloc returneaza valoarea curenta a lui allocp (adica inceputul blocului liber) atunci aceasta valoare esteincrementata cu n in asa fel incit allocp sa pointeze pe inceputul urmatoarei zone libere. Free(p) pune pur si simplu pe allocp pe p daca p este in interiorul lui allocbuf.

#define NULL 0 /* val pointerului in caz de eroare */
#define ALLOCSIZE 1000 /* lung spatiului disponibil */
static char allocbuf[ALLOCSIZE]; /* memorie pentru alloc*/
static char *allocp = allocbuf; /*memorarea parti libere*/
char *alloc(n) /* pointer de return pe n caractere */
int n;
{
if (allocp + n <= allocbuf + ALLOCSIZE) {
allocp += n;
return(allocp - n); /* vechiul p */
} else /* nu-i destul loc */
return(NULL)
}
free(p) /* zona de memorie libera pointata de p */
char *p;
{
if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
allocp = p;
}

Citeva explicatii. In general un pointer poate fi initializat ca orice alta variabila, desi in mod normal singurele valori semnificative sint NULL sau o expresie care opereaza adrese ale unor date in prealabil definite, de tip specificat. Declaratia

static char *allocp = allocbuf;

defineste pe allocp ca fiind un pointer pe caractere si il initializeaza pentru a-l pointa pe allocbuf care este urmatoarea pozitie libera atunci cind incepe programul. Aceasta stare ar putea fi scrisa si astfel

static char *allocp = &allocbuf[0];

deoararece numele tabloului este adresa elementului zero.

Testul

if (allocp + n <= allocbuf + ALLOCSIZE)

verifica daca este suficient loc pentru a satisface cererea pt n caractere. Daca ezista loc, noua valoare a lui allocp va fi cel mult mai dincolo de sfirsitul lui allocbuf. Daca cererea poate fi satisfacuta, alloc retur neaza un pointer normal (observati declaratia functiei). Daca nu, alloc trebuie sa returneze un semnal care sa semnifice ca nu exista spatiu liber. Limbajul C garanteaza ca nici un pointer care pointeaza o data valida nu va contine zero, asa ca valoarea zero returnata poate fi utilizata ca semnal de eveniment anormal, nu exista spatiu liber. Se scrie NULL in loc de zero pt a indica mai clar ca aceasta este o valoare speciala pt un pointer. In general intregii nu pot fi asignati pointerilor; zero este u caz special.

Teste ca

if (allocp + n <= allocbuf + ALLOCSIZE)

si

if (p >= allocbuf && p < allocbuf + ALLOCSIZE)

releva citeva fatete importante ale aritmeticii pointerilor.
Mai intii ca in unele situatii pointerii pot fi separati. Daca p si q pointeaza pe elemente ale aceluiasi tablou, relatii ca <, >, =, etc lucreaza exact.

p < q

este adevarata, de ex, in cazul in care p pointeaza pe un element anterior elementului pe care pointeaza q. Relatiile c= si != sint si ele permise. Orice pointer poate fi testat cu NULL. Dar nu exista nici o sansa in a compara pointeri in tablouri diferite. In cazul fericit se va obtine un evident nonsens, indiferent de masina pe care se lucreaza. Mai poate sa apara situatia nefericita in care codul va merge pe vreo masina esuind "misterios" pe altele.
In al doilea rind, tocmai s-a observat ca un pointer si un interg pot fi adunati sau scazuti. Instructiunea

p + n

desemneaza al n-lea obiect dupa cel pointat curent de p. Acest lucru este adevarat indiferent de tipul obiectelor pe care p a fost declarat ca pointer. Compilatorul atunci cind il intilneste pe n, il delaleaza in functie de lungimea obiectelor pe care pointeaza p, lungime determinata prin declaratia lui p. De exemplu, pe PDP11 factorii de scalare sint 11 pentru char, 2 pentru int si short, 4 pentru long si float si 8 pentru double.

Este valida si scaderea pointerilor: daca p si q pointeaza pe elementele aceluiasi tablou, p-q este numarul de elemente dintre p si q. Acest fapt poate fi utilizat pentru a scrie o noua versiune a lui strlen.

strlen(s) /* returneaza lungimea sirului */
char *s;
{
char *p = s;
while (*p != '\0')
p++;
return(p-s);
}

Prin declarare, p este initializat pe s, adica sa pointeze pe primul caracter din s. In cadrul buclei while este examinat pe care caracter pina se intilneste /0 care semnifica sfirsitul iar daca while testeaza numai daca expresia este zero este posibila omiterea testului expilcit iar astfel de bucle sint scrise adesea

while (*p)
p++;

Deoarece p pointeaza pe caractere, p++ face ca p sa avanseze de fiecare data pe caracterul urmator, iar p-v da numarul de caractere parcurse, adica lungimea sirului. Aritmetica pointerilor este consistenta: daca am fi lucrat cu float care ocupa mai multa memorie decit char, si daca p ar fi un pointer pe float, p++ ar avansa pe urmatorul float. Astfel, vom putea scrie o alta versiune a lui alloc care pastreaza sa zicem, float in loc de char, pur si simplu prin schimbarea lui char in float. in cadrul lui alloc si free. Toate manipularile de pointeri iau automat in considerare lungimea obiectului pointat in asa fel incit trebuie sa nu fie alterat.

Alte operatii in afara celor mentionate deja (adunarea sau scaderea unui pointer cu un intreg, scaderea sau comapararea a doi pointeri). Toate celelalte operatii arrrtmetice cu pointeri sint ilegale. Nu este permisa adunarea, impartirea, deplasarea logica, sau adunarea unui float sau double la pointer.
avatar
zooky
Moderator
Moderator

Numarul mesajelor : 147
Data de inscriere : 15/03/2009
Varsta : 24
Localizare : Cernatesti City

Vezi profilul utilizatorului http://e-learning.forumhit.ro

Sus In jos

Vezi subiectul anterior Vezi subiectul urmator Sus


 
Permisiunile acestui forum:
Nu puteti raspunde la subiectele acestui forum