Document, comentariu, eseu, bacalaureat, liceu si facultate
Top documenteAdmitereTesteUtileContact
      
    


 


Ultimele referate adaugate

Adauga referat - poti sa ne ajuti cu un referat?

Politica de confidentialitate



Ultimele referate descarcare de pe site
  CREDITUL IPOTECAR PENTRU INVESTITII IMOBILIARE (economie)
  Comertul cu amanuntul (economie)
  IDENTIFICAREA CRIMINALISTICA (drept)
  Mecanismul motor, Biela, organe mobile proiect (diverse)
  O scrisoare pierduta (romana)
  O scrisoare pierduta (romana)
  Ion DRUTA (romana)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  Starea civila (geografie)
 

Ultimele referate cautate in site
   domnisoara hus
   legume
    istoria unui galban
   metanol
   recapitulare
   profitul
   caract
   comentariu liric
   radiolocatia
   praslea cel voinic si merele da aur
 
despre:
 
POINTERI SI TABLOURI IN C
Colt dreapta
Vizite: ? Nota: ? Ce reprezinta? Intrebari si raspunsuri
 

Un pointer este o variabila care contine adresa unei alte varia bile. Pointerii sint foarte mult utilizati in C parte pentru ca uneori sint singura cale de rezolvare a unei anumite probleme, parte pentru ca folosirea lor duce la alcatuirea unui cod mai compact si mai eficient decit altul obtinut in alt mod. t1k10ke

Pointerii au fost "ingramaditi" la citeva instructiuni goto ca un minunat mod de a crea programe "imposibil" de priceput.
Acest lucru devine pe deplin adevarat atunci cind pointerii sint folositi neatent, fiind usor de creat pointeri care sa pointeze in locuri cu totul neasteptate. Cu metoda, pointerii se utilizeaza pt un plus de simplitate. Aceasta este aspectul pe care vom incerca sa-l ilustram.

5.1. Pointeri si adrese

Din moment ce un pointer sustine adresa unui obiect, este posibila adresarea acelui obiect "indirect" prin intermediul pointerului.
Sa presupunem ca x este o variabila, sa spunem int si ca px este un pointer creat intr-un mod neprecizat. Operatorul & da adresa unui obiect, astfel incit instructiunea px=&x asigneaza variabilei px adresa lui x acum, px inseamna "pointeaza pe x". Operatorul & poate fi aplicat numai variabilelor si elementelor unui tablou, constructii ca &(x+1) si &3 sint inter zise. Este deasemenea interzisa pastrarea adresei unei variabile registru.

Operatorul unar * testeaza operandul sau ca adresa ultimului semnal si acceseaza aceasta adresa pentru a aduce continutul locatiei de la adresa respectiva. Astfel, daca y este tot un int y = *px asigneaza lui y, ori de cite ori este cazul continutul locatiei unde pointeaza px. Astfel secventa px = &x; y = *px; asigneaza lui y aceasi valoare ca si y = x

Totodata este necesara declararea variabilelor care apar in sec venta: int x, y; int *px;

Declararea lui x si y este deja cunoscuta. Noua este declararea pointerului px int *px este interpretata ca o mnemonica; aceasta inseamna ca *px este un int, adica in momentul in care px apare in context sub forma
*px, este echivalenta cu a intilni o variabila de tip int. De fapt, sintaxa declararii unei varaibile imita sintaxa expresiilor in care ar putea sa apara respectiva variabila. Acest rationament este util in toate cazurile care implica declaratii complicate. De exemplu: double atof(), *dp; spune ca intr-o expresie atof() si *dp au valoare de tip double.
De notat declaratia implicita, ceea ce vrea sa insemne ca un pointer este constrins sa pointeze o anumita categorie de obiecte. (Functie de tipul obiectului pointat).
Pointerii pot aparea in expresii. De exemplu, daca px poin teaza pe intregul x atunci *px poate aprarea in orice context in care ar putea apare x. y = *px + 1 da lui y o valoare egala cu x plus 1. printf("%d\n", *px) imprima o valoare curenta a lui x si d = sqrt((double) *px) face ca d = radical din x, care este fortat de tipul double inainte de a fi transmis lui sqrt (vezi capitolul 2).

In expresii ca y = *px + 1 operatorii unari * si & au prioritate mai mare decit cei aritmetici, astfel aceasta expresie ori de cite ori pointerul px avanseaza, aduna 1 si asigneaza valoarea lui y. Vom reveni pe seama asupra a ceea ce inseamna y = *(px + 1)

Referiri prin pointer pot apare si in partea stinga a asignarilor.
Daca px pointeaza pe x atunci

*px = 0 il pune pe x pe zero si

*px += 1 il incrementeaza pe x, ca si

(*px)++

In acest ultim exemplu parantezele sint necesare; fara ele, expre sia va incrementa pe px in loc sa incrementeze ceea ce pointeaza px deoarece operatorii unari * si + sint evaluati de la dreapta la stinga.
In sfirsit, daca pointerii sint variabile, ei pot fi manipu lati ca orice alta variabila. Daca py este un alt pointer pe int, atunci py = px copiaza continutul lui px in py facind astfel ca py sa varieze odata cu px.




5.2. Pointeri si argumente de functii

Datorita faptului ca in C este posibila transmiterea de argumente unei functii prin "apel prin valoare" nu exista modalitate directa pentru functia apelata de a altera o variabila in functia apelan ta. Ce este de facut atunci cind de fapt, se intentioneaza schim barea unui argument obisnuit ? De exemplu, o rutina de sortare trebuie sa inverseze doua elemente neordonate cu o functie swap.
Nu este suficient sa se scrie swap(a, b); unde functia swap este definita ca swap(x, y) /* GRESIT */ int x, y;
A int temp; temp = x; x = y; y = temp;
S

Din cauza apelului prin valoare, swap nu poate afecta argumen tele a si b in rutina care o apeleaza.
Din fericire, exista o modalitate de a obtine efectul dorit.
Programul apelant trasmite pointeri pe valorile care trebuie schimbate. swap(&a, &b);

Din moment ce operatorul & da adresa unei variabile, &a este un pointer pe a. In swap insasi, argumentele sint declarate ca fiind pointeri iar adevaratii operanzi sint accesati prin ei ( prin pointeri). swap(px, py) /* interscimba *px si *py */ int *px, *py;
A int temp; temp = *px;
*px = *py;
*py = temp;
S

O utilizare comuna a argumentelor de tip poiter se intilneste in cadrul functiilor care trebuie sa returneze mai mult decit o singura valoare. (Veti putea obiecta ca swap returneaza doua valori, noile valori ale argumentelor sale.) Ca un exemplu sa luam o functie getint care realizeaza inversia la intrare prin transsformarea unui sir de caractere in valori intregi, un intreg la fiecare apel, getint trebuie sa returneze valoarea gasita sau semnul de sfirsit de fisier atunci cind s-a terminat sirul de caractere de la intrare. Aceste valori trebuie sa fie returnate ca obiecte separate, pentru indiferent ce valoare este utilizata pentru EOF aceasta putind fi deasemenea valoarea unui intreg-input
O solutie, care este bazata pe functia input scanf, functie care o vom descrie in cap7, este de a folosi getint care sa returneze ca valoare o functie EOF, atunci cind se intilneste sfirsitul de fisier; orice alta valoare returnata inseamna ca a fost prelucrat un intreg obisnuit Valoarea numerica a intregului gasit este returnata printr-un argument care trebuie sa fie pointer pe un intreg. Aceasta organizare separa starea de sfirsit de fisier de valorile numerice.
Urmatoarea bucla completeaza un tablou cu intergi prin ape luri la get int. int n, v, arrayaSIZEi for (n = 0; n < SIZE && getint(&v) != EOF; n++) arrayani = v;

Fiecare apel pune pe y pe urmatorul intreg gasit la intrare. De notat faptul ca este esential a scrie &y in loc de y, ca arg al lui getint. A scrie doar y constituie eroare de adresare, getint sustinind ca are de a face cu un pointer propriu zis.
Insasi getint este o modificare evidenta a lui atoi tratata mai inainte. getint(pn) /* ia numarul interg -input */ int *pn;
A int c, sign;
while ((c = getch() == ' ' || c == '\n' || c == '\t');
/*sare blancuri*/ sign = 1; if (c == 't' || c == '-') A /* semnul */ sign = (c == '+' ? 1 : -1; c = getch();
S for (*pn = 0; c >= '0' && c <= '9'; c = getch())
*pn = 10 * *pn + c - '0';
*pn *= sign; if (c != EOF) ungetch(c); return(c);
S

Peste tot in getint, *pn este utilizat ca o variabila int ordina ra. Deasemenea, am utilizat getch si ungetch ( descrise in cap
4) in asa fel incit caracterul special ( semnalul EOF) care tre buie citit sa poata fi restocata la intrare.

Exercitiul 5.1 Scrieti getfloat pentru virgula floatnta analoa ga lui getint. Ce tip de valoare returneaza functia getfloat.

5.3. 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 aa10i defineste un tablou de dimensiunea 10, care este un bloc de 10 obiecte consecutive numite aa0i, aa1i, ..., aa9i notatia aaii 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 = &aa0i face ca pa sa pointeze pe al "zero-ulea" element al tabloului a; aceasta inseamna ca pa contine adresa lui aa0i. Acum asignarea x = *pa va copia continutul lui aa0i 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 aa0i

*(pa + 1) refera continutul lui aa1i, pa + i este adresa lui aaii si
*(pa+i) este continutul lui aaii.
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 = &aa0i poate fi scrisa si pa = a

Inca si mai surprinzator la prima vedere este faptul ca o referinta la aaii poate fi scrisa si ca *(a+i). Evaluind pe aaii, C il converteste in *(a+i); cele doua forme sint echivalen te. Aplicind operatorul & ambilor termeni ai acestei echivalente, rezulta ca &aaii 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 paaii 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
A int n; for (n = 0; *s != '0'; s++) n++; return(n);
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 sai 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(&aa2i) si f(a + 2) ambele transmit functiei f adresa elementului aa2i deoarece &aa2i 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 arrai;
A
...
S sau f(arr) int *arr;
A
...
S

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 allocbufaALLOCSIZEi; /* memorie pentru alloc*/ static char *allocp = allocbuf; /*memorarea parti libere*/ char *alloc(n) /* pointer de return pe n caractere */ int n;
A if (allocp + n <= allocbuf + ALLOCSIZE) A allocp += n; return(allocp - n); /* vechiul p */
S else /* nu-i destul loc */ return(NULL)
S free(p) /* zona de memorie libera pointata de p */ char *p;
A if (p >= allocbuf && p < allocbuf + ALLOCSIZE) allocp = p;
S

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 = &allocbufa0i; 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;
A char *p = s;
while (*p != '\0') p++; return(p-s);
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.


5.5 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 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 sai, tai;
A int i; i = 0;
while ((saii = taii != '\0') i++;
S

Iata o versiune a lui strcpy cu pointeri strcpy(s, t) /* copiaza t in s, versiunea pointeri 1*/ char *s, *t;
A
while ((*s = *t) != '\0') A s++; t++;
S
S


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;
A
while ((*s++ = *t++) != '\0')
;
S

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;
A
while (*s++ = *t++)
;
S

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 sai, tai;
A int i; i = 0;
while (saii == taii) if (sai++i == '\0') return(0); return(saii - taii);
S

Versiunea pointeri a lui strcmp. strcmp(s, t) /* returneaza <0 daca s<t, 0 daca s==t,
>0 daca s>t */ char *s, *t;
A for ( ; *s == *t; s++, t++) if (*s == '\0') return(0); return(*s - *t);
S

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;
A char *p, *alloc(); if ((p = alloc(strlen(s) + 1)) != NULL) strcpy(p, s); return(p);
S

In practica, exista o tendinta puternica de a omite declararile: strsave(s) /* salveaza undeva sirul s */
A char *p; if ((p = alloc(strlen(s) + 1)) != NULL) strcpy(p, s); return(p);
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).

5.7. Tablouri multi-dimensionale

C este prevazut cu probabilitatea de a lucra cu tablouri multidi mensionale, cu toate ca in practica exista tendinta ca ele sa fie mult mai putin utilizate decit tabloutile de pointeri. In aceasta sectiune vom da citeva dintre proprietatile lor.
Sa reluam problema de conversia datei, zi-in-luna in zi-in an si viceversa. De exemplu, 1 martie este a 60-a zi dintr-un an nebisect si a 61-a dintr-un an bisect. Sa definim doua functii care sa faca conversia "day_of_year" converteste luna si ziua in ziua din an si luna, iar "month_day" converteste ziua din an in luna si ziua. Daca aceasta din urma functie returneaza doua va lori, argumentele "luna si ziua" vor fi pointeri: month day (1977, 60, &m, &d) puna pe m pe 3 si pe d pe 1.

Aceste functii au nevoie de aceasi informatie, o tabela cu numarul zilelor din fiecare luna ("30 zile are septembrie"). Din moment ce numarul de zil/luna difera in functie de an bisect sau an nebisect, este mai usor sa separam aceste informatii pe doua linii ale unui tablou bidimensional; apoi sa incercam sa tinem cont de ce se intimpla cu februarie in timpul calcului.
Tabloul si functia pentru rezolvarea transformarilor sint dupa cum urmeaza: static int day_taba2ia13i = A
A0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31S,
A0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31S
S; day-of-year(year, month, day) /* pune nr zilei in an */ int year, month, day; /* din luna &an */
A int i, leap; leap = year % 4 ==0 && year % 100 !=0 || year % 400 ==0; for (i = 1; i < month; i++) day += day_tabaleapiaii; return(day);
S month_day(year, yearday, pmonth, pday) /*pune luna, zi */ int year, yearday, *pmounth, *pday; /* din ziua in an */

A int i, leap; leap = year % 4 ==0 && year % 100 !=0 || year % 400 ==0; for (i = 1; yearday > day_tabaleapiaii; i++) yearday -= day_tabaleapiaii;
*pmonth=i;
*pday=yearday;
S

Tabloul day-tab trebuie sa fie extern ambelor functii
"day_of_year" si "month_day", in asa fel incit ambele sa-l poata utiliza.
"day-tab" este primul tablou bidimensional cu care avem de a face in C, Prin definitie un tablou bidimensional este de fapt un tablou unidimensional alei carei elemente sint fiecare in parte cite un tablou. Prin uramre, indicii se scriu astfel day_tabaiiaji in loc de day_tabai, ji ca in majoritatea limbajelor in plus un tablou bidimensional paote fi tratat in mai multe moduri decit in alte limbaje. Elementele sint memorate pe linii, ceea ce inseamna ca indicele din deapta varaiaza primul in asa fel incit elementele sint accesate in ordinea memoriei.
Un tablou se initializeaza printr-o lista de initiali zatori scrisi intre acolade; fiecare liniea unui tablou bidimen sional este initializata printr-o sublista corspondenta. Am ince put tabloul day-tab cu o coloana de zero, in asa fel incit numero tarea liniilor poate fi facuta de la 1 la 12 in loc de 1 0-11.
Daca exista spatiu suficient, este mai usor sa se procedeze innmodul mai sus aratat in loc sa se ajusteze indicii.
In cazul in care un tablou bidimensional trebuie transmis unei functii, declararea argumentelor in funtie trebuie sa includa dimensiunea coloanei, dimensiunea liniei este irelevanta deoarece unei functii i se transmite ca si in cazurile anterioare, uh pointer. In cazul de fata este vorba de un pointer care parcurge obiecte care sint tablouri de cite 13 int. Astfel daca trebuie transmis tabelul day-tab unei functii f, declararea lui f va fi f(day_tab) int day_taba2ia13i;
A
...
S

Declararea argumentului in f va fi deasemenea int day-tabaia13i; din moment ce nr liniilor este irelevant, sau ar putea fi int (*day-tab)a13i; care spune ca argumentul este un pointer pe un tablou de 13 in tregi. Sint necesare parantezele pentru ca crosetele au prioritate mai mare decit *, fara paranteze; declararea

int *day-taba13i; este un tablou de 13 pointeri pe intregi, dupa cum se va vedea in sectiunea urmatoare.

5.8. Tablouri de pointeri, pointeri pe pointeri

Datorita faptului ca pointerii sint ei insisi variabile, este de asteptat ca ei sa fie utilizati in tablouri de pointeri. Deci, se pune problema de a ilustra prin scrierea unui program care sorteaza un set de linii de text in ordine alfabetica, o versiune a sortului utilitar UNIX.
In capitolul 3 am prezentat o functie sort shell care sorta un tablou de intregi. Vom utiliza acelasi algoritm cu exceptia faptului ca acum vom avea de-a face cu linii de text de lungimi diferite si care, spre deosebire de intregi, nu pot fi comparate sau deplasate printr-o singura operatie. Avem nevoie de o reprezentare a datelor care sa poata face eficient si potri vit regulilor in gestionarea linilor de text de lungime diferita.
Acum este momentul potrivit pt a introduce tabloul de pointeri.
Daca liniile de sortat sint memorate cap la cap intr-un lung sir de caractere (rezervat prin alloc, sazicem) atunci fiecare linie poate fi accesata printr-un pointer pe primul sau caracter.
Pointerii insisi pot fi memorati intr-un tablou. Doua linii pot fi comparate prin transmiterea pointerilor respectivi lui strcmp.
Cind doua linii neordonate trebuiesc inversate se inverseaza poin terii lor in tabelul de pointeri, nu liniile insele. Acest mod de lucru elimina cuplul de probleme legate de gestionarea memoriei si, ceea ce este mai presus de orice, poate deplasa liniile reale.
Procesul de sortare consta din trei parti: citirea tuturor liniilor la intrare sortarea liniilor tiparirea liniilor in ordine

Ca de obicei cel mai bine este sa impartim programul in functii care rezolva aceasta defalcare, cu o rutina principala care controleaza totul. Sa aminam pentru un moment pasul de sortare si sa ne concentram asupra structurii de date si a I/O. Rutina de inceput trebuie sa colecteze si sa salveze caracterele din fiecare linie si sa construiasca un tablou de pointeri pe linii. Va trebui, deasemenea sa se numere liniile la intrare, deoarece aceasta informstie este necesara pt sortare si tiparire. Deoarece functia de intrare poate opera doar cu un numar finit de linii input, ea va returna o valoare, cum ar fi -1, in cazul in care se vor prezenta mai multe linii. RUtina de output trebuie doar sa tipareasca liniile in ordinea in care apar i tabloul de pointeri.

#define NULL 0
#define LINES 100 /* maximum de linii de sortat */ main() /* sortarea liniilor de intrare */
A char *lineptraLINESi;/*pointeri pe linii de text*/ int nlines; /* nr liniilor libere */ if ((nlines = readlines(lineptr, LINES)) >= 0) A sort(lineptr, nlines);
writelines(lineptr, nlines);
S else printf("input prea mare pt sort \n");
S
#define MAXLEN 1000 readlines(lineptr, maxlines) /* citeste linii input */ char *lineptrai; int maxlines;
A int len, nlines; char *p, *alloc(), lineaMAXLENi; nlines = 0
while ((len = getline(line, MAXLEN)) > 0) if (nlines >= maxlines) return(-1); else if ((p = alloc(len)) == NULL) return(-1); else A linealen-1i = '\0'; /* zap newline */ strcpy(p, line); lineptranlines++i = p;
S return(nlines);
S

"newline" de la sfirsitul fiecarei linii este sters astfel incit nu va fi afectata ordinea in care sint sortate liniile.

writelines(lineptr, nlines) /* scrie linii la iesire */ char *lineptrai; int nlines;
A int i; for (i = 0; i < nlines; i++) printf("%s\n", lineptraii);
S

Principala noutate este declarata pentru "lineptr": char *lineptraLINESi; spune ca lineptr este un tablou de elemente LINES, fiecare element fiind un pointer pechar. Adica, lineptraii este un pointer pe caractere iar *lineptraii acceseaza un caracter.
Daca lineptr este el insusi un tablou care este transmis lui
writelines, el poate fi tratat ca un pointer in exact aceeasi maniera ca in exemplul nostru anterior, iar functia poate fi scrisa.

writelines(lineptr, nlines) /* scrie linii la iesire */ char *lineptrai; int nlines;
A
while (--nlines >= 0) printf("%s\n", *lineptr++);
S

*lineptr pointeaza initial pe prima linie, cu fiecare incrementare el avanseaza pe linia urmatoare pina cind nlines se epuizeaza.
Intrarea si iesirea fiind controlate, se poate duce la sortare. Sortul -shell din cap 3 va suferi modificari: declaratii le trebuie modificate iar operatia de grupare trebuie montata intr-o functie separata. Algoritmul de baza ramine acelasi ceea ce ne da o oarecare speranta ca totul va merge bine inca short(v, n) /* sorteaza sirurile va0i. . . van-1i */ char *vai; /* in ordine crescatoare */ int n;
A int gap, i, j; char *temp; for (gap = n/2); gap>0; gap /= 2) for (i = gap; i < n; i++) for (j = i - gap; j >= 0; j -= gap)
A if (strcmp(vaji, vaj+gapi) <= 0) break; temp = vaji; vaji = vaj+gapi; vaj+gapi = temp;
S
S

Daca orice element individual din v(alias lineptr) este un pointer pe caractere, temp va fi si el astfel de pointer, asa incit cei doi pot fi copiati unul in altul.
Am scris un program care, in fc de cunostintele din acel moment a fost rapid pe cit posibil. Acest program poate fi facut mai rapid, de exemplu sa copieze liniile input direct intr-un tablou mentinut prin readlines in loc sa le copieze in line pt ca apoi sa le plaseze undeva prin alloc. Dar, pentru a facilita intelegerea programului sa intocmim mai intii o schema logica, si abia dupa aceea sa ne preocupam de eficienta sa.
Modalitatea de a face acest program mai eficeint nu vizeaza neaparat evitarea unei copii a liniilor input. Inlocuirea sortului shell prin ceva mai bun cum ar fi sortul Quicksort, este probabil mai mult decit a marca o simpla diferenta.
In capitolul 1 am semnalat acest lucru deoarece buclele while si for testeaza conditia finala inaintea executarii chiar si pt prima data a corpului buclei; ele ajuta la a ne asigura ca progra mele vor merge chiar si la limita, in particular fara input. Este edificator a umbla prin functiile programelor de sortare pt a verifica ce se intimpla daca nu exista deloc text de intrare.

Exercitiul 5.5. Rescrieti readlines pt a crea linii intr-un tablou umplut cu main, in loc de a apela pe alloc pt rezervarea de memorie, Cu cit este mai rapid acest program ?

5.9. Initializarea tablourilor de pointeri

Sa ne pune problema scrierii unei functii month_name(n) care returneaza un pointer pe un sir de caractere continind numele a n linii. Aceasta este o aplicatie ideala pentru un tablou static intern. month-name contine un tablou propriu de siruri de caracte re si returneaza un pointer pe sirul convenabil atunci cind este apelat. Scopul acestei sectiuni este de a arata cum se initiali zeaza tabloul de nume.
Sintaxa este similara cu cea a initializarilor precedente: char *month-name(n) /*returneaza numele celei de-a n-a luni*/ int n;
A static char *nameai =
A
"luna eronata",
"ianuarie",
"februarie",
"martie",
"aprilie",
"mai",
"iunie",
"iulie",
"August",
"septembrie",
"octombrie",
"noiembrie",
"decembrie"
S; return((n < 1 || n > 12) ? namea0i : nameani);
S

Declararea numelui, care este un tablou de pointeri pe caractere este aceeasi ca si la lineptr, in ex de sortare. Valorile de initializare sint de fapt o lista de caractere; fiecare dintre acestea din urma este asignat pozitiei corespunzatoare din tablou.
Mai precis, caracterele celui de-al i-lea sint plasate undeva iar pointerul pe ele este stocat in nameaii. Daca lungimea tabloului name nu este specificata, compilatorul numara valorile de intializare si pune lungimea corecta.

5.10. Comaparatie pointeri. Tablouri multi-dimensionale

Nou venitii in C sint uneori confuzi in legatura cu deosebirea dintre un tablou bidimensional si un tablou de pointeri cum ar fi name din exemplul de mai sus. Fiind date declaratiile int aa10ia10i; int *ba10i; utilizarile lui a si b pot fi similare, in sensul ca aa5ia5i si ba5ia5i sint ambele referinte legale ale aceluiasi int. Dar a este un tablou in toata regula: toate cele 100 celule de memorie trebuie alocate iar pentru gasirea fiecarui element se face calculul obisnuit al indicelui. Pentru b, oricum prin decla rarea se aloca 10 pointeri; fiecare trebuie facut sa pointeze un tablou de intregi. Presupunind ca fiecare pointeaza cite 10 ele mente din tablou, atunci vom obtine 100 celule de memorie rezerva te, plus cele 10 celule pt pointeri. Astfel tabloul de pointeri utilizeaza sensibil mai mult spatiu si poate cere un pro explicit de initializare. Dar, exista doua avantaje: accesarea unui element se face indirect prin intermediul unui pointer, in loc sa se faca prin inmultire si adunare iar liniile tabloului pot fi de lungimi diferite. Aceasta insemna ca nu orice element al lui b este con strins sa pointeze pe un vector de 10 elemente, unii pot pointa pe cite 2 elemente, altii pe cite 20 si altii pe niciunul.
Desi am mai discutat acest lucru la intregi, de departe, cea mai frecventa utilizare a tabloului de pointeri este cea ilustrata prin mounth-name: sa stocheze lanturi de caractere de lungimi diferite.

Exercitiul 5.6. Rescrieti rutinele day_of_year si mounth-day cu pointeri in loc de indexare.

5.11. Argumentele liniei de comanda

Printre facilitatile oferite de C exista modalitatea de a transmi te argumentele liniei de comanda sau parametrii unui program atunci cind el incepe sa se execute. Pt inceperea executiei este apelat main prin doua argumente. Primul (numit convetional arge) este numarul argumentelor liniei de comanda prin care a fost apelat programul; al doilea (argv) este un pointer pe un tablou de lanturi de caractere care contine argumentele, unul pentru fiecare lant. Manipularea acestor lanturi de caractere este o utilizare comuna a nivelelor multiple de pointeri.

Cea mai simpla ilustrare a declaratiilor necesare si a celor de mai sus amintite este programul echo, care pune pur si simplu pe o singura linie argumentele liniei de comanda, separate prin blancuri. Astfel, daca este data comanda echo hello, world iesirea este hello, world


Prin conventie, argva0i este numele prin care se recunoaste programul, asa ca argc este 1. In exemplul de mai sus, argc este 3 si argva0i, argva1i si argva2i sint respectiv echo, hello si
world. Aceasta este ilustrata in echo: main(argc, argv) /* arg. echo; prima versiune */ int argc; char *argvai;
A int i; for (i = 1; i < argc; i++) printf("%s%c", argvaii, (i <argc-1) ? ' ' : '\n');

Arg fiind un pointer pe un tablou de pointeri, exista citeva modalitati de a scrie acest program care implica manipularea pointerului mai curind decit indexarea tabloului. Sa vedem doua variante: main(argc, argv) /* arg echo; a doua versiune */ int argc; char *argvai;
A
while (--argc > 0) printf("%s%c", *++argv, (argc >1) ? ' ' : '\');
S

Daca argv este un pointer pe inceputul tabloului care contine siruri de argumente, a-l incrementa cu i(++argv) face ca el sa pointeze pe argva1i in loc de argva0i. Fiecare incrementare succesiva muta pe argv pe urmatorul argument; argv este deci pointerul pe acel argument. Simultan argc este decrementat; atunci cind el devine zero, nu mai exista argumente de imprimat. main(argc, ragv) /* arg echo; a treia versiune */ int argc; char *argvai;
A
while (--argc > 0) printf((argc > 1) ? "%s " : "%s\n", *++argv);
S

Aceasta versiune arata ca formatul argumentului lui printf poate fi o expresie ca oricare alta. Aceasta utilizare nu este foarte frecventa dar este bine sa fie retinuta.

Ca un al doilea exemplu, sa facem unele modificari in confi guratia programului de cautare din cap4. In cazul unui apel repe tat, configuratia ce serveste de model va fi prelucrata ca atare, de fiecare data de catre program, ceea ce ar duce la un aranjament evident nesatisfacator. Urmind exemplul utilitarului grep-UNIX, sa schimbam programul in asa fel incit configuratia model sa fie specificata prin primul argument al liniei de comanda.

#define MAXLINE 1000 main(argc, argv)
/* cautarea model specificat prin primul argument*/ int argc; char *argvai;
A char lineaMAXLINEi; if (argc != 2) printf("usage: find pattern\n); else
while (getline(line, MAXLINE)>0) if (index(line, argva1i >= 0) printf("%s", line);
S

Acum poate fi elaborat modelul de baza in asa fel incit sa ilus treze viitoarele constructii realizate cu ajutorul pointerilor.
Sa presupunem ca dorim ca doua argumente sa fie optionale. Unul dintre ele spune "tipareste toate liniile cu exceptia celor care contin modelul "; al doilea cere "fiecare linie tiparita sa fie precedata de numarul curent".

O conventie uzuala pt programele C este legata de argumentul care incepe cu un semn minus si care introduce un flag sau un paramentru optional. Daca se alege -x(pt "exceptie") pt semnalarea inversarii, si ("nr") pt a cere numararea liniilor, atunci comanda find -x -n the cu intrarea now is the time for all good men to come to the aid of their party va produce iesirea

2: for all good men

Argumentele optionale sint admise in orice ordine iar restul programului va fi insensibil la numarul argumentelor care au fost, de fapt, prezente. In particular, apelul la index nu va referi pe argva2i atunci cind a fost un singur flag si nici la argva1i daca n-a existat nici un argument flag. In plus, este convenabil pt utilizatori daca argumentele optionale pot fi adunate, ca in find -nx the

Iata programul

#define MAXLINE 1000 main(argc, argv) /*gasirea configuratiei din primul arg*/ int argc; char *arvai;
A char limaMAXLINES, *s; long lineno = 0; int except = 0, number = 0;
while (--argc > 0 && (*++argv)a0i == '-') for (s = argva0i + 1; *s != '\0'; s++) switch (*s) A case 'x': except = 1; break; case 'n': number = 1; break; default: printf("fiind: ilegal option%c\n", *s); argc = 0; break;
S if (argc != 1) printf("usage: find -x -n pattern\n"); else
while (getline(lim, MAXLINE) > 0)
A lineno++; if ((index(line,*argv)>=0) !=except)
A if (number) printf("%ld: ", lineno); printf("%s", line);
S
S
S argv este incrementat inaintea fiecarui argument optional si argc este decrementat. Daca nu exista erori, la sfirsitul buclei argc va fi 1 iar argv va pointa pe configuratia data. De notat ca
*++argv este un pointer pe un lant de caractere; (*++argv)a0i este primul caracter. Parantezele sint necesare deoarece fara de expresia ar fi *++(argva0i), ceea ce este cu totul altceva (si eronat). O forma corecta ar fi.

**++argv

Exercitiul 5.7. Scrieti programul add care evalueaza o expresie poloneza inversata din linia de comanda. De exemplu, add 2 3 4 + * calculeaza 2 x (3 + 4)

Exercitiul 5.8. Modificati programele entab si detab (scrise ca exercitii in cap 1) in asa fel incit sa accepte ca argumente o lista de tab-stop-uri utilizati tab-urile normale daca nu exista argumente.

Exercitiul 5.9. Extindeti entab si dentab in asa fel incit sa accepte prescurtarea. entab m +n care inseamna tab-stop dupa fiecare n coloane, incepind de la coloana m. Scrieti functia oarecare implicita convenabila pentru utilizator.

Exercitiul 5.10. Scrieti programul tail care tipareste rutinele n linii-input. Presupunem, implicit n=10, dar el poate fi schimbat un argument optional, astfel tail -n imprima ultimele n linii. In mod normal, programul va functiona indiferent de intrare (rationala sau nu), sau de valoare lui n.
Scrieti progarmul in asa fel incit sa utilizeze in mod optim memoria: liniile vor fi pastrate ca in short, nu intr-un tablou bidimensional de lungime fixata.

5.12. Pointeri pe functii

In C functia in sine nu este o variabila, dar exista posibilitatea de a defini un pointer pe o functie, care poate fi manipulat, transmis functiilor, plasat in tablouri etc. Vom ilustra aceasta modificind procedura de sortare scrisa mai anterior in acest cap, in asa fel incit fiind dat argumentul optional-n sa se sorteze liniile input memorie, nu linii copiate.
De obicei, un sort consta in trei parti-o comparatie care realizeaza ordonarea oricarei perechi de obiecte, o schimbare, prin care se inverseaza oridinea obiectelor si un algoritm de sortare care face comparatii si schimbari pina cind obiectele sint definitiv ordonate. Algoritmul de sortare este independent de operatiile de comparare si schimbare, astfel incit prin transmite rea functiei de comparare si schimbare catre el, se va putea realiza sortarea pe diferite criterii. Acest lucru ni-l propunem in noul sort.

Compararea lexicografica a doua linii este realizata prin strcmp iar schimbarea prin swap; avem nevoie de o rutina numecmp care compara doua linii pe baza valorilor numerice si returneaza un indice de conditie de acelasi fel ca si strcmp.
Aceste trei functii sint declarate in main iar pointerii pe ele sint transmisi la sort. Sort la rindul sau apeleaza functiile prin sort, la rindul sau apeleaza functiile prin pointeri. Am sarit peste procesul de tratare a arguentelor eronate, concentrin du-ne astfel pe rezolvarea problemelor principale.

#define LINES 1000 /* maximum de linii de sortat */ main(argc, argv) /* sortarea linii input */ int argc; char *argvai;
A char *lineptraLINESi; /* pointeri pe liniile de text */ int nlines; /* nr linii-input citire */ int strcmp(), numecmp(); /* fc de comparare */ int swap(); /* fc de schimbare */ int numeric = 0; /* 1 daca este sort numeric */ if (argc > 1 && argva1ia0i == '-' && argva1ia1i == 'n') numeric = 1; if ((nlines = readlines(lineptr, LINES)) >= 0)
A if (numeric) sort(lineptr, nlines, numecmp, swap); else sort(lineptr, nlines, strcmp, swap);
writelines(lineptr, nlines);
S else printf("intrare prea mare pentru sort\n");
S strcmp, numecmp si swap sint adrese de functii; din moment ce ele sint cunoscute ca fiind functii, operatorul & nu este necesar la fel cum el nu este necesar inaintea numelui unui tablou. Compila torul este cel care rezolva transmiterea adresei functie.
Al doilea pas este modificarea lui sort: sort(v, n, comp, exch)
/* sorteaza sirurile va0i. . . van-1i */ char *sai; int n; int (*comp)(), (*exch)();
A int gap, i, j; for (gap = n/2; gap > 0; gap /= 2) for (i = gap; i < n; i++) for (j = i - gap; j >= 0; j -= gap)
A if ((*comp)(vaji, vaj+gapi) <= 0) break;
(*exch)(&vaji, &vaj+gapi);
S
S

Declaratiile vor fi studiate cu grija. int (*comp)() spune ca comp este un pointer pe o fc ce returneaza un int.
Primul set de paranteze este necesar; fara ele int *comp() ar spune ca cmp este o functie ce returneaza un pointer pe un integer, ceea ce este cu totul altceva. Utilizarea lui comp in linia if ((*comp)(vaji, vaj+gapi) <= 0) este comparabila cu declaratia potrivit careia cmp este u pointer pe o functie; *comp este functie, iar

(*comp)(vaji, vaj+gapi) este apelul ei. Parantezele sint necesare pentru asocierea corecta a componentelor.
Am ilustrat deja prin strcmp compararea a doua siruri.
Iata numecmp care compara doua siruri numerice pe baza valorii numerice: numcmp(s1, s2) /* compara numeric s1 cu s2 */ char *s1, *s2;
A double atof(), v1, v2; v1 = atof(s1); v2 = atof(s2); if (v1 < v2) return(-1); else if (v1 > v2) return(1); else return(0);
S

Pasul final este adaugarea functiei swap care schimba doi pointeri. Aceasta este adoptata direct din ceea ce am prezentat mai devreme in acest capitol. swap(px, py) /* interschimba *px si *py **/ char *pxai, *pyai;
A char *temp; temp = *px;
*px = *py;
*py = temp;
S

Exista o varietate de alte optiuni care pot fi adaugate la programul de sortare; unele dintre ele pot fi reincercate ca exerctii.

Exercitiul 5.11. Modificati sort in asa fel incit sa gestioneze un flag, -r care indica sortarea in ordine inversa (descresca toare). Bineinteles -r trebuie sa fie compatibil cu -n.

Exercitiul 5.12. Adaugati optiunea -n pt a prelucra impreuna literele mari si literele mici adica sa nu se mai faca distinc tia intre aceste doua tipuri de caractere grafice intimpul sortarii; datele cu litere mari si cee cu litere mici sint sortate impreuna, in asa fel incit a si A apar adiacent, nu separate prin intregul alfabet al literelor mici sau mari.

Exercitiul 5.13. Adaugati optiunea -d ("ordinea din dictionar") care realizeaza comparari doar pentru litere, numere si flancuri. Asiguarti-va ca -d merge impreuna cu -f.

Exercitiul 5.14. Adaugati o faclitatea legata de gestionarea cimpurilor, in asa fel incit sortarea sa poata fi facuta pe cimpuri si interiorul liniilor, fiecare corespunzind unui set independent de optiuni. (Indexul acestei carti fost sortat cu
-df pt ordinea alfabetica si cu -n pentru paginilor.)


Colt dreapta
Creeaza cont
Comentarii:

Nu ai gasit ce cautai? Crezi ca ceva ne lipseste? Lasa-ti comentariul si incercam sa te ajutam.
Esti satisfacut de calitarea acestui document, eseu, cometariu? Apreciem aprecierile voastre.

Nume (obligatoriu):

Email (obligatoriu, nu va fi publicat):

Site URL (optional):


Comentariile tale: (NO HTML)


Noteaza documentul:
In prezent fisierul este notat cu: ? (media unui numar de ? de note primite).

2345678910

 
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite
Colt dreapta