Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml


Pointeri si tablouri

c



+ Font mai mare | - Font mai mic



Pointeri si tablouri

In C, exista o relatie strinsa intre pointeri si tablouri,



atit de strinsa incit pointerii si tablourile pot fi tratate

simultan. Orice operatie care poate fi rezolvata prin indicierea

tablourilor poate fi rezolvata si cu ajutorul pointerilor. Versiu-

nea cu pointeri va fi in general, mai rapida dar, pentru incepa-

tori, mai greu de inteles imediat.

Declaratia

int a[10]

defineste un tablou de dimensiunea 10, care este un bloc

de 10 obiecte consecutive numite a[0], a[1], , a[9] notatia

a[i] desemneaza elementul deci pozitiile, ale tabloului, numarate

de la inceputul acestuia. Daca pa este un pointer pe un interg,

decalarat ca

int *pa

atunci asignarea

pa = &a[0]

face ca pa sa pointeze pe al 'zero-ulea' element al tabloului a;

aceasta inseamna ca pa contine adresa lui a[0]. Acum asignarea

x = *pa

va copia continutul lui a[0] in x.

Daca pa pointeaza pe un element oarecare al lui a atunci prin

definitie pa+1 pointeaza pe elemmentul urmator si in general

pa-i pointeaza cu i elemente inaintea elementului pointat de

pa iar pa+i pointeaza cu i elemente dupa elementul pointat de

pa. Astfel, daca pa pointeaza pe a[0]

*(pa + 1)

refera continutul lui a[1], pa + i este adresa lui a[i] si

*(pa+i) este continutul lui a[i].

Aceste remarci sint adevarate indiferent de tipul varaiabi-

lelor din tabelul a. Definitia 'adunarii unitatii la un pointer '

si prin extensie, toata aritmetica pointerilor este de fapt

calcularea prin lungimea in memorie a obiectului pointat. Astfel,

in pa+i i este inmultit cu lungimea obiectelor pe care pointeaza

pa inainte de a fi adunate la pa.

Corespondenta intre indexare si aritmetica pointerilor este

evident foarte strinsa. De fapt, referinta la un tablou este

convertita de catre compilator intr-un pointer pe inceputul tablo-

ului. Efectul este ca numele unui tablou este o expresie pointer.

Aceasta are citeva implicatii utile. Din moment ce numele unui

tablou este sinonim cu locatia elementului sau zero, asignarea

pa = &a[0]

poate fi scrisa si

pa = a

Inca si mai surprinzator la prima vedere este faptul ca o

referinta la a[i] poate fi scrisa si ca *(a+i). Evaluind pe

a[i], C il converteste in *(a+i); cele doua forme sint echivalen-

te. Aplicind operatorul & ambilor termeni ai acestei echivalente,

rezulta ca &a[i] este identic cu a+i: a+i adresa elementului al

i-lea in tabloul a. Reciproc: daca pa este un pointer el poate fi

utilizat in expresii cu un indice pa[i] este identic cu *(pa+i).

Pe scurt orice tablou si exprimare de indice pot fi scrise ca

un pointer si offset si orice adresa chiar in aceeasi instructiune

Trebuie tinut seama de o difernta ce exista intre numele

tablou si un pointer. Un pointer este o variabila, astfel ca pa=a

si pa++ sint operatii. Dar, un nume de tablou este o constanta,

nu o variabila: constructii ca a=pa sau a++ sau p=&a sint interzi-

se. Atunci cind se transmite un nume de tablou unei functii,

ceea ce se transmite este locatia de inceput a tabloului. In

cadrul functiei apelate acest fapt argument este o variabila

ca oricare alta astfel incit un argument nume de tablou este un

veritabil pointer, adica o variabila continind o adresa. Ne vom

putea folosi de aceasta pentru a scrie o noua versiune a lui

strlen, care calculeaza lungimea unui sir.

strlen(s) /* returneaza lungimea sirului s */

char *s

Incrementarea lui s este perfect legala deoarece el este o

variabila pointer; s++ nu are efect pe sirul de caractere in

funca care a apelat-o pe strlen, dar incrementeaza doar copia

adresei. Ca parametri formali in definirea unei functii

char s[]

si

char *s;

sint echivalenti; alegerea celui care trebuie scris este deter-

minata in mare parte de expresiile ce vor fi scrise in cadrul

functiei. Atunci cind un nume de tablou este transmis unei

functii, aceasta poate, dupa necesitati s-o interpreteze ca

tablou sau ca pointer si sa-l manipuleze in consecinta. Functia

poate efectua chiar ambele tipuri de operatii daca i se

pare potrivit si corect.

Este posibila si transmiterea catre o functie doar a unei

parti dintr-un tablou prin transmiterea unui pointer pe inceputul

subtabloului. De exemplu, daca a este un tablou;

f(&a[2])

si

f(a + 2)

ambele transmit functiei f adresa elementului a[2] deoarece &a[2]

si a+2 sint expresii pointer care refera al treilea element al lui

a. In cadrul lui f, declarea argumentului poate citi

f(arr)

int arr[];

sau

f(arr)

int *arr;

Astfel, dupa cum a fost conceputa functia f faptul ca argumentul

refera de fapt o parte a unui tablou mai mare nu are consecinte.

5.4. Aritmetica adreselor

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 consecu-

tive 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. Biblio-

teca 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 aplica-

tii care au realmente nevoie de canalul alloc pentru a dispensa

mici portiuni de memorie, de lungimi neprevazute la momente nepre-

vazute.

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 opera-

re 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;

else /* nu-i destul loc */

return(NULL)

}

free(p) /* zona de memorie libera pointata de p */

char *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 initia-

lizeaza 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 (obser-

vati 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 exem-

plu, 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;

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 carac-

tere 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.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1154
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved