You are on page 1of 104

Introducere

Aprut n 1972, limbajul C este un limbaj de nivel nalt cu utilizare universal (neorientat spre un domeniu limitat). Autorii acestui limbaj sunt Dennis M. Ritchie i Brian W. Kernighan de la Bell Laboratories. Limbajul C a fost proiectat n ideea de a asigura implementarea portabil a sistemului de operare UNIX. Un rezultat direct al acestui fapt este acela c programele scrise n limbajul C au o portabilitate foarte bun. n mod intuitiv, spunem c un program este portabil dac el poate fi transferat uor de la un tip de calculator la altul. n principiu, toate limbajele de nivel nalt asigur o anumit portabilitate a programelor. n prezent, se consider c programele scrise n C sunt cele mai portabile. Dup 1980, limbajul C a fost utilizat de un numr tot mai mare de programatori, cunoscnd o rspndire spectaculoas. n prezent are o poziie dominant n domeniul limbajelor de programare. Principalele caracteristici care au asigurat succesul su, sunt: { Se utilizeaz pentru aplicaii tiinifice i tehnice, cu faciliti grafice; { Permite accesul la toate resursele hardware ale calculatorului pn la nivel de locaie de memorie, ceea ce se obine n general numai n limbaj de asamblare; { Este un limbaj profesional, care prezint att avantajele unui limbaj de nivel nalt ct i avantajele limbajelor de nivel cobort (l. main, l.de asamblare); { Este un limbaj structurat sau procedural (ca i Pascal) i bazat pe compilator; forma .exe a unui program poate fi rulat fr o nou complilare; { Unitatea de baz a unui program n C este funcia; limbajul dispune de o mare varietate de clase de funcii predefinite, puse la dispoziia utilizatorului, fiind grupate n biblioteci; { Se consider c efortul pentru scrierea unei aplicaii n C este mai mic dect n Pascal dar durata de nvare este mai mare. Odat cu evoluia limbajului (i rspndirea sa) a fost necesar adoptarea unui standard internaional care s defineasc limbajul, biblioteca de funcii i s includ o serie de extensii utile, impuse de marea varietate de aplicaii. Limbajul C - standard sau C - ANSI

(American National Standard Institute) a aprut n 1988 i reprezint varianta inclus n toate dezvoltrile ulterioare ale limbajului (cu mici excepii). Anii '90 sunt consacrai programrii orientate spre obiecte ( Object Oriented Programming - OOP) la fel cum anii '70 au fost numii anii programrii structurate . Programarea orientat spre obiecte este un stil de programare care permite abordarea eficient a aplicaiilor complexe, ceea ce se realizeaz, n principiu, prin: { elaborarea de componente reutilizabile, extensibile i uor de modificat, fr a reprograma totul de la nceput; { construirea de biblioteci de module extensibile; { implementarea simpl a interaciunilor dintre programe i sistemul de calcul, prin utilizarea unor elemente standardizate. n anii '80, interesul pentru programarea orientat spre obiecte a crescut, ceea ce a condus la apariia de limbaje de programare care s permit utilizarea ei. Limbajul C a fost dezvoltat i el n aceast direcie, astfel c n 1980 a fost lansat limbajul C++. Acesta, elaborat de Bjarne Stroustrup de la AT&T, este o extindere a limbajului C i permite utilizarea principalelor concepte ale programrii orientate spre obiecte. Limbajul C++ a fost implementat pe microcalculatoare compatibile IBM PC, n mai multe variante. Cele mai importante implementri sunt cele realizate de firmele Borland i Microsoft. Conceptele programrii orientate spre obiecte au influenat n mare msur dezvoltarea limbajelor de programare n ultimii ani. Multe limbaje au fost extinse astfel nct s permit utilizarea conceptelor programrii orientate spre obiecte. Au aprut chiar mai multe extensii ale aceluiai limbaj. De exemplu, n prezent asistm la extensiile limbajului C++. O astfel de extensie este limbajul E ai crui autori sunt Joel E. Richardson, Michael J. Carey i Daniel T. Shuh, de la universitatea Wisconsin Madison. Limbajul E a fost proiectat pentru a permite exprimarea simpl a tipurilor i operaiilor interne, pentru sistemele de gestiune a bazelor de date. O alt extensie a limbajului C++ este limbajul O, dezvoltat la Bell Laboratories. Cele dou limbaje (E i O) sunt n esen echivalente. Interfeele de utilizator au atins o dezvoltare mare datorit facilitailor oferite de componentele hardware ale diferitelor tipuri de calculatoare. n principiu, ele simplific interaciunea dintre programe i utilizatorii lor. Astfel, diferite comenzi, date de intrare i rezultate

pot fi exprimate simplu i natural utiliznd ferestre de dialog, bare de meniuri, cutii, butoane etc. Implementarea interfeelor de utilizator este mult simplificat prin utilizarea limbajelor orientate spre obiecte. Aceasta, mai ales datorit posibilitilor de utilizare a componentelor standardizate aflate n biblioteci specifice. Firma Borland comercializeaz o bibliotec de componente standardizate pentru implementarea interfeelor de utilizator folosind unul din limbajele C++ sau Pascal. Produsul respectiv se numete Turbo Vision. De obicei, interfeele de utilizator gestioneaz ecranul n modul grafic. Una din cele mai populare interfee de utilizator grafice, pentru calculatoarele IBM PC, este produsul Windows al firmei Microsoft. Lansat n 1985, produsul Windows a parcurs mai multe variante i are un succes fr precedent. ncepnd cu Windows 95, a devenit sistem de operare, care a nlocuit treptat sistemul MS DOS. Aplicaiile sub Windows se pot dezvolta folosind diferite medii de programare, ca: Turbo C++, Pascal, Microsoft C++7.0, Microsoft Visual Basic, Visual C i Visual C++.

1 Elemente de baz ale limbajului C


1.1. Setul de caractere
Reprezint mulimea de caractere ce pot fi utilizate n textul unui program. Setul de caractere al limbajului C este un subset al standardului ASCII (American Standard Code for Information Interchange ) i conine: { Literele mari: A, B, C, . . . , V, W , Z; { Literele mici: a, b, c, . . . , v, w, z; { Cifrele zecimale: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9; { Caractere speciale (29) :

. , ; : ? ' " ( ) [ ] { } ! | \ / ~ _ # % & ^ + - * = < >


Alte caractere i semne pot s apar n comentarii, pot fi constante de tip caracter sau iruri de caractere. Caractere de tip spaiu (neimprimabile): \n - newline (produce trecerea la nceputul rndului urmtor); \t - tab. orizontal (introduce mai multe spaii libere succesive; se utilizeaz pentru organizarea imprimrii pe coloane); \v - tab. vertical (introduce mai multe rnduri libere pe vertical); \r - carriage return (deplasarea carului de imprimare la nceputul rndului curent); \f - form feed (salt la pagin nou). Setul de caractere neimprimabile depinde de varianta limbajului.

1.2. Cuvinte rezervate


Sunt cuvinte n limba englez cu semnificaie predefinit. Ele nu pot fi utilizate ca identificatori sau cu alt semnificaie dect cea standard. auto break case char const double else enum extern float int long register return short struct switch typedef union unsigned

continue for signed void default goto sizeof volatile do if static while Cuvintele rezervate (sau cuvintele cheie) sunt utilizate n construirea instruciunilor, declararea tipurilor de date sau drept comenzi; n anumite situaii, pot fi redefinite (nlocuite cu alte cuvinte). Cuvintele rezervate se scriu cu litere mici.

1.3. Identificatori
Sunt nume date funciilor, variabilelor, constantelor, n vederea utilizrii lor n programe. Un identificator este o succesiune de litere i cifre, primul caracter fiind obligatoriu o liter. Se pot utiliza literele mari i literele mici din setul de caractere, precum i caracterul subliniere _ considerat liter. Numrul de caractere din componena unui identificator nu este limitat dar n mod implicit, numai primele 32 de caractere sunt luate n consideraie. Aceasta nseamn c doi identificatori sunt diferii, numai dac difer n primele 32 de caractere. Standardul ANSI garanteaz 6 caractere / identificator. Mediile de programare Turbo C i Turbo C++ permit utilizatorului modificarea limitei de 32. n limbajul C, literele mari difer de cele mici, aadar identificatorii: aria_trg , Aria_trg , ARIA_TRG, difer ntre ei. Se recomand ca identificatorii s fie nume sugestive pentru rolul obiectelor pe care le definesc. De exemplu, funcia care calculeaz o sum se va numi sum , cea care calculeaz factorialul, fact etc. n mod tradiional, identificatorii de variabile se scriu cu litere mici, constantele i macrodefiniiile cu litere mari etc. Exemple:
Identificatori coreci d1, x1, x2, y11, alfa, beta contor_1, data_16 delta_ec_2, tabel_nume TipElementCurent Identificatori incoreci $contor struct tab nr.2 2abs, 5xx, Observaii $ - caracter interzis cuvnt cheie conine un spaiu i punct ncep cu o cifr

Observaii: Cuvintele cheie nu pot fi utilizate ca identificatori; {

{ Pentru identificatori se utilizeaz numai setul de caractere C; ca urmare, punctul, virgula, semnele de operaii etc., nu se utilizeaz n construirea identificatorilor; { Identificatorii nu trebuie s conin spaiu liber; { Pentru un identificator format din mai multe cuvinte, fie se utilizeaz liniua de subliniere, fie se insereaz litere mari pentru separarea cuvintelor.

1.4. Comentarii
Sunt texte explicative plasate n textul programului, pentru a facilita nelegerea lui i pentru a marca principalele aciuni. Comentariile nu sunt luate n consideraie de ctre compilator i ca urmare trebuie delimitate prin caractere speciale. Se delimiteaz prin /* nceputul de comentariu i prin */ sfritul de comentariu. Exemplu: /* Acesta este un comentariu */ Dac se omite marcarea sfritului de comentariu, textul programului (aflat dup comentariu) se consider ca fcnd parte din acesta! n limbajul C++ s-a mai introdus o convenie pentru a insera comentarii i anume succesiunea : // marcheaz nceput de comentariu. Comentariul delimitat prin // se termin pe acelai rnd pe care se afl i nceputul lui, fr s mai fie necesar marcarea sfritului. Observaii: { Comentariile pot fi introduse oriunde n programul surs; { Comentariile pot fi scrise pe una sau mai multe linii de text; { Nu este permis introducerea unui comentariu n interiorul altui comentariu. n general, se recomand introducerea de comentarii dup antetul unei funcii, care s precizeze: { Aciunea sau aciunile realizate de funcie; { Tipul datelor de intrare i ieire; { Algoritmii realizai de funcie, dac sunt mai complicai; { Limite impuse datelor de intrare, dac este cazul.

1.5. Structura unui program

Un program conine una sau mai multe funcii. Dintre acestea, una singur este funcia principal . Fiecare funcie are un nume. Numele funciei principale este main. Celelalte funcii au nume date de utilizator sau au nume predefinit, dac sunt funcii standard, de bibliotec. Programul se pstreaz ntr-un fiier sau n mai multe fiiere. Acestea au extensia .c pentru limbajul C i .cpp pentru C++. Un fiier care conine textul programului sau o parte a acestuia se numete fiier surs. Prin compilarea unui fiier surs, compilatorul genereaz un fiier obiect , cu extensia .obj . Fiierele surs care intr n compunerea unui program pot fi compilate separat sau mpreun. Fiierele obiect, corespunztoare unui program, pot fi reunite ntr-un program executabil, prin editarea de legturi (link - editare), rezultnd un fiier executabil, cu extensia .exe . Execuia unui program ncepe cu prima instruciune a funciei main . Dac funcia main( ) face apel la alte funcii, acestea vor intra n aciune cnd vor fi chemate de main ( ). Exemplu: # include <stdio.h> void main(void) { printf("C el mai simplu program C \n"); } { Prima linie este o directiv pentru compilator, pentru a include n textul surs fiierul cu numele stdio.h (standard input output) aflat ntr-un director predefinit. Directivele de compilator au marcajul #. { A doua linie este antetul funciei main() - funcia principal. Cuvntul void arat c funcia este fr tip (nu furnizeaz valori) iar void dintre paranteze arat c funcia este fr parametri. { Corpul funciei este delimitat de paranteze acolade { }. Funcia main conine un apel la alt funcie, printf() - funcie de bibliotec. Ea realizeaz tiprirea sau afiarea unui mesaj, conform unui anumit format (print format). Funcia printf este o funcie de intrare/ieire, care se gsete n fiierul header stdio.h; de aceea acest fiier trebuie inclus n toate programele n care se fac operaii de citire / scriere.

n limbajul C, nu exist instruciuni de intrare / ieire (pentru citire i scriere date) ca de exemplu read, write din Pascal sau Fortran. Acest fapt se justific prin dependena instruciunilor de intrare/iere de sistemul de operare. Autorii limbajului C au optat pentru realizarea operaiilor de intrare / ieire cu ajutorul funciilor de bibliotec. Semnul ; este separator de instruciuni (ca n Pascal). Funcia printf() are un singur parametru = ir de caractere, delimitat de ghilimele ". . . ". Ultimele caractere sunt de tip neimprimabil i determin trecerea la linie nou, dup imprimarea textului.

1.5.1. Structura unei funcii n programul surs, o funcie are dou pri: antetul i corpul funciei. O funcie poate avea un numr de parametri (variabile) dar o singur valoare - valoarea funciei. Antetul conine informaii despre tipul valorii funciei, numrul i tipul parametrilor i conine identificatorul (numele) funciei. Structura unei funcii: <tip > <nume > (< lista declaraiilor parametrilor formali >) { <declaraii > <instruciuni > } Primul rnd este antetul; acesta apare obligatoriu naite de corpul funciei dar i la nceputul programului (nainte de main()) situaie n care se numete prototip . n cazul tipurilor standard, <tip> din antet este un cuvnt cheie care definete tipul valorii returnate de funcie. n C exist dou categorii de funcii: { funcii cu tip, care returneaz o valoare, de tipul <tip>; { funcii fr tip, care nu returneaz nici o valoare dup execuie, dar efectueaz anumite prelucrri de date; pentru acestea se va folosi cuvntul rezervat void n calitate de <tip>. O funcie poate avea zero, unul sau mai muli parametri. Lista declaraiilor parametrilor (formali) este vid cnd funcia nu are parametri. Exemple: 1. void ff_1(void)

{ . . . . } Funcia ff_1 este fr tip, deci nu returneaz o valoare concret i nu are parametri. 2. void ff_2() { . . . . } Funcia ff_2 este fr tip i nu are parametri (variant de scriere). 3. int ff_3() { . . . . }

Funcia ff_3 returneaz o valoare de tip ntreg, dup execuie, dar nu are parametri.

4.

float ff_4(int a, int b) { . . . . }

Funcia ff_4 returneaz o valoare de tip real (floating point) i are doi parametri de tip ntreg (funcie real cu dou variabile ntregi). n corpul funciei pot s apar una sau mai multe instruciuni return, care au ca efect revenirea n programul apelant. Dac nu apare nici o instruciune return, revenirea n programul apelant se face dup execuia ultimei instruciuni din corpul funciei. n cazul n care funcia are mai muli parametri, declaraiile de tip pentru parametri se separ prin virgul. Parametrii se utilizeaz pentru a permite transferul de date, ctre o funcie, n momentul utilizrii ei n program. La construirea unei funcii, se face abstracie de valorile concrete. Acestea vor fi prezente numai la execuia programului. n momentul compilrii, este necesar doar cunoaterea tipurilor valorilor pe care le vor primi parametrii la execuie. Aceste declaraii

de tip sunt indicate n antetul funciei i vor servi compilatorului s rezerve memoria necesar pentru fiecare parametru. Parametrii declarai n antetul unei funcii se numesc formali, pentru a evidenia faptul c ei nu reprezint valori concrete, ci numai in locul acestora, pentru a putea exprima procesul de calcul. Ei se concretizeaz la execuie prin valori ce rezult din apelarea funciei, cnd sunt numii parametri efectivi. Observaii: { Dac nu se specific tipul unei funcii, se consider automat c ea va returna o valoare de tip int . { n limbajul C++, controlul cu privire la tipul valorii unei funcii este mai strict i ca urmare se recomand, ca tipul s fie efectiv specificat, n toate cazurile. { Pentru funcia principal main, se pot utiliza antetele: int main() int main(void) void main() void main(void) main() main(void) Primele dou antete arat c funcia returneaz o valoare ntreag; de asemenea ultimele dou antete arat c se returneaz o valoare ntreag, chiar dac nu se specific tipul int n antet. Se utilizeaz foarte frecvent varianta fr tip main(). Funcia main poate avea i parametri. 1.5.2. Declararea variabilelor. Clase de alocare Obiectele de baz ale limbajului sunt variabilele. Acestea se declar la nceputul funciilor (variabile locale) sau n exteriorul tuturor funciilor (variabile globale), precizndu-se clasa de alocare (eventual, vezi cap.5), tipul, numele i valorile iniiale (eventual). Pentru a putea folosi variabile ntr-un program, acestea trebuie declarate. O declaraie de variabile conine obligatoriu un specificator de tip (tipul de date) i o list de identificatori, de exemplu: int a,b,c; prin care se declar (se rezerv spaiu de memorie) trei variabile de tip int (ntregi cu semn), numite a, b, c. Dimensiunea unui tip de date depinde de implementarea limbajului i se exprim prin numrul de locaii de memorie (octei, un octet = 8 bii) necesare pentru stocarea valorii. Dimensiunea tipului int este 2 sau 4 octei (16 sau 32 de bii). Un ntreg pe 16 bii poate lua valori n domeniul [-32768, 32767].

Alte tipuri uzuale sunt: char - tipul caracter, dim.=1 octet; long - tipul ntreg lung, dim.=4 octei; short - tipul ntreg scurt; float - tipul real, simpl precizie, dim.=4 octei; double - tipul real, dubl precizie, dim.=8 octei; long double - tipul real, precizie extins, dim.=10 octei. Variabilele pot fi iniializate cu valori, chiar la declarare. n exemplul de mai jos, variabilele reale x i z sunt iniializate iar y, nu. float x =10.07 , y, z = 2.0; Programul urmtor produce tabelarea valorilor unei funcii de dou variabile, n domeniul [0, 1]x[0, 1], cu pasul 0.1. # include <stdio.h> double fm(double, double); void main(void) { double x, y, pas=0.1; for(x=0.0; x<=1.0; x=x+pas) for(y=0.0; y<=1.0; y=y+pas) printf("x=%lf y=%lf f(x,y)=% lf \n ", x, y, fm(x, y)); { double fm(double x, double y) { return (3.0*x*y + 1.0)/(1.0 + x + y + x*y); } Programul conine dou funcii: main() i fm(). Funcia matematic fm() are doi parametri de tip real, doubl precizie i anume x i y iar tipul valorii funciei este tot real, dubl precizie. Numele funciei reprezint valoarea ntoars de aceasta, de exemplu, fm(0.0, 1.0)=0.5 este valoarea lui fm cnd argumentele sale iau valorile x=0 i y=1. Instruciunea return <expresie>, ntoarce valoarea expresiei n programul apelant, aceast form fiind obligatorie la funciile cu tip. Declaraia double fm(double, double) ; de la nceputul programului este un prototip al funciei fm(), care descrie tipul funciei, numele i tipul fiecrui parametru. n acest mod, funcia fm() este recunoscut n main(), chiar dac definiia ei se afl dup main() .

n general, este indicat s se scrie prototipurile tuturor funciilor, att ale celor definite n modulul curent de program, ct i ale celor definite n alte module dar folosite n modulul curent. Funciile de bibliotec sunt complet definite (inclusiv prototipurile) n fiiere header, deci includerea acestor fiiere, asigur i prezena prototipurilor. De exemplu, prototipul lui printf() este n fiierul stdio.h . n funcia princial main() se declar trei variabile, x, y i pas, ultima fiind iniializat cu valoarea 0.1. Cele dou instruciuni for implementeaz execuia repetat a funciei printf(), pentru 11 valori ale lui x i 11 valori ale lui y, n total funcia se repet de 11 x 11 = 121 de ori. Prima aciune (x = 0.0) este o iniializare i se execut o singur dat. A doua expresie din for este condiia de repetare: dac x<=1, execuia va fi reluat pentru valoarea curent a lui x. Ultima aciune (x = x + pas) este actualizarea variabilei x i se execut la fiecare iteraie, dup execuia corpului ciclului. Primul ciclu for conine un alt ciclu for, cu variabila y, care se va executa de 11 ori pentru fiecare valoare a lui x. La fiecare execuie a ciclului cu y, se tipresc valorile x, y, fm(x, y), cu ajutorul funciei printf(). irul de caractere din printf() conine specificatori de format, anume secvenele %lf, care indic modul de interpretare a valorilor ce se afiaz. Ele vor fi interpretate ca valori reale n dubl precizie. Evident, numrul i tipul specificatorilor de format trebuie s coincid cu parametrii care urmeaz dup irul ce descrie formatul. Restul caracterelor din ir vor fi tiprite prin copiere. Secvena \n asigur trecerea la rndul urmtor dup scrierea celor trei valori.

2 Tipuri de date, operatori i expresii


Tipul scalar (char ) { caracter { ntreg { real { adres (pointer ) { enumerativ (enum ) (int) (float ) Tipul structurat { tablou { structur { uniune { ir de caractere Tipul void (fr tip) { void

2.1. Tipuri de baz (scalare)


Pentru utilizarea eficient a memoriei, limbajul C ofer mai multe tipuri de reprezentare pentru numerele ntregi i reale: { pentru ntregi: int, short int, long int , cu sau fr semn; { pentru reale: float, double, long double . Fiecare variabil sau funcie trebuie asociat cu un tip de date nainte de a fi efectiv folosit n program, prin declaraii de forma: int c1, k, m; float x, y, w=21.7; long double ff_1(double, double); Pe baza acestor declaraii, compilatorul rezerv memorie pentru fiecare variabil, corespunztor tipului su. 2.1.1. Tipul ntreg Este o submulime a numerelor ntregi; se reprezint pe 1, 2 sau 4 octei, respectiv pe 8, 16 sau 32 de bii. Tip Domeniul valorilor (IBM - PC) Lungime (bii) int short int long int unsigned short unsigned long signed char 16 16 32 16 32 8 -32 768 . . . +32 767 -32 768 . . . +32 767 -2 147 483 648 . . . +2 147 483 648 0 . . . 65 535 (fr semn) 0 . . . 4 294 967 295 (fr semn) -128 . . . +127 (cu semn)

8 0 . . . 255 (fr semn) unsigned char Restriciile impuse de standardul ANSI sunt: dim (short) m 16 bii ; dim (int) m 16 bii ; dim (long) m 32 bii ; dim (short) [ dim (int) [ dim (long) ; Observaie: declaraia short int este echivalent cu short iar long int este echivalent cu long , fr s apar confuzii. Constante de tip ntreg Pot fi scrise n sistemul de numeraie zecimal (baza 10), octal (baza 8) sau hexazecimal (baza 16). O constant zecimal ntreag este un ir de cifre zecimale, cu prima cifr diferit de zero. Constantele ntregi reprezentate pe 16 bii sunt de tip int iar cele reprezentate pe 32 de bii sunt de tip long. Dac dorim s reprezentm o constant ntreag pe 32 de bii, dei ea se poate reprezenta pe 16 bii, constanta trebuie s aib sufixul L sau l. Constantele ntregi sunt, de regul, cu semn. Dac dorim s fie fr semn, constanta se termin cu litera U sau u. Constantele ntregi fr semn se utilizeaz pentru a economisi memorie. Astfel, constantele de tip int din intervalul [32768, 64535] se pstreaz n memorie pe 32 de bii, iar constantele de tip unsigned din acelai interval se reprezint pe 16 bii. Pentru a reprezenta constante ntregi de tip long, fr semn, se utilizeaz sufixele echivalente: ul, lu, LU, UL. O constant octal ntreag este o succesiune de cifre octale (0. .7), prima cifr fiind un zero, nesemnificativ. Sufixele L, l, U, u, se pot utiliza cu aceleai semnificaii ca la constantele zecimale. O constant hexazecimal ntreag este o succesiune de cifre hexazecimale (0. .9, A, B, C, D, E, F), sau (0. . 9, a, b, c, d, e, f) primele dou caractere fiind 0x sau 0X. Sufixele L, l, U, u, se pot utiliza cu aceleai semnificaii ca la constantele zecimale. Exemple: 132 12345L 0400 04000L 0xabbf

constant constant constant constant constant

zecimal de tip int; zecimal de tip long; octal; octal de tip long; hexazecimal;

0X2ACFFBL constant hexazecimal de tip long; Indicarea tipului este util cnd se folosesc funcii de bibliotec avnd argumente de tip long sau fr semn; tipul constantei trebuie s fie compatibil cu tipul predefinit al argumentului. Operaiile de citire i scriere a valorilor de tip ntreg, cu funciile de bibliotec printf(. . .) i scanf(. . .), necesit specificatori de format corespunztori. Tipul n zecimal n octal n hexazecimal int long short unsigned %d %ld %hd %du %o %lo %ho %ou %x %lx %hx %xu

Programul urmtor citete o valoare de tip zecimal i scrie pe ecran aceeai valoare n octal i n hexazecimal: # include <stdio.h> void main() { int val; printf("Introducei o constant de tip ntreg:"); scanf("%d",&val); printf("Valoarea %d in baza 8 este: %o\n", val, val); printf("Valoarea %d in baza 16 este: %x\n", val, val); } Tiprirea unei valori cu semn, cu formatul %d poate duce la valori graite, dac valoarea nu se ncadreaz n domeniul int: # include <stdio.h> main() { unsigned short n1=40000, n2=400; printf("n1 si n2 considerate ca intregi fara semn:\n"); printf("n1=%hu n2=%hu \n", n1, n2); printf("n1 si n2 considerate ca intregi cu semn:\n"); printf("n1=%hd n2=%hd \n", n1, n2); } Programul are ca efect pe ecran: n1 si n2 considerate ca intregi fara semn: n1=40000 n2=400 n1 si n2 considerate ca intregi cu semn: n1=-25536 n2=400

2.1.2. Tipul caracter Este asociat cu tipul ntreg fr semn, pe 8 bii, deoarece fiecare caracter are un numr de ordine, n codul ASCII, cuprins ntre 0 i 255. O constant de tip caracter imprimabil se reprezint prin caracterul respectiv, cuprins ntre semne apostrof: 'a', 'A', 'b', 'B', '7' etc. Limbajul C permite efectuarea de operaii aritmetice asupra unor valori de tip caracter i se pot da valori numerice unor variabile de acest tip. Exemplu: char c, car; //se definesc doua variabile caracter . . . . . . . car ='a'; // car = 97, codul lui 'a' c = 97 // c = 97, deci c = car

2.1.3. Tipul real Este o submulime a numerelor reale, mai precis, o submulime a numerelor raionale. O constant de tip real se compune din: { o parte ntreag, numr ntreg cu semn (poate lipsi); { o parte fracionar, ir de cifre zecimale cu punct n fa; { un exponent, numr ntreg cu semn (poate lipsi). Partea fracionar poate lipsi numai cnd este prezent partea ntreag, meninnd punctul de separare: 2. sau 0. sau -314. Exponentul este un ntreg cu sau fr semn, precedat de litera e sau E i reprezint o putere a lui 10. n mod implicit, constantele reale se reprezint n dubl precizie, deci pe 64 de bii. Pentru economie de memorie, se poate opta pentru reprezentare pe 32 de bii, prin adugarea literei f sau F la sfrit. Pentru reprezentare pe 80 de bii, se adaug la sfrit litara L sau l. Exemple: Constant real Valoare 123. 123,0 -765.77 .255 69e4 .23E-2 222.7e2 3377.5678e-5 -765,77 0,255 690 000,0 0, 0023 22270 0,033775678

Variantele utilizate pentru tipurile reale sunt descrise n tabelul urmtor. Tip Domeniul valorilor absolute Lungime (bii) 32 float 3, 4 % 10 38 3, 4 % 10 38 64 double 1, 7 % 10 308 1, 7 % 10 308 3, 4 % 10 4932 3, 4 % 10 4932 80 long double Fiierele header limits.h i float.h conin constante care definesc implementarea tipurilor de baz. Variabilele de tip real se declar prin specificarea tipului i listei de identificatori: float val_1, val_f, zz, t, w; double xx_1, y; long double epsilon, pass_1; Observaii: { Nu trebuie utilizate inutil variabile i constante de tip double sau long double, deoarece determin creterea timpului de calcul i ocup inutil memorie suplimentar; { Descriptorii de format la tiprire cu printf( ) sunt: %f sau %lf pentru scriere normal, i %e sau %E, pentru scriere cu exponent. Exemplu: # include <stdio.h> void main() { float nr_real=0.0057; printf("nr_real=%f \n", nr_real); printf("nr_real=%6.4f \n", nr_real); printf("nr_real=%e \n", nr_real); } Rezultatul pe ecran este: nr_real=0.005700 nr_real=0.0057 nr_real=5.70000e-03 Codul 6.4 asociat cu %f realizeaz controlul formatului de scriere prin indicarea spaiului total (6 caract.) i a prii fracionare (4 caract.). Valorile de tip float sunt convertite automat la double de ctre funcia printf( ), deoarece reprezentarea implicit a constantelor reale este pe 64 de bii, deci double.

Valorile de tip long double necesit specificatori de format %Lf sau % lf. Constantele simbolice se definesc prin directiva #define, de exemplu: # define MAX 4500 //constanta simbolica de tip intreg # define X_REF 37.99f //constanta simbolica de tip real Constantele simbolice sunt substituite la compilare cu valoarile prin care au fost definite. Se recomand scrierea lor cu litere mari. Valoarea unei constante simbolice nu poate fi modificat la execuie. 2.1.4. Constante de tip ir de caractere Un ir de caractere este o succesiune de caractere, eventual vid, ncadrat de ghilimele ". . . . " , de exmplu: "Limbajul C++ este un limbaj profesional" n limbajul C, nu exist o declaraie special, ca de exemplu, string n Pascal, pentru iruri de caractere. Acestea sunt considerate tablouri de caractere, fiecare element al tabloului fiind un caracter, care ocup n memorie un octet (8 bii). Declararea datelor de tip ir de caractere, se face astfel: char num_pr[20]; unde num_pr reprezint un identificator (nume i prenume) iar 20 este lungimea irului, adic irul are maxim 20 de caractere. Compilatorul adaug un caracter special, de sfrit de ir, neimprimabil, '\0'. irurile de caractere pot fi scrise i citite cu funciile printf( ) i scanf( ), specificatorul de format fiind %s. # include <stdio.h> void main() { char prenume[20]; printf("Care este prenumele tau? \n"); scanf("%s" , &prenume); printf("Buna ziua %s \n", prenume); }

2.1.5. Tipul pointer Fiecrui obiect dintr-un program (variabil, constant, funcie), i se aloc un spaiu de memorie exprimat n numr de locaii (octei, bytes), corespunztor tipului de date prin care a fost definit obiectul respectiv. Localizarea (gsirea) unui obiect n memorie se face prin intermediul adresei la care a fost memorat. Adresa este un numr ntreg, fr semn i reprezint numrul de ordine al unei locaii de memorie; n C adresele sunt de regul de tip long int, domeniul int fiind insuficient. Datele de tip adres se exprim prin tipul pointer. n limba romn, se traduce prin referin, reper, localizator, indicator de adres . MEMORIE Locaia par Locaia impar 11110000 11010100 1 1 0 0 10 1 0 00000000 00000000 00000000 00000000 00000000 11110000 1 1 0 0 10 1 0 10101010 10101110 1 1 0 0 10 1 0 10000000 11110000 00000000 00000000 00000000 00111111 1 1 0 0 10 1 0 . . . .

Adresa 0000 0002 ...... 0026 0028 0030 0032 0034 0036 0038

Variabila . . . . . . . . . . . . x = 128 y = 240

z = 1.0

n tabelul de mai sus, este reprezentat o zon de memorie, n care sunt stocate variabilele x, y, z, declarate astfel: int double *px, *py, x=128, y=240 ; *pz, z=1.0 ;

Se rezev doi octei pentru variabilele de tip ntreg i 8 octei (64 de bii) pentru variabila z, de tip real, dubl precizie.

Variabilele px, py, pz, sunt de tip pointer (adres); ele pot furniza adresele variabilelor de tip corespunztor, dac sunt asociate cu acestea prin atribuiri de forma: px = &x; py = &y; pz = &z; Din px = &x i din tabelul de mai sus, rezult px = 0026 ; Analog, py = 0028, pz = 0030. Observaii: { Variabilele de tip pointer se declar cu prefixul '*' : *p, *u, *v ; { Variabilele de tip pointer se asociaz cu adresa unui obiect prin atribuiri de forma p = &var, unde var este o variabil declarat; semnul '&' arat c se consider adresa lui var (care se transfer n pointerul p); { O variabil de tip pointer trebuie declarat astfel: <tip de baz> *<identificator> ; unde tipul de baz reprezint tipul de date pentru care va memora adrese. Exemple: float *a1, var_1; / /pointerul a1 va memora adrese pentru valori float int *a2, var_2; // pointerul a2 va memora adrese pent ru valori int a1 = &var_1 ; // atribuire corect , var_1 este de tip float a2 = &var_2 ; // atribuire corect, var_2 este de tip int a2 = &var_1 ; // greit, pentru c a2 se asociaz cu variabile int { Operaia invers, de aflare a valorii din memorie, de la o anumit adres, se realizeaz cu operatorul * pus n faa unui pointer: int *p, x, y; x = 128; p = &x; // p = adresa lui x y = *p; // y = valoarea de la adresa p, deci y = 128 y = *&x; // y = valoarea de la adresa lui x, deci y = 128; Aadar, operatorul * are efect invers fa de operatorul & i dac se pun consecutiv, efectul este nul: x = *&x, sau p = &*p. { Un pointer poate fi fr tip , dac se declar cu void : void *p ; caz n care p poate fi asociat cu orice tip de variabile. Exemple: int x; float y; void *p; . . . . . . p = &x ; // atribuire corect, p este adresa lui x

p = &y ;

// atribuire corect, p este adresa lui y

{ O variabil pointer poate fi iniializat la fel ca oricare alt variabil, cu condiia ca valoarea atribuit s fie o adres: int k ; // pointerul adr_k este egal cu adresa lui k int *adr_k = &k ; Un pointer nu poate fi iniializat cu o adres concret, deoarece nu poate fi cunoscut adresa atunci cnd se scrie un program; adresa este dat de sistemul de operare dup compilare, n funcie de zonele libere de memorie, disponibile n momentul execuiei. Astfel, declaraii de tipul: p = 2000 ; &x = 1200 ; sunt eronate i semnalizate de compilator ca erori de sintax. O expresie de tipul *<adresa> poate s apar i n stnga: *adr_car = 'A' ; // caracterul A se memoreaz la adresa= adr_car. Exemple de programe care utilizeaz pointeri: #include <stdio.h> main ( ) { int x = 77 ; printf("Valoarea lui x este: %d \n", x); printf("Adresa lui x este: %p \n", &x); } Funcia printf() utilizeaz specificatorul de format %p, pentru scrierea datelor de tip adres. 2) #include <stdio.h> main ( ) // afisarea unei adrese si a valorii din memorie { char *adr_car, car_1='A' ; adr_car = car_1; // pointerul ia valoarea adresei lui car_1 printf("Valoarea adresei: %p \n", adr_car); printf("Continutul de la adr_car %c \n", *adr_car); }
#include <stdio.h> main ( ) // perechi de instruct iuni cu acelasi efect int x=177, y, *p ;

1)

3) {

y = x + 111; // y = 177+111=288 p = &x; y = *p + 111; // y = 177+111=28 8, acelasi efect x = y ; // x = 288 p = &x; *p = y ; // x = 288, adic valoarea lui y x++ ; // x = x+1, deci x = 299 p = &x ; (*p)++ ; // x = 299+1=300 }

2.1.6. Tipul enumerativ Se utilizeaz cnd o variabil trebuie s ia un numr redus de valori, de natur nenumeric. Sintaxa conine cuvntul cheie enum : enum <identificator > {lista de valori} ;

unde identificatorul este numele tipului de date . Exemple: enum culoare {ALB, ROSU, GALBEN }; enum culoare fanion_1, fanion_2; A doua declaraie definete dou variabile de tip "culoare", fanion_1 i fanion_2. Se pot nlocui cele dou declaraii, prin una singur: enum culoare {ALB, ROSU, GALBEN } fanion_1, fanion_2 ; Fiecare constant din list va primi o valoare ntreag dat de poziia sa n list, 0, 1, 2, . . . , n. n exemplul de mai sus, ALB=0, ROSU=1, GALBEN=2 . Tipul enumerativ nu este, de fapt, un tip nou; este o nou alocare de nume pentru tipul ntreg. Este permis asocierea constantelor simbolice cu valori ntregi, altele dect n asocierea implicit: enum cod_err {ER1=-2, ER2, ER3=3, ER4, ER5} ; Asocierea de valori se va face astfel: ER1=-2, ER2=-1, ER3=3, ER4=4, ER5=5. Se observ c pentru constantele neasociate cu valori la declarare, se dau valori succesive n ordine cresctoare. Tipul enumerativ produce economie de memorie, pentru c se utilizeaz un singur octet, pentru o valoare din list.

2.2. Operatori i expresii

Un operator este o comand ce acioneaz asupra unor obiecte (termeni) ce se numesc generic operanzi; fiecare operator este definit printr-un simbol special sau grup de simboluri. Operatorul determin executarea unei anumite prelucrri (operaii) asupra operanzilor. O formul n care exist operanzi i operatori este numit expresie . Un operand poate fi: constant, variabil simpl, nume de funcie, nume de tablou, nume de structur, expresie nchis ntre paranteze rotunde. Orice operand are un tip i o valoare concret n momentul n care se efectueaz operaia. Operatorii se pot clasifica, dup numrul de operanzi pe care i combin, n: { unari (acioneaz asupra unui singur operand); { binari ( acioneaz asupra a doi operanzi); { ternar (acioneaz asupra a trei operanzi) - exist doar unul n C. Dup tipul operaiei ce se execut, operatorii sunt de mai multe categorii: aritmetici, logici, relaionali, condiionali, de atribuire. La evaluarea unei expresii, se ine seama de categoria de prioritate a fiecrui operator, de asociativitatea operatorilor de aceeai prioritate i de regula conversiilor implicite. n tabelul urmtor sunt prezentai sintetic operatorii din limbajul C - standard, cu principalele lor caracteristici.
Niv. 1 Selectori 2 Categorie Operatori ( ) [ ] Semnificaie Expresie, apel de funcie Expresie cu indici Selectori la structuri Negare logic Negare bit cu bit Plus, minus (semne aritmetice) Incrementare/ Decrementare Luarea adresei Indirectare Dimensiune operand (n octei) Conversie explicit de tip nmulire, mprire, rest Adunare, scdere Deplasare stnga, dreapta mai mic/egal, mai mare/egal Egal logic, diferit de Asociere stnga dreapta dreapta stnga

-> ! ~
Op. unari

+ ++ &

--

* sizeof (tip) 3 4 5 6 7
Op. aritm._1 Op. aritm._2

st - dreapta st - dreapta st - dreapta st - dreapta st - dreapta

Deplasri Relaionali Egalitate

<< >> < <= > >= == !=

8 9 10 11 12 13 14 Op. de atribuire 15 Virgula


Op. condiional

& Operatori logici ^ | && || ? :

i logic bit cu bit Sau exclusiv bit cu bit Sau logic bit cu bit i logic (ntre expresii) Sau logic (ntre expresii) (expresie)? expr_1 : expr_2

st - dreapta st - dreapta st - dreapta st - dreapta st - dreapta dreapta-st.

= *= /= %= += -= &= ^= |= <<= >>= ,

Atribuire simpl dreapta Atribuire produs, ct, rest, sum, dif. stnga Atribuire i, sau excl., sau bit cu bit Atribuire i deplasare stg., dreapta
Evalueaz op1, op2. Valoarea expr. st - dreapta este op2.

Nivelul de prioritate arat cum se evalueaz expresiile care conin doi sau mai muli operatori, cnd nu exist paranteze care s impun o anumit ordine a operaiilor. Operatorii de nivel 1 au prioritate maxim; n tabel operatorii sunt prezentai n ordinea descresctoare a prioritii. Operatorii cu acelai nivel de prioritate se execut conform regulii de asociere: stnga - dreapta sau dreapta - stnga. De exemplu, n expresia:
x= *p++ ;

apar doi operatori: cel de indirectare (derefereniere) i cel de post incrementare; se pune ntrebarea: ce se incrementeaz, p sau *p ?, adic ce operator acioneaz primul ?. Din tabel, se vede c att ++ ct i * au acelai nivel de prioritate, dar ordinea de asociere este dreapta-stnga, aadar, ++ i apoi *. Se incrementeaz aadar p nu *p. Dac dorim s incrementm *p, trebuie scris:
x=(*p )++;

Asociativitatea operatorilor arat cum se evalueaz expresiile, n care, apare acelai operator de mai multe ori. De exemplu, n expresia:
c=x*y *z ;

apar dou nmuliri. Expresia se poate evalua ca (x*y)*z sau x*(y*z). Operatorul * are asociativitatea de la stnga la dreapta, adic expresia se interpreteaz
c=(x* y)*z ;

Operatorul de atribuire are asociativitatea de la dreapta la stnga, deci expresia:


a=b=c =d;

se evalueaz ca i cum ar fi scris:


c=d; b=c; a=b;

Asociativitatea poate fi modificat prin paranteze rotunde, ordinea de evaluare fiind "mai nti parantezele interioare ". Operatorii unari (nivelul 2), cel condiional (nivelul 13) i cei de atribuire (nivelul 14) se asociaz de la dreapta la stnga. Toi ceilali operatori se asociaz de la stnga la dreapta. 2.2.1. Operatori aritmetici (+, -, *, /, %) { Se pot aplica numai la tipurile ntregi (char, short, int, long). { Operatorii + i - pot fi i unari, adic se poate scrie x=+2; y=-7; { Asociativitatea este de la dreapta la stnga pentru cei unari i de la stnga la dreapta pentru cei binari. { Dac operanzii au tipuri diferite, se aplic conversii implicite de tip, unul din operanzi fiind convertit la tipul celuilalt, rezultatul fiind de tipul comun, dup conversie. 2.2.2. Operatori relaionali i de egalitate (< , <=, >, >=, ==, !=) Toi sunt binari, iar expresiile formate se evalueaz la 0 sau la 1, corespunztor valorilor logice fals i adevrat. n limbajul C nu exist constante logice de tip TR UE i FAL SE , ntregii 1 i 0 fiind asociai cu cele dou valori ( 1 = TR UE , 0 =F AL SE ). Mai general, orice valoare numeric diferit de 0 este interpretat logic ca "adevrat". Operatorul == produce valoarea 1 cnd cei doi operanzi sunt egali i 0 cnd sunt diferii iar operatorul != , invers. O eroare frecvent este confuzia dintre = (care este atribuire, adic se transfer valoarea din dreapta, ctre variabila din stnga) i == , adic testarea egalitii: x==y nseamn este x egal cu y ? (Da? sau Nu?). Adesea, eroarea nu este semnalat de compilator, de exemplu, dac n loc de :
if (x==2) . . .

se scrie
if (x=2) . . .

se atribuie lui x valoarea 2, care fiind diferit de 0 se interpreteaz ca fiind "adevrat" i se execut istruciunea din ramura if, indiferent de valoarea iniial a lui x (care se pierde). Astfel de erori, care nu pot fi detectate de compilator sunt greu de remediat. Asociativitatea este de la dreapta la stnga, iar prioritatea operatorilor == i != este mai slab dect a celorlali operatori relaionali. O expresie de forma a <b = = x >y este echivalent cu ( a<b) = =
(x>y) .

2.2.3. Operatori logici (&& || !) Operanzii sunt evaluai cu adevrat (diferit de 0) sau fals (egal cu 0) iar valoarea expresiei este totdeauna 0 sau 1. Operatorul ! (negaie logic) transform o valoare 0 n 1 i o valoare diferit de 0 (adevrat ) n 0 (fals ). Astfel, !0=1, !1=0, !7=0, !!7=1, !(2>3)=1. La operatorii && (i logic) i || (sau logic), se garanteaz ordinea de evaluare a operanzilor dup urmtoarele reguli: { la &&, se evalueaz nti primul operand; dac acesta este diferit de 0, se evalueaz i al doilea operand iar valoarea expresiei este dat de al doilea; dac primul operand este 0, nu se mai evalueaz al doilea, deoarece orice valoare ar avea, n final se obine 0. { la || , se evalueaz, de asemenea, primul operand; dac valoarea logic este 1 (adevrat) nu se mai evalueaz al doilea, deoarece orice valoare ar avea, n final se obine 1 (adevrat). Acest mod de evaluare este eficient, deoarece se reduce la minimum posibil numrul de operaii, ceea ce nseamn economie de timp. Prioritatea operatorului unar ! este mai mare dect a tuturor operatorilor aritmetici, logici i relaionali iar a operatorilor && i || este inferioar operatorilor relaionali, adic o expresie de forma:
a>b&& c==d && e! =f

se evalueaz ca i cum ar fi scris:


(a>b) &&(c == d) && (e != f)

Prioritatea operatorului || (sau) este mai sczut dect a lui && (i) iar asociativitatea este de la stnga la dreapta. 2.2.4. Opratori de incrementare / decrementare (++ --)

Au ca efect creterea valorii cu 1 (incrementare) sau scderea valorii cu 1 (decrementare). Fiecare operator are dou forme: forma prefixat, adic ++x, --y, sau forma postfixat x++ , y--. Valoarea expresiei x++ este valoarea lui x nainte de incrementare iar valoarea expresiei ++x este valoarea lui x dup incrementare. Aceti operatori nu se aplic unor expresii aritmetice i nici constantelor. Exemple:
n=7; m=n++ ; //se atrib uie lui m valoarea 7 n=7; m=++n ; //se atrib uie lui m valoarea 8

ambele variante modific n final valoarea lui n n n+1. Operatorii de incrementare / decrementare contribuie la scrierea unor programe mai eficiente i mai scurte. 2.2.5. Operatorul unar sizeof Returneaz dimensiunea n octei (numrul de octei ocupai) a unei variabile, a unui tip de date, sau a unui tip structurat. De exemplu, siz eo f( in t) d numrul de octei pe care este memorat un ntreg iar dac tab este un tablou, expresia:
sizeo f(ta b) / sizeo f( ta b[ 0] )

d numrul de elemente ale tabloului, oricare ar fi tipul de date, pentru c se mparte numrul total de octei ocupat de tablou la numrul de octei ocupai de un element. n diverse implementri mai vechi, sizeof ntorcea tipuri diferite de ntregi, de exemplu int, unsigned int sau unsigned long. Standardul ANSI introduce un tip predefinit, numit size_t, garantnd c sizeof ntoarce acest tip de ntreg. Astfel, programele care folosesc acest tip, capt o portabilitate sporit. Tipul size_t este definit n fiierul header stddef.h , ca i n stdio.h i n alte fiiere header uzuale. 2.2.6. Operatori de atribuire (= *= += /= %= -= &= ^= |= <<= >>=) n limbajul C, pe lng operatorul de atribuire normal = (se atribuie valoarea expresiei din dreapta, variabilei din partea stng a semnului =), exist i operatori de atribuire combinat cu o operaie, pentru a face textul mai concis, dat fiind c foarte frecvent atribuirea este nsoit de o operaie. Astfel, avem scrieri echivalente:
x=x+3 ; y=y*6 .5; u=u/( v-2) ; a=a % b ; echival en t echival en t echival en t echival en t cu cu cu cu x y u a += *= /= %= 3; 6.5; v-2; b;

Tipul expresiilor formate cu operatorii de atribuire este tipul expresiei din stnga. Valoarea unei expresii de atribuire de forma a=b este valoarea membrului drept, dup o eventual conversie la tipul membrului stng. Asociativitatea este de la dreapta la stnga; de exemplu, n atribirea multipl de mai jos:
x = y = z = 0 ;

ordinea operaiilor este:


x = (y = (z = 0));

faptul c z = 0 este o expresie cu valoarea 0 (a membrului drept), face posibil atribuirea urmtoare y = 0 i apoi x = 0; aadar, n final toate cele trei variabile primesc valoarea 0. Evaluarea expresiilor cu operatori compui de atribuire se face pe baza definiiei echivalente a acestora. De exemplu, expresia:
a = b += c + 1;

se evalueaz ( de la dreapta la stnga) ca:


a = (b = b + (c+1)) ;

2.2.7. Operatorul condiional (? :) n situaia n care unei variabile trebuie s i se atribuie o valoare sau alta n funcie de o condiie logic, se poate folosi instruciunea if dar n C exist, ca variant, operatorul condiional (? :), cu sintaxa:
(expr ) ? expr_ 1: expr_ 2;

unde (expr) este evaluat logic; dac este adevrat (diferit de 0), se evalueaz expr_1 care devine valoare final pentru expresia condiional; dac (expr) este fals (egal cu 0), atunci este evaluat expr_2, care devine valoare final. Se garanteaz c una i numai una dintre expr_1 i expr_2 este evaluat. Exemplu: tiprirea unui vector numeric, cu un anumit numr de elemente pe linie (de exemplu, 10), separate prin blank:
for(i =0 ; i<n; i++) print f("% 6d %c ", a[ i] ,( i% 10 == 9 || i==n-1 ) ? '\n': ' ');

Se tiprete ntregul a[i] i apoi un singur caracter (cu formatul %c), acesta fiind '\n' sau ' ' dup cum s-a tiprit tot al 10-lea ntreg sau ultimul. Orice alt variant de program ar fi dus la mai multe instruciuni. 2.2.8. Operatorul virgul ( , ) O expresie n care apare operatorul virgul are forma:
expr_ 1, expr_ 2

Evaluarea se face n ordinea (garantat) expr_1 i apoi expr_2. Valoarea final este dat de expr_2. Se aplic regulile privind conversia de tip (dac este cazul). O asemenea construcie este util acolo unde sintaxa impune o singur expresie. Prin folosirea operatorului virgul, putem realiza mai multe aciuni.

2.3. Tipuri structurate: tablouri i structuri


2.3.1. Tablouri Un tablou este o colecie de date de acelai tip - numit tip de baz - la care accesul se realizeaz prin intermediul indicilor. Elementele unui tablou ocup, n memorie, locaii succesive iar ordinea n care sunt memorate elementele, este dat de indici. O caracteristic important a unui tablou este dimensiunea, adic numrul de indici. Astfel, exist tablouri: { unidimensionale - elementele au un singur indice a[0], a[1], . . .; { bidimensionale - elementele au doi indici b[0][0], b[0][1], . . . ; { multidimensionale - elementele au mai muli indici. Declaraia de tablou, n forma cea mai simpl, conine tipul comun al elementelor sale, numele tabloului i limitele superioare pentru fiecare indice (valorile maxime ale fiecrui indice):
<tip> <nume> [dim_ 1] [d im _2 ]. . . [dim_n ];

{ tip este cuvnt cheie pentru tipurile predefinite; { nume este un identificator pentru tablou; { di m_ k este limita superioar pentru indicele k ; acesta va lua valorile 0, 1, 2, . . . k-1, n total k valori.

Limitele d im _k sunt expresii constante, adic expresii care pot fi evaluate la compilare. Numele unui tablou este un simbol care are ca valoare adresa primului element din tablou. La ntlnirea unei declaraii de tablou, compilatorul aloc o zon de memorie pentru pstrarea valorilor elementelor sale. Numele tabloului respectiv poate fi utilizat n diferite expresii, n program, valoarea lui fiind chiar adresa de nceput a zonei de memorie care i-a fost alocat. Exemple:
int a[20]; char sir[8 0] ; float x[10][5 0] ;

Declaraiile de mai sus arat c a este un tablou de 20 de variabile de tip ntreg, sir este un tablou de 80 de caractere iar x este un tablou bidimensional care conine 10 x 50 = 500 elemente de tip real. Elementele tabloului pot fi accesate prin numele tabloului urmat de o expresie ntreag pus ntre paranteze drepte. Expresia trebuie s ia valori n domeniul 0, . . . , dim-1. De exemplu, referiri corecte la elementele tabloului a de mai sus sunt: a [0 ], a[ 1], . . . , a[ 19 ] iar la elementele tabloului x sunt: x[0] [0 ], x [0 ][ 1] ,. .. ,
x[9][ 0] , x[9][1], .. ., x[9][ 49].

Prelucrrile de tablori se implementeaz de obicei prin cicluri for, deoarece numrul de repetri este cunoscut, fiind dependent de valorile indicilor. Tablourile pot fi declarate n interiorul funciilor, caz n care sunt implicit n clasa auto, sau n exteriorul funciilor, caz n care sunt implicit n clasa extern. Tablourile nu pot fi declarate n clasa register . Tablourile pot fi iniializate cu valori. Iniializarea respect regulile care deriv din clasa de alocare: un tablou n clasa auto (iniializat explicit) va fi iniializat la fiecare intrare n funcia respectiv iar un tablou n clasa static sau extern (iniializat explicit) se iniializeaz o singur dat, la ncrcarea programului n memorie. Tablorile n clasa static sau extern, care nu sunt iniializate explicit, se iniializeaz automat cu 0 iar cele n clasa auto vor primi valori aleatoare. Iniializarea se face cu semnul egal i o list de valori:
int a[4]={ 2, 4, 6, 8}; char s[4]= {' x' , 'y', 'z', '\0'};

Dac lista de valori conine mai puine elemente dect dimensiunea declarat a tabloului, celelalte valori se iniializeaz cu 0, indiferent de clasa de alocare. De exemplu, dup declaraia:
int a[7]={ 1, 5, 11, -3};

elementele a[4], a[5], a[6] vor conine valoarea 0. n cazul unui tablou iniializat, se poate omite dimensiunea, ea fiind calculat de compilator, dup numrul de elemente din list. De exemplu, declaraia :
float x[ ]={1.0 , 2.1, 3.2};

arat c x este un tablou cu trei elemente reale, specificate n list. Un caz special l reprezint tablourile de caractere, care pot fi iniializate i cu iruri constante de caractere. Astfel, declaraia:
char s[ ]="ab cd ef " ;

este perfect echivalent cu


char s[ ]={'a ', 'b ','c ', 'd ', 'e ', 'f', '\ 0' };

observnd prezena caracterului special '\0' (octetul nul, care are rol de terminator de ir). Transmiterea tablourilor cu o dimensiune la funcii se face prin declararea parametrului formal al funciei ca fiind tablou, ceea ce se realizeaz punnd paranteze drepte. Nu este necesar prezena explicit a dimensiunii tabloului ntre paranteze, deoarece ceea ce se transmite efectiv este adresa de nceput a tabloului. Dac totui, este necesar dimensiunea tabloului, aceasta se poate preciza ntr-un parametru separat. n exemplul de mai jos, este prezentat o funcie care calculeaz produsul scalar a doi vectori de lungime n, reprezentai prin dou tablouri de numere reale:
float prod_s ca la r( floa t x[], float y[], int n) { float prod=0.0 ; int i; for (i=0; i<n; i++) prod +=x[i ]* y[i] ; retur n prod; }

Dac sunt declarate dou asemenea tablouri, de 3 elemente:


float a[3]= {- 1. 0, 2.0, 3.14}; float b[3]= {0 .9 , -1.02, 0.0}; float ab;

atunci produsul scalar al vectorilor a i b se poate obine prin:


ab=pr od_s ca la r( a, b, 3);

Tablourile de caractere se prelucreaz diferit, deoarece ele nu au de obicei specificat dimensiunea; aceasta se deduce din prezena terminatorului '\0' care este ultimul caracter din ir. O funcie care calculeaz numrul de caractere dintr-un asemenea tablou este:
int nr_car (c ha r s[]) { int i; for (i=0; s[i] != '\0'; i++); retur n i; }

Se observ c terminatorul '\0' nu se consider caracter util, deci nu este inclus n rezultat. Funcia de mai sus este echivalent cu funcia de bibliotec strlen() . Pentru tablouri uni- sau multi-dimensionale, alocarea memoriei se face dup reguli stricte, cuprinse n funcia de alocare a memoriei . Modul de alocare a memoriei se poate ine minte prin regula sintetic: "tabloul este alocat la adrese succesive de memorie, ultimul indice avnd variaia cea mai rapid". De exemplu, considernd declaraia: char c[2][2][3] imaginea n memorie a elementelor tabloului c[] va fi:
c[0][ 0][0 ] c[0][ 0][1 ] c[0][ 0][2 ] c[0][ 1][0 ] c[0][ 1][1 ] c[0][ 1][2 ] c[1][ 0][0 ] c[1][ 0][1 ] c[1][ 0][2 ] c[1][ 1][0 ] c[1][ 1][1 ] c[1][ 1][2 ]

Adresa lui c[1][0][1] se calculeaz astfel: adr(c[1][0][1]) = baza(c) + 1*2*3 + 0*3 + 1 = 7, elementul c[1][0][1] gsindu-se ntr-adevr la 7 octei distan de c[0][0][0]. Adesa se calculeaz astfel: baza(tab) + nr.octei/element * [ indice_1 * dim_2 * dim_3 + indice_2 * dim_3 + indice_3 ]. n cazul tablourilor cu dou dimensiuni, regula de alocare este dat de: "matricele se memoreaz pe linii"; ntr-adevr, un tablou x[2][3] va fi memorat astfel:
x[0][ 0]

x[0][ 1] x[0][ 2] x[1][ 0] x[1][ 1] x[1][ 2]

n limbajul C, un tablou cu mai multe dimensiuni este tratat ca un tablou cu o dimensiune (i anume, prima), care are ca elemente tablouri cu restul de dimensiuni; astfel, nu exist limitri sintactice ale numrului de dimensiuni. De exemplu tabloul:
int a[2][3 ];

este interpretat ca un tablou uni-dimensional cu 2 elemente, fiecare fiind tablou 1-dimensional cu 3 elemente: a[0] este tablou cu 3 elemente, a[1] este tablou cu 3 elemente. Transmiterea unui tablou cu mai multe dimensiuni unei funcii n toate situaiile, n care lucrm cu tablouri cu mai multe dimensiuni, trebuie s inem seama de funcia de alocare a tabloului i s stabilim dac la compilare se calculeaz corect adresa elementelor. Este nevoie de toate dimensiunile, mai puin prima. S considerm o funcie care tiprete elementele unui tablou. Ceea ce se transmite funciei este adresa de nceput dar sunt necesare i informaiile pentru calculul adresei, adic dimensiunea a doua. Definim tipul de date DATA i formatul corespunztor de tiprire:
typed ef int DATA; #defi ne FORMA T "%7d"

Este incorect forma:


void matpr in t( DA TA a[][] , int m, int n) { int i, j; for (i=0; i<m; i++) { for (j=0; j<n; j++) printf( FO RM AT , a[i][ j] ); print f( "\ n" ); } }

deoarece nu se poate calcula adresa lui a[i][j]. n momentul compilrii, nu este cunoscut a doua dimensiune a lui a, deci apare eroare, chiar la compilare. Pentru tiprirea unei matrice declarate cu:
DATA x[3][4 ];

prima linie din definiia funciei trebuie s fie:


void matpr in t( DA TA a[][4 ], int m, int n )

iar apelul va fi:

matpr int( x, 3, 4);

Acum compilatorul "tie" s calculeze adresa lui a[i][j], dar funcia astfel construit poate fi folosit numai cu matrice cu 4 coloane. O soluie mai eficient este folosirea unei forme echivalente pentru a[i][j], anume aceea care simuleaz funcia de alocare memorie, pe baza celei de-a doua dimensiuni, care este n.
void matpr in t( DA TA (*a)[ ], int m, int n) { int i, j; for (i=0; i<m; i++) { for (j=0; j<n; j++) print f( FO RM AT, ((DATA *)a)[i* n+ j] ; print f( "\ n"); } }

Cea ce se transmite este adresa de nceput a tabloului a, vzut ca pointer la un tablou cu o dimensiune de tipul DATA (nu mai apar erori la compilare). Conversia explicit de tip ((DATA *)a) face ca a s fie vzut ca un pointer la DATA, deci ca un tablou uni-dimensional. Indicele i*n+j face ca s fie accesat corect elementul a[i][j], indiferent de dimensiuni. n concluzie, se poate omite numai prima dimensiune a tabloului, celelalte trebuie cunoscute, pentru a se cunoate modul de organizare; dimensiunea omis se poate transmite ca parametru separat, ea trebuie cunoscut la execuia funciei; programul urmtor calculeaz suma elementelor unei matrice.
#incl ude <stdio .h > #incl ude <conio .h > #defi ne MAX 3 int suma(i nt tab[][MA X] , int r); void afis( in t tab[] [M AX ], in t r); void main() { int mat[3] [M AX ]= {{ 1,2, 3} ,{ 4, 5, 6} ,{7, 8, 9} }; //ini tial iz ar e matrice int rind=3 ; afis( mat, ri nd ); print f("\ n Suma este %d",s um a( ma t,ri nd )) ; getch (); } int suma(i nt tab[][MA X] ,i nt r) { int i, j, s=0;

for(i =0;i <r ;i ++ ) for(j= 0; j< MA X; j++) s+=tab [i ][ j] ; retur n s; } void afis( in t tab[] [M AX ], int r) { int i,j; print f("\ n" ); for(i =0;i <r ;i ++ ) { for(j =0 ;j <M AX ;j++ ) print f( " %2d", ta b[ i] [j]) ; print f( "\ n" ); } }

2.3.2. Structuri n multe aplicaii, este necesar gruparea datelor sub forma unei mulimi de elemente care s poat fi prelucrate att element cu element ct i global, ca mulime. Dac toate elementele sunt de acelai tip, mulimea de elemente poate fi definit ca tablou; dac elementele sunt diferite ca tip, atunci mulimea lor se definete ca structur . Structura este o colecie de date de tipuri diferite, grupate sub acelai nume. Un exemplu simplu de structur este data calendaristic; ea conine 3 componente: zi (1, 2, . . . , 31) - de tip ntreg, luna - de tip ir de caractere i anul - de tip ntreg. Pentru a utiliza structuri n programe, acestea trebuie definite. Sintaxa definirii unei structuri este:
struc t <nume >{ declara ti i de varia bi le ; } unde <n ume> este un identificator de tip de date definite de utilizator, str uc t este cuvnt cheie iar declaraiile de variabile sunt cele

obinuite. Dup o asemenea definiie, se pot declara variabile sau funcii de tipul <nume> . Exemple:
struc t compl ex {f lo at re; float im}; struc t stude nt {c ha r nume[2 5] ;i nt gr;fl oa t medie _a n} ;

Aceste declaraii definesc numai structurile, fr a aloca memorie, deoarece nu au fost nc declarate variabile - structur. Pentru a defini obiecte concrete de tip structur, cu rezervare de memorie, se scrie:
struc t compl ex z1, z2, zeta; struc t stude nt std[99] ;

Variabilele z 1, z 2, ze ta sunt de tip "complex", deci fiecare va avea dou componente reale, re i i m . Cele 100 de variabile s td [k ] sunt de tip "student", fiecare cu 3 componente: n um e, gr ,
medie _a n.

Definirea structurii i declararea variabilelor de tip structur pot fi combinate ca mai jos:
struc t compl ex {f lo at re; float im} z1, z2, zeta;

situaie n care numele structurii ("complex") ar putea lipsi, dac definirea conine i declaraii de variabile:
struc t {floa t re; float im} z1, z2, zeta;

dar acest mod de lucru nu se recomand, mai ales cnd se lucreaz cu mai multe tipuri de structuri. Este recomandabil ca structurile s aib asociate nume pentru identificarea tipurilor lor i s se foloseasc declaraia typedef pentru a lucra mai comod:
struc t compl ex {f lo at re; float im}; typed ef struc t comple x COMPLE X; COMPL EX z1, z2, zeta;

sau prin combinarea lui typedef cu definiia structurii:


typed ef struc t {float re; float im} COMPLE X; COMPL EX z1, z2, zeta;

n concluzie, o structur asociaz diverse tipuri de variabile, simple sau structurate, sub un singur tip de date. ntregul grup poate fi, apoi, tratat global (prin atribuiri, comparri, transmitere la funcii etc.). Iniializarea structurilor. Se poate face la declarare, prin precizarea unor valori pentru variabilele componente, de exemplu:
COMPL EX zeta = {1.0, -2.33 }; struc t stude nt sx = {"Tana se Ion", 8310, 9.33};

sau, dac structura conine date mai complexe (tablouri, alte structuri), se pot folosi mai multe niveluri de acolade:
struc t tty {int a[10]; int b[3]; char *s}; struc t tty x = {

{2, 2, 3, 3, 10, 20}, {22, 44, 66}, "Cheia de cripta re, codata " };

Se iniializeaz primele 6 elemente ale tabloului a (celelalte se vor iniializa automat cu 0), toate cele 3 elemente ale tabloului b i irul de caractere s. Accesul la componentele unei structuri. Se face prin operatorul "." aplicat structurii, conform sintaxei:
<nume variab il a> .< nume compo ne nt a>

Exemple:
COMPL EX z, zeta; struc t stude nt sx; z.re= 2.0; z.im= 7.2; zeta= z;// ec hi va le nt cu zeta.r e= z.re ; zeta.i m= z.im ; sx.nu me=" Ni cu le sc u D. R. Liviu" ; sx.gr = 8315; sx.me die_ an = 8.99;

Este posibil ca o structur s conin o alt structur drept membru; n exemplul urmtor, se definete structura PE RSOA NA , care are o component a dr (adresa persoanei), aceasta fiind la rndul su o structur cu trei componente: oras, strada , cod .

typed ef struc t

typed ef struc t PERSO AN A; PERSO ANA zz; zz.nu me=" Gh eo rg he Hagi"; zz.ad r.or as =" Co ns ta nt a" ; zz.ad r.st ra da =" B- du l Tomis 44"; zz.ad r.co d= 55 67 ;

{ char char long { char

*oras ; *stra da ; cod; } ADRES A; *nume; ADRESA adr;}

Dac elementele sunt tipuri structurate (tablouri, iruri etc.), accesul se face n mod natural: x .num e este un pointer la caracter iar x.num e[ i] reprezint elementul i al irului x.num e .

Pointeri la structuri i accesul prin pointeri. Un pointer ctre o structur se declar similar cu pointerii ctre variabile simple:
COMPL EX z, *pz;

unde *pz este un pointer ctre o structur de tipul complex; se poate face acum atribuirea:
pz=&z ;

prin care se d lui pz adresa structurii z. n operaiile cu pointeri la structuri (comparaii, atribuiri), acetia trebuie s fie definii ctre acelai tip de structur (acelai nume de structur). Chiar dac dou structuri sunt identice ca numr i tip de componente dar au nume diferite, pointerii corespunztori nu sunt compatibili. Exemplu:
struc t tip_a {int x; float y} s1, *p1; struc t tip_b {int x; float y} s2, *p2; p1=&s 2; // eroare , adrese le sunt incomp at ib ile

Dac p este un pointer ctre o structur, atunci *p este structura iar (*p).nume este un membru al structurii. S considerm din nou structura student, anterior definit, variabila sx de tip student i un pointer ps ctre o structur student, iniializat cu adresa lu sx; se pot face atribuirile de mai jos:
struc t stude nt sx, *ps=& sx ; (*ps) .num e= "I on es cu Octav ia n" ; (*ps) .gr= 44 2; (*ps) .med ie _a n= 9.73;

Limbajul C permite o form echivalent pentru aceste construcii: dac p este un pointer ctre o structur iar ms este un membru al acestei structuri, atunci ( *p). ms se poate scrie echivalent p-> ms , unde simbolul - > este format din caracterul - (minus) urmat de caracterul > (mai mare). Cele trei atribuiri de mai sus, se pot scrie, aadar:
ps->n ume= "I on es cu Octavi an "; ps->g r=44 2; ps->m edie _a n= 9.73;

Se pot folosi operatorii ++ / -- pentru p sau ms:

(++p) ->ms ; //incr emen te az a p si apoi acces ea za pe ms (p++) ->ms ; //acce seaz a ms si apoi incre me nt eaza p (p->m s)++ ; // incre me nt ea za (p->ms), dupa acces ++(p- >ms) ; // incre me nt ea za (p->ms), in ai nt e de acces

Toate regulile referitoare la pointeri ctre variabile simple rmn valabile i la pointeri ctre structuri.

3 Instruciuni

Instruciuni simple { de atribuire { break { continue { goto { return

Instruciuni structurate { instr. compus { alternative { repetitive { { . . . } { if { switch { while { do while { for

Descrierea unui proces de calcul se realizeaz prin instruciuni. Instruciunile simple sunt auxiliare, fiind utilizate n componena celor structurate. Orice proces, poate fi descris prin trei structuri de control fundamentale: secvenial, alternativ i repetitiv. Structura secvenial elementar este reprezentat de instruciunea compus.

3.1. Instruciunea compus


Instruciunea compus este o succesiune de instruciuni incluse ntre paranteze acolade, care poate fi precedat de declaraii. Dac sunt prezente declaraii, atunci variabilele definite sunt valabile doar n timpul execuiei instruciunii compuse. Instruciunea compus este tratat sintactic ca o singur instruciune; de aceea, se utilizeaz cnd sintaxa permite existena unei singure instruciuni: n structura if, for etc. Exemplu: schimbarea reciproc a valorilor ntre dou variabile, a, b: // instructiune compusa { int t ; // variabila ajutatoare t=a ; a=b ; b=t ; } Dup execuia secvenei de instruciuni, variabila t nu mai este stocat n memorie i deci devine inexistent.

Partea executabil a unei unei funcii (corpul funciei) este de fapt o instruciune compus.

3.2. Instruciunea if
Corespunde structurii alternative cu dou ramuri, sau unui bloc de decizie cu dou ieiri. Sintaxa instruciunii if este: if (expresie) instructiune_1 ; [else instructiune_2 ; ] Ramura else, inclus ntre [ ], este opional; instructiune_1 i instructiune_2 pot fi oricare instruciuni ale limbajului, simple sau structurate, inclusiv if. Dac este necesar, una sau ambele pot fi nlocuite cu instruciuni compuse. Efectul : se evalueaz expresia (se calculeaz valoarea expresiei) ; { { dac este adevrat (valoare diferit de zero), se execut numai instructiune_1 (instructiune_2 nu este luat n consideraie); { dac este fals (valoare = 0), se execut numai instruciune_2; dac aceasta lipsete, se trece la instruciunea urmtoare din program. Observaii: { n C, valoarea logic "adevrat" este asociat cu " ! 0" iar valoarea logic "fals" este asociat cu "= 0". Valoarea expresiei poate fi de tip ntreg: if (k) x=k+3 ; // pentru k<>0, se executa x=k+3 if (k=5) x=k+3 ; // se executa totdeauna x=k+3, pentru ca expr.=5 if (k==5) x=k+5 ; // se executa x=k+3, numai pentru k=5 { Dac una sau ambele instruciuni din if, sunt tot instruciuni if, else se asociaz cu cea mai apropiat instruciune if anterioar, din acelai bloc, care nu are ramur else . Exemplu: if (y==1) if (a==2) x=0; else x=2; Ramura else aparine lui if (a==2), cea mai apropiat instruciune if anterioar, care nu are ramur else. Dac se dorete alt apartene, se pote utiliza o instruciune compus:

if (y==1) { if (a==2) x=0; } else x=2; // else apartine lui if (y==1) 3x 2+1 1 1. Se consider funcia f : R d R, f(x ) = x2+1 , x ! 0 . 3/2 , x = 0 S se afieze valoarea funciei, pentru o valoare a lui x introdus de la tastatur. #include <stdio.h> #include <math.h> int main(void) { double x, f ; printf("Introduceti valoarea lui x: "); scanf("% lf ", &x); //t otdeauna, argumentul lui scanf este adresa:
&x

if (x !=0) f=(sqrt(3*x*x+1)-1)/(x*x+1); // instr_1 else f=3.0/2.0 ; // instr_2 printf("Valoarea functiei este: %lf \n", f); return 0; } 2. Se consider trei valori ntregi; s se determine cea mai mare. Se utilizeaz instruciunea if pe mai multe niveluri.
a>b b>c return c return b return c a>c return a

if (a>b) if (a>c) return a ; // intoarce ca rezultat valoarea a else return c ; else if (b>c) return b ; else return c ;

Se observ alinierea fiecrui else cu if -ul cruia i aparine.

3.3. Instruciunea switch


Permite realizarea structurii selective cu mai multe ramuri. Este o generalizare a structurii alternative de tip if i ca urmare poate fi realizat cu mai multe instruciuni if incluse unele n altele, pe mai multe niveluri. Utilizarea instruciunii switch, face ca programul s fie mai clar dect dac se utilizeaz if pe mai multe niveluri. Sintaxa instruciunii switch: switch (expresie) { case const_1: instructiune_1; instructiune_2; . . . . . . . . . . ; break ; case const_2: instructiune_1; instructiune_2; . . . . . . . . . . ; break ; . . . . . . . . . . . . . . . . . . . . . . . . case const_n: instructiune_1; instructiune_2; . . . . . . . . . . ; break ; [ default: instructiune_1; // ramura optionala instructiune_2; . . . . . . . . . . ; ] } Efectul instruciunii switch este descris mai jos: Se evalueaz expresie (de tip ntreg) i se compar cu valorile { constante (care trebuie s fie distincte), din lista de etichete case; { Dac valoarea expresiei coincide cu una din constantele const_k, se execut numai secvena de instruciuni corespunztoare acestei etichete, pn la break , goto sau return i se trece la instruciunea urmtoare din program, aflat dup paranteza

acolad }; n cazul n care secvenele de instruciuni nu conin break, goto sau return, se execut secvena cazului, precum i toate secvenele care urmeaz pn la sfritul lui switch ; { Dac valoarea expresiei nu coincide cu nici una din constantele notate const_1, const_2, . . . , const_n, se execut succesiunea de instruciuni aflat dup default (dac exist) i se trece la instruciunea urmtoare din program, aflat dup paranteza acolad }. { Un caz (case) poate conine una, mai multe sau nici o instruciune, adic pot exista mai multe cazuri asociate la aceeai secven de instruciuni. Exemple: switch (c=getch( )) { case 'd': case 'D': del_file; break; case 'l': case 'L': list_file; break; default: error( ); break; } Se citete un caracter de la consol (cu getch()) i se execut una din funcile del_file(), list_file() sau error(), dup cum caracterul este 'd' sau 'D', respectiv 'l' sau 'L', respectiv diferit de cele patru caractere. Comanda break asigur ieirea din switch dup execuia unei funcii. Comanda break de la default nu este necesar, dar este o bun msur de protecie, n cazul n care se adaug ulterior alte cazuri, dup default . double calcul (doble op1, char operator, double op2) { switch (operator) { case '+' : return op1+op2; case '-' : return op1-op2; case '*': return op1*op2; case '/': return op1/op2; default: printf("Operator necunoscut\n"); exit(1); }

} Funcia calcul(), de mai sus, se apeleaz cu trei argumente: op1, op2 i un operator (+, -, *, /); ea returneaz rezultatul operaiei, n funcie de operatorul de tip caracter, care a fost specificat n lista de argumente. Funcia exit(1) produce terminarea execuiei programului; dac valoarea specificat ca parametru este zero, arat terminare normal, n caz contrar definete o terminare anormal, deci prezena unei erori. n cazul de mai sus, se produce o terminare forat, din cauza unei erori de operator. Funcia calcul(), n mod normal, returneaz o valoare real de tip double. Din cauza operatorului necunoscut, funcia nu poate returna o valoare normal, fapt ce necesit terminarea forat a programului, altfel eroarea va genera un lan de alte erori, foarte greu de depistat. Funcia exit() este o funcie de bibliotec. Pentru utilizarea n programe trebuie specificate fiierele header <stdlib.h> sau < process.h> .

3.4. Instruciunea while


Corespunde structurii repetitive condiionate anterior. Sintaxa: while (expresie) instruciune ; Efectul instruciunii while : { 1. Se evalueaz expresia; { 2. Dac are valoarea "adevrat" (este diferit de zero), se execut instruciunea i se repet pasul 1. { 3. Dac are valoarea "fals" (este egal cu zero), execuia instruciunii while se ncheie i se trece la instruciunea urmtoare din program. Pe poziia "instruciune" este acceptat o singur instruciune C; dac sunt necesare mai multe, acestea se includ ntre paranteze acolade i formeaz astfel o singur instruciune, compus. Observaii:

{ Dac la prima evaluare, expresie=0, while se ncheie fr s se execute "instruciune". { Dac n partea executabil apar instruciuni ca break, goto sau return, se schimb aciunea normal a lui while, prin ncheierea ei nainte de termen. { Instruciunea while se ncheie dac dup expresie se pune ';' while (k<=100) ; // eroare: corpul lui while este vid k=k+1 ; // se considera "instructiune urmatoare" { Execuia instruciunii while trebuie s produc modificarea valorii "expresie" dup un numr finit de repetri; n caz contrar, se produce un ciclu infinit . Exemplul 1: Tabelarea valorilor unui polinom: # include <stdio.h> main() / *afiseaza valorile lui p(x), pentru x=1, 2, . . .,10
p(x)=x*x - 3*x+2 */

{ int x=1; while (x<=10) { printf("x = %d \t p(x) = %d \n",x, x*x - 3*x +2); x++ ; } } Exemplul 2: Tabelarea valorilor funciei sinus(). Funcia sinus este o funcie standard, se afl n fiierul math.h i are prototipul double sin(double x); Argumentul x este interpretat n radiani; pentru a utiliza n program valori ale lui x n grade, acestea trebuie nmulite cu factorul f=PI/180. # include <stdio.h> # include <math.h> # define PI 3.14159 2653 5897 9 main() { int x=0 ; double f=PI/180. 0 ; while (x<=360) { printf( "sin (%d) = %.16f\n",x ,sin (x*f )); x++ ;

} } Valorile se afieaz cte una pe linie, deci programul afieaz 360 de linii. Pe ecran pot fi analizate doar ultimele 25 de linii. n asemenea situaii se pune problema de a ntrerupe temporar execuia programului, dup ce s-au afiat 25 de linii. Oprirea programului se poate face n mai multe moduri; un mod simplu de oprire este apelarea funciei getch() n momentul n care ecranul este plin. Apelarea funciei getch() oprete execuia, deoarece se ateapt acionarea unei taste i se afieaz fereastra utilizator. La acionarea unei taste, programul intr din nou n execuie. n exemplul urmtor se apeleaz funcia getch() dup afiarea a 23 de valori (linii); pe linia 24 se va afia textul: Daca doriti sa continua ti, apasati o tasta! Se utilizeaz valorile lui x =0, 1, . . . , 360, pentru a apela getch, cnd x ia valorile 23, 46, 69, . . . , deci multiplu de 23. Aceasta se exprim prin expresia x % 23 ==0. # include <stdio.h> # include <math.h> # define PI 3.14159 2653 5897 9 main() { int x=0 ; double f=PI/180. 0 ; while (x<=360) { printf( "sin (%d) = %.16f\n",x ,sin (x*f )); x++ ; if(x % 23==0){ printf( "Dac a doriti sa continua ti, apasati o tasta! \n"); getch() ; } } }

3.5. Instruciunea do - while


Corespunde structurii repetitive condiionate posterior. Efectul ei poate fi obinut cu celelalte instruciuni repetitive. Prezena ei n setul de instruciuni asigur o mai mare flexibilitate i claritate programelor. Sintaxa:

do instructiune while (expresie) ; Efectul instruciunii do - while : 1. Se execut "instruciune"; 2. Se evalueaz expresia; 3. Dac are valoarea "adevrat" (diferit de zero), se reia pasul 1. 4. Dac are valoarea "fals" (zero), execuia instruciunii do-while se ncheie i se trece la instruciunea urmtoare din program. Pe poziia "instruciune" este acceptat o singur instruciune C; dac sunt necesare mai multe, acestea se includ ntre paranteze acolade i formeaz astfel o singur instruciune, compus. Observaii: { { { { { Blocul "instruciune" se execut cel puin o dat, chiar dac la prima evaluare, expresie=0. { Dac n partea executabil apar instruciuni ca break , goto sau return, se schimb aciunea normal a lui do-while, prin ncheierea ei nainte de termen. { Pentru a nu confunda, n textul programului, un while din do-while cu un ciclu while care conine instruciune vid, se recomand folosirea acoladelor chiar i atunci cnd do-while conine o singur instruciune. do { instructiune } while (expresie) ; { Execuia instruciunii do-while trebuie s produc modificarea valorii "expresie" dup un numr finit de repetri; n caz contrar, se produce un ciclu infinit. Exemple: # include <stdio.h> # define PUNCT '.' int main(void) { char car ; int nr_sp=0 ; printf("Introduceti o fraza terminata cu punct\n"); do {

car = getch(); if (car == ' ') nr_sp++; } while (car != PUNCT); printf("S-au gasit %d spatii albe \n", nr_sp); return 0 ; } Programul de mai sus contorizeaz numrul de spaii albe dintr-o fraz, introdus de la tastatur. La citirea caracterului punct ".", ciclul do-while se ncheie i se afieaz numrul de spaii. Analog, se poate contoriza tastarea unui caracter oarecare, prin modificarea condiiei din instruciunea if(car == ' ') nr_sp++; exemplu: if (car == 'd') nr_sp++ ; Exemplul urmtor este un program care calculeaz rdcina ptrat dintr-un numr real pozitiv, prin metoda iterativ, a lui Newton. Se utilizeaz irul (x n ) ncN , definit prin recuren: x 2 +a n x n+1 = 2$xn , x 0 = 1. Acest ir este convergent i are limita L = a . Termenii irului se calculeaz succesiv: x 0 , x 1 , , x N , pn cnd diferena dintre doi termeni consecutivi este mai mic dact o valoare impus, de exemplu,  = 10 10 : x N x N1 [  Valorea lui x N este limita irului i deci x N = a . #include <stdio.h> #include <stdlib.h> #define EPS 1e-10 //se calculeaza radical din a = numar real pozitiv main( ) { double a, x1, x2, y ; printf("Introduceti valoarea lui a>0, a= "); scanf(" %lf ", &a); x2=1; do { x1=x2; x2=0.5*(x1+a/x1); if ((y=x1-x2)<0) y=-y ; // valoarea absoluta a lui y } while (y>EPS); printf("a=% .11g \t radical(a)=% .11g\n", a, x2);

} n program, x1 i x2 sunt dou valori consecutive ale termenilor irului. Ele sunt necesare pentru estimarea erorii i oprirea procesului de calcul, cnd eroarea este mai mic sau egal cu EPS. Variabila y este utilizat pentru diferena a doi termeni consecutivi, valoarea absolut a acesteia i compararea cu EPS. Specificatorul .11g arat c valoarea se tiprete pe minim 11 spaii dup virgul, n formatul cel mai economic (normal sau cu exponent). Numrul de iteraii (de repetri ale buclei do-while) nu este anterior cunoscut; el depinde de valorea EPS impus: cu ct aceast valoare este mai mic, volumul de calcul (numrul de repetri) crete. Pentru aflarea numrului de repetri, se poate introduce un contor, iniializat cu zero, care este incrementat la fiecare trecere. n final, valoarea contorului arat numrul de treceri prin do-while.

3.6. Instruciunea for


Corespunde structurii repetitive condiionate anterior, ca i n cazul instruciunii while; spre deosebire de while, instruciunea for utilizeaz trei expresii, fiecare avnd un rol propriu. Sintaxa: for (expresie_1; expresie_2; expresie_3) instructiune ; Prin definiie, construcia de mai sus este absolut echivalent cu secvena: expresie_1; while (expresie_2) { instructiune ; expresie_3 ; } Din construcia echivalent cu while, rezult c: { expresie_1 este interpretat ca o instruciune care se execut o singur dat (iniializare); { expresie_2 este condiia logic de execuie a ciclului; { expresie_3 este interpretat ca instruciune de actualizare;

Instruciunea for este, n esen, un while dotat cu o iniializare i o actualizare la fiecare iteraie, actualizare care se face dup execuia instruciunii din corpul while. Pe poziia "instruciune" este acceptat o singur instruciune C; dac sunt necesare mai multe, acestea se includ ntre paranteze acolade i formeaz astfel o singur instruciune, compus. Observaii: { Att expresie_1 ct i expresie_3 pot lipsi: for ( ;expresie _2; ) instructiu ne; Ceea ce se obine este while, care este preferabil lui for din punct de vedere al claritii. Dac lipsete i expresie_2, condiia este tot timpul adevrat, ceea ce duce la repetare infinit: for( ; ; ) instructi une; determin un ciclu infinit, din care se poate iei numai cu break, goto sau return . { Sub influena practicii din alte limbaje de programare, se consider i se prezint uneori instruciunea for limitat la un ciclu cu contor. Evident, cu for se pot implementa i cicluri cu contor dar este greit s limitm posibilitile lui for la cicluri cu contor. Fa de for din alte limbaje, n C instruciunea este mai complex. Cele trei expresii pot conine diferite instruciuni ale limbajului i se pot utiliza variabile diferite: #includ e <stdio.h> int main(vo id) { float depunere= 1000 .0; an=2000 for(pri ntf( "Dep ozit initial: \n") ;an+ +<=2 010; printf( "Dep ozit in %d este %.2f\n",a n,de pune re)) depuner e=de pune re * 1.19; return 0; } Programul de mai sus calculeaz suma dintr-un depozit bancar, care beneficiaz de o dobnd anual de 19%, pentru o periad de 10 ani. Dobnda se vars n contul de depozit dup fiecare an iar dobnda anului urmtor se calculeaz pentru suma majorat. Prima expresie din for este un mesaj care se scrie o singur dat. A doua expresie conine condiia de repetare i incrementarea variabilei "an".

A treia expresie conine mesajul privind suma din depozit dup fiecare an din perioada stabilit: 2000 - 2010. { O eroare frecvent este for (i=1; i=n; i++), care atribuie lui i valoarea n. Dac n este diferit de zero, acest for va cicla la infinit. O alt eroare este for (i=0; i==n; i++), n care se testeaz dac i este identic cu n, uitnd c exp_2 este condiie de continuare, nu de terminare. Exemple: 1. Repetri cu numr cunoscut de pai: double putere(do uble nr, int n) { double x_la_n; int k ; x_la_n= 1.0 ; for (k=1 ; k <=n ; k++) x_la_n *= x ; return x_la_n ; } Funcia putere() primete ca argumente numerele x i n i ntoarce valoarea x n , prin nmulire cu x, repetat de n ori. 2. Calcul de sume cu numr cunoscut de termeni: float suma(int n) { float sum=0; int k; for (k=0 ; k <=n ; k++) sum += 1.0/((k+1 .)*( k+2. )); return sum ; } Funcia sum() calculeaz suma specificat de argument.
k=0 1 (k+1)(k+2) n

, pentru valoarea lui n

3.7. Instruciunea continue


Se utilizeaz numai n corpul unei instruciuni repetitive (while, do-while, for). Sintaxa:

continue; Permite revenirea la nceputul blocului care se execut repetat, nainte de ncheierea sa. La apariia instruciunii continue, se renun la executarea instruciunilor urmtoare i se execut un salt la nceputul blocului. Prezena instruciunii mrete flexibilitatea programelor i contribuie la rezolvarea mai simpl a unor ramificaii. Nu se utilizeaz n cazul instruciunii switch.

3.8. Instruciunea break


Produce ieirea forat din instruciunile while, do-while, for, switch. Dac sunt mai multe asemenea instruciuni incluse unele n altele, se realizeaz numai ieirea din instruciunea curent (n care este break ). Sintaxa: break; n cazul instruciunii for, ieirea se realizeaz fr reactualizarea expresiei exp_3. n instruciunea switch, prezena lui break este strict necesar pentru a produce ieirea, dup tratarea cazului selectat; n celelate instruciuni, se poate evita break, prin includerea unor condiii suplimentare n cmpul "expresie", care condiioneaz repetarea.

3.9. Instruciunea goto


Este o instruciune de salt necondiionat la o instruciune etichetat, care se afl n aceeai funcie. Etichetele se definesc printr-un nume, urmat de dou puncte ":". Dei principiile programrii structurate recomand evitarea folosirii lui goto, instruciunea se dovedete uneori util. De exemplu, ieirea forat dintr-un ciclu inclus n altul (cu break se iese numai din ciclul interior). n secvena urmtoare, se caut o valoare comun n dou tablouri de ntregi, a i b: for (i=0; i<n; i++) for (j=0; j<m; j++) if(a[i]==b[j]) goto ok;

pritf("Nu sunt elemente comune\n"); . . . . . . . . . . . . . . . . . . . ok: printf("S-a gasit a[%d] = b[%d]=%d\n", i, j, a[i]); . . . . . . . . . . . . . . . . . . . O construcie cu goto se poate nlocui ntotdeauna cu una structurat introducnd eventual variabile suplimentare. Secvena anterioar se poate scrie: int gasit = 0; for (i=0; i<n && ! gasit; i++) for (j=0; j<m && ! gasit; j++) gasit = (a[i]==b[j]) ; if (gasit) printf("S-a gasit a[%d] = b[%d]=%d\n", i-1, j-1, a[i-1 ]); else pritf("Nu sunt elemente comune\n"); . . . . . . . . . . . . . . . . . . . Instruciunile break i continue permit construirea unor cicluri while, do - while, for, cu mai multe puncte de ieire sau ramificaii interne, evitnd folosirea instruciunii goto. Din sintaxa instruciunilor while, do-while, for, rezult c punctele lor de ieire pot fi doar la nceput (cnd blocul executabil nu se parcurge nici mcar o dat) sau la sfrit. Sunt situaii, cnd ieirea este necesar undeva "la mijloc", caz n care se folosete un ciclu infinit i un break ntr-o instruciune if.

4 Funcii
4.1. Definirea i apelul funciilor
Funciile sunt elementele constructive ale limbajului C i sunt de dou categorii: { funcii standard (de bibliotec) definite n fiiere *.h: pr in tf () , sc an f( ), s in () , st rlen () , ge tc ha r( ), p ut char () , . . . ; acestea pot fi utilizate n programe, prin specificarea fiierului header n care se afl. { funcii definite de utilizator, altele dect cele standard. n programul surs, o funcie definit de utilizator, are dou pri: antetul i corpul funciei. O funcie poate avea un numr de parametri (variabile) dar o singur valoare - valoarea funciei. Antetul

conine informaii despre tipul valorii funciei, numrul i tipul parametrilor i conine identificatorul (numele) funciei. Structura unei funcii: <tip > <nume > (< lista declaraiilor parametrilor formali >) { <declaraii > <instruciuni > } Primul rnd este antetul; acesta apare obligatoriu naite de corpul funciei dar i la nceputul programului (nainte de main()) situaie n care se numete prototip . n cazul tipurilor standard, <tip> din antet este un cuvnt cheie care definete tipul valorii returnate de funcie. n C exist dou categorii de funcii: { funcii cu tip, care returneaz o valoare, de tipul <tip>; { funcii fr tip, care nu returneaz nici o valoare dup execuie, dar efectueaz anumite prelucrri de date; pentru acestea se va folosi cuvntul rezervat void n calitate de <tip>. O funcie poate avea zero, unul sau mai muli parametri. Lista declaraiilor parametrilor (formali) este vid cnd funcia nu are parametri. n corpul funciei pot s apar una sau mai multe instruciuni return, care au ca efect revenirea n programul apelant. Dac nu apare nici o instruciune return, revenirea n programul apelant se face dup execuia ultimei instruciuni din corpul funciei. Valoarea returnat de o funcie. Instruciunea return Forma general: return <expresie> ; unde "expresie" trebuie s aib o valoare compatibil cu tipul declarat al funciei. Efectul const n ncheierea execuiei funciei i revenirea n programul apelant; se transmite valoarea calculat <expresie> ctre programul apelant, reprezentnd valoarea f unciei, ce va nlocui numele funciei. Dac funcia este de tip void, expresia lipsete. Exemplu:
void fact( in t n); //calc ul ea za n! { int k; long fct; if (n<0) {

print f( "V aloa re a arg. negati va !"\n) ; retur n; //nu se calcu le aza n! } for (fct=1 , k=1; k<=n; k++) fct *=k; print f(Va lo ar ea %d!=%l d\ n" , n, fct); retur n; // s-a calcula t n! si s-a afisa t }

{ Dac ntr-o funcie sunt mai multe comenzi return, prima ce se execut va produce revenirea n programul apelant. { O funcie poate transmite prin return o singur valoare, de orice tip (inclusiv structur) dar fr tipul tablou. { Valoarea returnat de o funcie cu tip trebuie s fie compatibil cu tipul declarat al funciei; de exemplu, dac o funcie este definit prin int f(), valoarea returnat poate fi de orice tip aritmetic, deoarece aceasta poate fi convertit imediat la tipul ntreg:
int f() { int i, float a; char c; . . . . . . . . . . . . retur n i; //valoa re a . . . . . . . . . . . . retur n a; //valoa re a . . . . . . . . . . . . retur n c; //valoa re a . . . . . . . . . . . . }

. . . coincide cu tipul func ie i . . . "a" este conver ti ta la int . . . "c" este conver ti ta la int . . .

{ Dac o funcie returneaz o valoare de tip adres (pointer), cerinele sunt mai restrictive: tipul valorii returnate trebuie s fie exact tipul funciei .
char *f() { char c, *adr_ c, tablo u_ c[ 5] , **adr_ ad r_ c; int *adr_i ; . . . . . . . . . . . . c='A' ; adr_c =&c; retur n adr_c ; //corec t, adr_c = tipul funct iei . . . . . . . . . . . . *adr_ adr_ c= ad r_ c; r et urn * adr_ ad r_ c; // co rect ,* ad r_ ad r_ c=ti pu l funct ie i adr_c =tab lo u_ c; retur n tablou _c ; //cor ect, numel e tabloul ui e un //poi nter de acelas i tip //cu tipul funct ie i . . . . . . . . . . . .

retur n *adr_c ;/ /g resi t, se retur neaz a un carac te r //in loc de adresa . . . . . . . . . . . . retur n adr_i; //gre si t, tipul pointe ru lu i (int) nu //core sp un de cu tipul funct ie i . . . . . . . . . . . . retur n ; //valo ar ea returna ta nu este defini ta

n cazul n care funcia are mai muli parametri, declaraiile de tip pentru parametri se separ prin virgul. Parametrii se utilizeaz pentru a permite transferul de date, ctre o funcie, n momentul utilizrii ei n program. La construirea unei funcii, se face abstracie de valorile concrete. Acestea vor fi prezente numai la execuia programului. Exemple: void f(int a, int b, char c) /*corect, se specifica tipul fiecarui param.*/ /* incorect, nu se specifica tipul lui b */ void f(int a, b, char c) Parametrii unei funcii, ca i variabilele definite n corpul funciei sunt variabile locale, adic sunt valabile numai n interiorul funciei; ele nu sunt recunoscute n exterior, nici ca nume, nici ca valoare. n momentul compilrii, este necesar doar cunoaterea tipurilor valorilor pe care le vor primi parametrii la execuie. Aceste declaraii de tip sunt indicate n antetul funciei i vor servi compilatorului s rezerve memoria necesar pentru fiecare parametru. Parametrii declarai n antetul unei funcii se numesc formali, pentru a evidenia faptul c ei nu reprezint valori concrete, ci numai in locul acestora, pentru a putea exprima procesul de calcul. Ei se concretizeaz la execuie prin valori ce rezult din apelarea funciei, cnd sunt numii parametri efectivi. Observaii: { Dac nu se specific tipul unei funcii, se consider automat c ea va returna o valoare de tip int . { n limbajul C++, controlul cu privire la tipul valorii unei funcii este mai strict i ca urmare se recomand, ca tipul s fie efectiv specificat, n toate cazurile. { Pentru funcia principal main, se pot utiliza antetele: int main() int main(void) void main() void main(void) main() main(void)

Primele dou antete arat c funcia returneaz o valoare ntreag; de asemenea ultimele dou antete arat c se returneaz o valoare ntreag, chiar dac nu se specific tipul int n antet. Se utilizeaz foarte frecvent varianta fr tip main(). Funcia main poate avea i parametri. Programul urmtor produce tabelarea valorilor unei funcii de dou variabile, n domeniul [0, 1]x[0, 1], cu pasul 0.1.
# include <stdi o. h> doubl e fm(dou bl e, doubl e) ; void main( vo id ) { doubl e x, y, pas=0.1 ; for(x =0.0 ; x<=1.0 ; x=x+pa s) for(y =0 .0 ; y<=1.0 ; y=y+pa s) print f( "x =%lf y=%lf f(x,y) =% lf \n ", x, y,fm (x ,y )) ; { doubl e fm(dou bl e x, doubl e y) { retur n (3.0*x *y + 1.0)/(1 .0 + x + y + x*y); }

Programul conine dou funcii: main() i fm(). Funcia matematic fm() are doi parametri de tip real, doubl precizie i anume x i y iar tipul valorii funciei este tot real, dubl precizie. Numele funciei reprezint valoarea ntoars de aceasta, de exemplu, fm(0.0, 1.0)=0.5 este valoarea lui fm cnd argumentele sale iau valorile x=0 i y=1. Instruciunea return <expresie>, ntoarce valoarea expresiei n programul apelant, aceast form fiind obligatorie la funciile cu tip. Declaraia double fm(double, double) ; de la nceputul programului este un prototip al funciei fm(), care descrie tipul funciei, numele i tipul fiecrui parametru. n acest mod, funcia fm() este recunoscut n main(), chiar dac definiia ei se afl dup main() . n general, este indicat s se scrie prototipurile tuturor funciilor, att ale celor definite n modulul curent de program, ct i ale celor definite n alte module dar folosite n modulul curent. Funciile de bibliotec sunt complet definite (inclusiv prototipurile) n fiiere header, deci includerea acestor fiiere, asigur i prezena prototipurilor. De exemplu, prototipul lui printf() este n fiierul stdio.h .

Apelul funciilor se face difereniat, dup cum sunt cu tip sau fr tip. O funcie fr tip se apeleaz prin: nume( list de parametri actuali ); /* cand funcia are parametri nume(); /* cand functia nu are parametri O funcie cu tip se apeleaz n general prin: variabil = nume( list de parametri actuali ); sau, mai general, prin specificarea numelui funciei ntr-o expresie n care este permis tipul valorii funciei. Uneori nu ne intereseaz valoarea pe care o returneaz o funcie i atunci funcia se apeleaz exact ca o funcie fr tip. Un exemplu uzual este funcia printf(), care ntoarce numrul de caractere scrise la consol, dar apelul uzual al acestei funcii se face ca i cum ar fi de tip void . Funciile au clasa de alocare implicit external, deci sunt vizibile n toate modulele care compun aplicaia. Ca i la variabilele externe, o funcie este vizibil din locul unde este declarat, pn la sfritul fiierului surs. Din acest motiv se folosete un prototip al funciei care se plaseaz la nceputul textului surs, n afara tuturor definiiilor de funcii. O alt posibilitate este includerea prototipurilor n fiiere header. Fiierele header predefinite conin, ntre altele, prototipuri ale funciilor de bibliotec. Prezena prototipurilor face posibil verificarea la compilare a corespondenei dintre numrul i tipul parametrilor formali cu numrul i tipul parametrilor actuali (la apelare). Exemple de prototipuri: 1. 2. float fmat (float x, float y, int n) int fdet ( float, int, int, char)

Declaraiile parametrilor formali pot conine numele acestora (ca n exemplul 1) sau pot fi anonime, deci fr a specifica un nume pentru fiecare parametru (ca n exemplul 2). n limbajul C, ordinea evalurii parametrilor actuali la apelul unei funcii, nu este garantat. n fapt, aceast ordine este n general de la dreapta la stnga, dar programele trebuie asfel construite nct s funcioneze corect indiferent de aceast ordine. De exemplu, secvena: int n=7; printf (" %d %d\n", n++, n); va tipri (la Borland C): 7 7 i nu 7 8 cum ar fi de ateptat. Asemenea construcii trebuie evitate, prin separarea n mai multe apeluri, de exemplu: printf ("%d ", n++) ;

printf ("%d\n", n) ; Acelai lucru este valabil i la expresii n care intervin apeluri de funcii. De exemplu, n secvena urmtoare nu se garanteaz care funcie va fi apelat prima: y = f( ) - g( ) ; Dac f() este apelat prima, i calculul valorii lui f() are efecte asupra lui g() nu se obine acelai rezultat ca n cazul apelrii mai nti a lui g(). Dac ordinea este important, aceasta se poate impune prin: y = -g( ) ; y = y + f( ); Exemplul 1 - (varianta 1, corect) Programul ilustreaz necesitatea folosirii prototipului. Se calculeaz valoarea maxim dintr-un tablou de pn la 20 de numere reale, introduse de utilizator.
#incl ude <stdio .h > #incl ude <conio .h > #defi ne MAX 20 d ou ble ma x( do ub le *t ab , i nt n) ;/ /p roto ti pu l funct ie i void main() { doubl e x[MAX] ; int i; print f("\ n Tastat i numere le ,t er mina ti cu 0:\n") ; for(i =0; i<MAX; i++) { scanf (" %l f",& x[ i] ); if(x[ i] == .0) break; } // la iesir e din for, i=nr. el em en te intro du se print f("\ n Maximu l este %lf ", max(x ,i )) ; getch (); } doubl e max(do ub le *tab, in t n) /* functia max (),de tip double ; parame tr ii sunt: adres a tablo ul ui si numaru l de eleme nt e */ { int i; doubl e vmax= *t ab ; for(i =1;i <n ;i ++ ) if(vm ax <* (tab +i )) vmax= *( ta b+ i); retur n vmax; }

Exemplul 1 - (varianta 2, incorect)


#incl ude <stdio .h > #incl ude <conio .h > #defi ne MAX 20

void main() { doubl e x[MAX] ; int i; print f("\ n Introd . numere le ,t er mina ti cu 0:\n") ; for(i =0;i <M AX ;i ++ ) { scanf (" %l f",& x[ i] ); if(x[ i] == .0) break; } // la iesir e din for, i = nr.ele me nte print f("\ n Maximu l este %lf ", max(x ,i )) ; / * f unct ia m ax e c onsi de ra ta d e ti p in t d eo ar ece nu exist a proto ti p */ getch (); } doubl e max(do ub le *tab, in t n) /* functia max (),de tip double ; parame tr ii sunt: adres a tablo ul ui si numaru l de eleme nt e */ { int i; doubl e vmax= *t ab ; for(i =1;i <n ;i ++ ) if(vm ax <* (tab +i )) vmax= *( ta b+ i); retur n vmax; }

La compilare, apare mesajul de eroare: "Type mismatch in redeclaration of max", datorit nepotrivirii tipului din definiie cu cel considerat implicit la apelul funciei; soluie: - definirea funciei naintea funciei main (incomod); - folosirea prototipului. Exemplul 1 - varianta 3 - corect---------#incl ude <stdio .h > #incl ude <conio .h > #defi ne MAX 20 d ou ble ma x( do uble ta b[ ], int n) ;/ /p roto ti pu l funct ie i / * t ab lo u u ni di me ns io nal p entr u ca re nu treb ui e pre ci za t n um ar ul de el em en te ;el treb ui e c un os cu t l a prelu cr ar e si se trans fe ra ca parame tr u separa t */ void main() { doubl e x[MAX] ; int i; print f("\ n Introd . numere le ,t er mina ti cu 0:\n") ; for(i =0;i <M AX ;i ++ ) {

scanf (" %l f",& x[ i] ); if(x[ i] == .0) break; } // la iesir e din for, i = nr.ele me nte print f("\ n Maximu l este %lf ", max(x ,i )) ; getch (); } doubl e max(do ub le tab[] ,i nt n) /* functia max (),de tip double ; parame tr ii sunt: tablo ul tab[] si numaru l de element e */ { int i; doubl e vmax= ta b[ 0] ; for(i =1;i <n ;i ++ ) if(vm ax <t ab[i ]) vmax= ta b[ i] ; retur n vmax; }

Exemplul 2: Programul surs este alctuit din dou fiiere, grupate ntr-un proiect. Se calculeaz puterea ntreag a unui numr real.
// princ.c #incl ude <stdio .h > #incl ude <conio .h > doubl e power( do ub le, int); // protot ip funct ie void main() { int i; double x; print f( "\ n Numaru l real: "); scanf (" %l f",& x) ; print f( " Putere a intreaga : "); scanf (" %d ",&i ); p ri nt f( "\n Re zu lt at ul e st e %l f\ n", p ow er (x ,i)) ; getch () ; } // func.c #incl ude <math. h> doubl e power( do ub le a, int b) { int i, n; double p=1.; n=abs (b ); for(i =1 ; i<=n; i++) p*=a; if(b< 0) retur n (1/p); else retur n( p) ; }

Cele dou fiiere se pot compila separat, apoi se grupeaz ntr-un proiect, folosindu-se meniul Project al mediului. Se deschide un fiier proiect ( extensia .prj) care va conine numele celor dou fiiere; dac la nume nu se adaug extensia se

consider c e vorba de fiierele sursa (cu extensia .c); se pot include n proiect direct fiierele *.obj rezultate dup compilare. Pentru a se crea modulul executabil, se selecteaz "Make EXE file" din meniul Compile, i are loc compilarea (dac n proiect sunt date fiiere surs), editarea de legturi i crearea fiierului EXE; modulul EXE va avea acelai nume cu fiierul proiect; se va selecta direct comanda Run pentru crearea fiierului EXE i execuie. Exemplul 3 - (varianta 1, incorect) Programul ilustreaz conversia automat de tip de date, la apelul funciei dup regulile de conversie cunoscute. Programul calculeaz cel mai apropiat numr ntreg, pentru un numr real.
#incl ude <stdio .h > #incl ude <conio .h > void main() { float x; print f( "\ n Introd uc et i numaru l "); scanf (" %f ",&x ); p ri nt f( "\n C el m ai a pr op ia t in tr eg es te % d" , n_int (x )) ; getch () ; } /* deoarec e nu exista protot ip , functi a n_int este consi dera ta de tip int iar param etru l i este conve rtit autom at la double in defini ti a functiei , param etru l fiind tip float , apare eroare la compi la re */ int n_int( fl oa t num) { if(nu m>0) { if(nu m-(i nt )n um <0 .5 ) retur n((i nt )n um ); /* se return ea za o valoa re intre aga prin conve rs ie explic ita */ else retur n( (i nt )n um+1 ); } else { if((i nt)n um -n um <0 .5 ) retur n( (i nt)n um ); else retur n( (i nt)n um -1 ); } }

Sunt mai multe puncte de ieire diferite, din funcia n_int dar toate cu acelai tip de date de ieire. Exemplul 3 - (varianta 2, corect)

#incl ude <stdio .h > #incl ude <conio .h > void main() { float x; print f("\ n Introd ucet i numaru l "); scanf ("%f ", &x ); p ri ntf( "\ n Cel ma i ap ro pi at in t es te n_int (x )) ; // se face conver sia la doubl e a lui x } int n_int( do ub le num) { if(nu m>0) { if(nu m- (i nt)n um <0 .5 ) return (( in t) nu m); else retur n( (i nt )n um +1 ); } else { if((i nt )n um-n um <0 .5 ) return (( in t) nu m); else retur n( (i nt )n um -1 ); } }

% d" ,

Exemplul 3 - (varianta 3, corect)


#incl ude <stdio .h > int n_int( fl oa t) ; // nu e obliga to ri e prezen ta identif ic at or ul ui de //par amet ru , este sufici en t tipul; void main() { float x; print f("\ n Introd ucet i numaru l "); scanf ("%f ", &x ); p ri ntf( "\ n Cel ma i ap ro pi at in t es te % d" , n_int (x )) ; } int n_int( fl oa t num) { if(nu m>0) { if(num -( int) nu m< 0. 5) return (( in t) num) ; else retur n( (i nt )n um+1 ); } else { if((in t) num- nu m< 0. 5) return (( in t) num) ; else return (( in t) nu m-1) ; } }

TEM

1. S se scrie un program care s citeasca numere reale introduse de la tastatur pna la citirea numarului 0 i s conin funcii care s calculeze numrul minim, numrul maxim i media numerelor citite. 2. S se scrie un program care s numere cuvintele dintr-un text de maxim 1000 de caractere de la tastatur. Cuvintele sunt separate prin spaiu, caracterul Tab sau Enter. ncheierea citirii textului se face prin apasarea tastei Esc ('\0x1b'). 3. S se scrie un program care s calculeze i s afieze inversa unei matrice de 3 linii i 3 coloane cu elemente ntregi citite de la tastatur.

4.2. Transmiterea parametrilor la funcii


Transmiterea parametrilor actuali, ctre o funcie, se face: { prin valoare; { prin referin; La transmiterea prin valoare, se copiaz valoarea fiecrui parametru actual n spaiul rezervat pentru parametrul formal corespunztor. Acest principiu de transfer face ca modificrile fcute asupra parametrilor formali, n corpul funciei, s nu se reflecte n afara ei, adic o funcie nu poate modif ica parametrii actuali cu care este apelat, sau altfel spus, parametrii actuali sunt pentru funcie nite constante predefinite. Acest tip de transfer este predefinit n C i nu poate fi modificat ! Transmiterea parametrilor actuali prin referin, se bazeaz pe tipul de date pointer (adres). Parametrii actuali ai funciei sunt declarai n mod explicit, pointeri. n acest tip de transfer, o funcie primete indirect valorile actuale (prin adresele lor) i le poate modifica tot n mod indirect, prin intermediul pointerilor de acces, de care dispune. Exemplu: Pentru modificarea unor variabile, acestea nu se transfer direct ci prin pointeri, o funcie neputnd modifica direct valorile variabilelor din funcia care o apeleaz.
#incl ude <stdio .h > #incl ude <conio .h > void main() { v oi d s ch im ba(i nt * ,i nt *) ; / * pr ot ot ipul folo si t main, local */ int x=2003 , y=0; print f("\ n Valori initia le x=%d y=%d", x, y);

in

schim ba(& x, &y); print f("\ n Valori finale x=%d y=%d", x, y); getch (); } void schim ba (i nt *a,in t *b) { int temp; temp= *a ; *a=*b ; *b=te mp ; }

Programul produce pe ecran: Valori initiale : x=2003 y=0 Valori finale: x=0 y=2003 n general, modul n care se transmit parametrii la funcii este dependent de implementare. Unele principii generale, sunt ns valabile la toate implementrile. Parametrii sunt transmii prin stiv. Ordinea de plasare a parametrilor actuali n stiv este, n general, de la dreapta la stnga, pentru lista de parametri din apelul funciei. Standardul nu garanteaz ns acest lucru, i nici ordinea de evaluare a parametrilor. Un program corect conceput, nu trebuie s depind de aceast ordine. Descrcarea stivei se face, de regul, la majoritatea implementrilor de ctre funcia apelant. Regulile de mai sus sunt determinate de faptul c n C pot exista funcii cu numr variabil de parametri (aceeai funcie poate fi apelat o dat cu m parametri i alt dat cu n parametri etc.). Un exemplu clasic este funcia printf(), care are n mod obligatoriu numai primul parametru i anume adresa irului care descrie formatul, restul parametrior fiind n numr variabil i pot chiar lipsi. Modul de plasare asigur c, totdeauna, funcia printf() va gsi n vrful stivei adresa irului de caractere care descrie formatul. Analiznd specificatorii de format din acest ir, funcia printf() tie ci parametri s-i extrag din stiv i ce dimensiune are fiecare parametru ( 2 octei, 4 octei etc.). De exemplu, la un apel de forma:
print f("% d %s\n", i, s);

n vrful stivei se va gsi adresa irului constant "%d %s\n" . Funcia preia acest parametru din vrful stivei, citete irul i extrage din stiv nc doi parametri: un ntreg i adresa unui ir de caractere. Evident, dac parametrii actuali nu concord (ca numr i tip) cu specificatorii de format, rezultatele vor fi eronate. Este clar

acum, de ce este necesar ca stiva s fie descrcat de ctre programul apelant: acesta tie ci octei a ncrcat n stiv la apelarea unei funcii. De exemplu, un apel de forma:
printf( "% d %d", n);

va produce o a doua tiprire, fr sens ( se va tipri, ca ntreg, coninutul vrfului stivei nainte de apelul funciei), dar nu va duce la situaii de blocare sau pierdere a controlului; revenirea n programul apelant se face corect, iar acesta descarc stiva de asemenea corect. Un apel de forma:
printf( "% d" , m, n);

va tipri numai variabila m, ignornd pe n iar restul aciunilor se execut corect (revenire, descrcare stiv).

4.3. Biblioteci standard


Ofer o mare varietate de funcii, grupate dup tipul operaiilor efectuate (familii de funcii). Pentru fiecare familie, exist un fiier header (*.h), care conine prototipurile funciilor. Principalele fiiere header sunt: stdio.h ctype.h - conine funcii de intrare / ieire: printf(), scanf() - conine funcii pentru testarea apartenenei la clase de caractere: litere mari, litere mici, cifre, caractere tip spaiu, de control etc. - conine funcii pentru operaii cu iruri de caractere. - conine funcii matematice: sin(), cos(), exp(), log(), pow() - conine funcii utilitare: de conversie, de generare a numerelor aleatoare, alocare eliberare memorie etc. - conine macroinstruciuni pentru crearea de funcii cu numr variabil de argumente - conine funcii pentru gestionarea timpului (data i ora): numele zilei, lunii, anul, minutul, secunda, reprezentarea datei; - n aceste fiiere sunt definite constante care dau valorile minime i maxime pentru tipurile de baz; aceste valori depind de implementare: Borland C, Turbo C, Turbo C++ etc. - conine funcii standard pentru gestionarea ecranului n modul text (25 linii i 40 sau 80 de coloane)

string.h math.h stdlib.h stdarg.h time.h

limits.h float.h

conio.h

graphics.h - conine funcii (peste 60) pentru gestionarea ecranului n modul grafic (de ex. VGA: 480 x 640 pixeli); numrul de pixeli depinde de tipul de monitor i de adaptorul grafic. Pentru a utiliza o funcie, aceasta trebuie apelat. Forma general a apelrii este: nume_funcie(lista de parametri efectivi); Cu excepia funciilor de tip void, orice funcie returneaz o valoare care nlocuiete numele funciei. Funciile de tip void se numesc procedurale, deoarece realizeaz anumite prelucrri (operaii) dar nu returneaz o anumit valoare.

4.4. Intrri / ieiri standard: <stdio.h>


Conceptul de baz pentru intrri / ieiri standard este cel de pointer la fiier. n orice implementare este definit (cu typedef) un tip de structur, numit FILE. n general, prin fiier nelegem o colecie ordonat de elemente de acelai tip, numite nregistrri, organizate secvenial. Accesul la date se poate face numai prin intermediul unei variabile care schimb date cu o singur nregistrare, la un moment dat. Orice fiier are un marcator "nceput de fiier" i unul "sfrit de fiier".
ICF R1 R2 R3 xf Rn-1 variabil de acces Rn SFF

Fig.1 Structura secvenial a unui fiier

n C exist o mare varietate de funcii pentru lucrul cu fiiere. Acestea necesit informaii privind: numele fiierului, starea (deschis, nchis), poziia curent n fiier (componenta accesibil) etc. Aceste informaii sunt organizate ntr-o structur cu tip predefinit: FILE. Orice operaie cu fiiere, se face prin intermediul unei variabile de tip pointer la o structur de tip FILE. Pentru definirea acestei variabile se utilizeaz declaraia: FILE *adr_fisier; 4.4.1. Deschiderea unui fiier

Pentru deschiderea unui fiier se utilizeaz funcia fopen(). Ea returneaz un pointer spre tipul FILE (ce indic adresa informaiilor asociate fiierului) sau pointerul nul n caz de eroare. Prototipul este:
FILE *fopen (c onst char *nume_ fi si er , const char *mod);

unde: { nume_fisier este un ir de caractere - numele fiierului, care depinde ca form de sistemul de operare; de exemplu, la MS-DOS se scrie "c:\\us er\\ list a.tx t" este un ir de caractere care descrie modul de acces: { mod

"r" "w"

deschide un fiier text pentru citire; fiierul trebuie s existe deschide un fiier text pentru scriere; dac fiierul exist, coninutul lui se pierde; dac nu exist un fiier cu numele specificat, el este creat. deschide un fiier text pentru scriere prin adugare la sfrit; dac fiierul nu exist, el este creat. deschide un fiier text pentru actualizare, adic citire i scriere deschide un fiier text pentru actualizare; dac fiierul exist, coninutul lui se pierde; dac nu exist un fiier cu numele specificat, el este creat. deschide un fiier text pentru actualizare; fiierul poate fi citit dar scrierea se face prin adugare la sfrit; Exemplu: FILE *pf ; pf=fope n("c :\\u ser\ \lis ta.t xt", "w");

"a" "r+" "w+"

"a+"

Funcia fopen() creeaz un fiier pe discul c:\ cu numele lista.txt, pregtit pentru scriere date i returneaz un pointer de acces la fiier. Toate operaiile cu fiierul creat se realizeaz prin intermediul variabilei de tip pointer pf, care este un identificator de fiier. Dac operaia de deschidere fiier nu se poate realiza (disc protejat la scriere, disc plin), funcia fopen() returneaz valoarea zero = NULL care are semnificaia "nici o adres". De aceea, se recomand, ca la deschiderea unui fiier s se testeze dac operaia a decurs normal. Se

poate combina deschiderea cu testul de realizare, ca n exemplul urmtor:


FILE *pf ; if((p f=fo pe n( "c :\ \u se r\ \l is ta .t xt", "w ") )= =N UL L) { print f( "F isie ru l nu poate fi desch is "); exit( 1) ; }

Constanta NULL este predefinit n fiierul stddef.h i corespunde unui pointer care nu indic o adres (pointerul nul). Funcia exit() determin nchiderea tuturor fiierelor deschise, ncheierea execuiei programului i transferul controlului ctre sistemul de operare. n tabelul de mai sus, modurile care conin + (actualizare), permit scrierea i citirea din acelai fiier, mai precis prin acelai pointer. Dac se pune sufixul b, se indic modul binar: rb, wb, ab, r+b, w+b, a+b ; Se poate pune explicit i sufixul t, indicnd explicit modul text: rt, wt, at, r+t, w+t, a+t ; Distincia dintre modul binar i text, depinde de sistemul de operare (la MS-DOS exist diferene). Modul text este implicit. Diferenele se refer la dou aspecte: tratarea caracterelor "\n" i tratarea sfritului de fiier. 1. n modul text, gupul "\n" este convertit n dou caractere CR (0xD) i LF (0xA). La citire, are loc conversia invers. Dac se scrie n modul text i se citete n mod binar, nu se obine aceeai informaie. Se recomand ca scrierea i citirea s se realizeze cu acelai tip de funcii: fprintf / fscanf, fputs / fgets sau fwrite / fread etc. 2. Tratarea sfritului de fiier. n ambele moduri (text i binar), funciile de intrare/ieire gestioneaz numrul de octei din fiier. De exemplu, funcia fgetc(), la terminarea citirii fiierului, ntoarce o valoare ntreag predefinit EOF. Acest ntreg nu exist fizic n fiier ci este generat de funcia fgetc(). n modul text ns, exist un caracter special, 0x1A, ca ultim caracter al fiierului (exist fizic n fiier). La ntlnirea acestui caracter, fgetc() va ntoarce EOF. Dac un fiier conine numere ntregi sau reale, poate s apar un octet cu valoarea 26 (care se reprezint intern prin 0x1A). La citirea n modul text, nu se

poate trece de acest octet, el fiind interpretat ca sfrit fizic de fiier. Citirea trebuie fcut n acest caz n modul binar.

4.4.2. nchiderea unui fiier Toate fiierele deschise trebuie nchise nainte de terminarea programului. Dac se omite nchiderea fiierelor pot s apar incidente nedorite (distrugerea fiierelor, erori diverse). Funcia care nchide ceea ce s-a deschis cu fopen() este fclose(), cu prototipul: int fclose( FILE *pf); unde pf este pointerul de identificare a fiierului. Dac valoarea pe care o returneaz este zero, operaia a decurs normal iar dac nu este zero, operaia de nchidere nu s-a putut realiza (fiierul este inexistent sau este inaccesibil, cnd discul pe care se afla a fost scos din unitate).

4.4.3. Citirea i scrierea unor caractere { Funcia putc() face scrierea unui caracter ntr-un fiier deschis cu fopen(): int putc(in t caracter, FILE *pf); { Funcia getc() face citirea unui caracter dintr-u fiier deschis cu fopen(): int getc(FI LE *pf); Pentru a citi tot fiierul se poate folosi secvena: do caracte r=ge tc(p f) while (caracter !=EO F); Funciile f open (),f clos e(), putc (),g etc( ) reprezint un nucleu minim de funcii pentru lucrul cu fiiere.

5 Variabile globale i locale Domeniu de valabilitate


La dezvoltarea unui program este util ca problema s fie descompus n pri mai simple care s fie tratate separat (pe module). Dezvoltarea modular a programelor este avantajoas i conduce la programe structurate. Instrumentul prin care se poate aborda programarea structurat este funcia. Funciile care compun programul se pot afla n unul sau mai multe fiiere surs, care pot fi compilate separat i ncrcate mpreun. Este strict necesar se transmit corect informaiile ntre modulele de program. { Anumite informaii (valori de variabile) sunt utilizate numai n interiorul funciei; ele se numesc variabile locale sau interne i sunt definite i vizibile n interiorul funciei. { Alte informaii sunt necesare pentru mai multe funcii i se numesc globale; ele se definesc n afara oricrei funcii iar funciile au acces la acestea prin intermediul numelui variabilei. O variabil global poate fi accesat ntr-un singur fiier (n care este definit) sau n mai multe fiiere. { Variabilele globale, fiind accesibile mai multor funcii, pot transmite informaii ntre funcii, fiind utilizate ca parametri actuali ai funciilor.

5.1. Variabile locale


Sunt definite n interiorul unei funcii, pot fi utilizate i sunt vizibile numai n interiorul funciei. Nici o alt funcie nu are acces la variabilele locale, deoarece la ieirea din funcie, valorile lor sunt distruse (se elibereaz memoria ocupat de acestea). Nu exist nici o legtur ntre variabilele cu acelai nume din funcii diferite. Limbajul C nu perm ite s se defineas o funcie n interiorul altei funcii . Cea mai mic unitate de program, n care se poate declara o variabil local, este blocul = o instruciune compus, adic o secven de instruciuni delimitat de acolade. Limbajul C permite ca ntr-o instruciune compus s se introduc declaraii. O funcie poate conine mai multe blocuri. Durata de via a unei variabile este intervalul de timp n care este pstrat n memorie.

{ Unele variabile pot rmne n memorie pe toat durata execuiei programului, la aceeai adres; acestea sunt definite cu cuvntul cheie STATIC - variabile statice sau permanente . { Alte variabile sunt definite i primesc spaiu de memorie numai cnd se execut blocul sau modulul n care au fost definite. La ncheierea execuiei blocului sau modulului, se elibereaz automat memoria alocat i variabilele dispar. Dac se revine n blocul respectiv, variabilele primesc din nou spaiu de memorie, eventual la alte adrese. Variabilele de acest tip, sunt numite variabile cu alocare automat a memoriei. Exemplu:
float functi e( ) { int k ; stati c int a[]={1 , 2, 3, 4}; . . . . . . . . . . . . . . }

Variabilele blocul n care au Tabloul a[] programului. Variabila k

k i a[] sunt locale; domeniul lor de valabilitate este fost definite. pstreaz adresa de memorie pe toat durata execuiei este cu alocare automat a memoriei.

5.1.1. Iniializarea variabilelor locale La fiecare apel, variabilele cu alocare automat trebuie iniializate. Dac nu se face iniializarea, acestea vor avea valori ntmpltoare, care se afl n spaiul de memorie alocat. Variabilele statice se iniializeaz o singur dat. Exemple:
void incre me nt ar e( void ) { int i=1; stati c int k=1; i++ ; k++ ; print f("i =% d \t k=%d \n", i, k); } int main(v oi d) incre ment ar e( ); incre ment ar e( ); incre ment ar e( ); }

Rezultatul execuiei programului este:


i=2 i=2 k=2 k=3

i=2

k=4

Variabila i este cu alocare automat; ea este iniializat la fiecare apel al funciei cu valoarea i=1. Variabila k este de tip static, permanent; se iniializeaz o singur dat iar valoarea curent se pstreaz la aceeai adres. Variabilele statice dac nu sunt iniializate, primesc valoarea zero. Variabilele statice interne (locale) ofer posibilitatea pstrrii unor infoemaii despre funcie (de exemplu, de cte ori a fost apelat).

5.1.2. Variabile globale Dup domeniul de valabilitate, variabilele pot fi ierarhizate astfel: { variabile cu aciune n bloc (locale); { variabile cu aciune n funcie (locale); { variabile cu aciune n fiierul surs; { variabile cu aciune extins la ntregul program. Pentru ca o variabil s fie valabil n tot fiierul, trebuie declarat n afara oricrei funcii, precedat de cuvntul STATIC. Dac fiierul conine mai multe funcii, variabila este vizibil n toate funciile care urmeaz declaraiei. Prin convenie, numele unei variabile globale se scrie cu liter mare. Sfera de aciune cea mai mare pentru o variabil este ntregul program; o asemenea variabil este vizibil n toate funciile aflate att din fiierul n care a fost definit, ct i din alte fiiere. Pentru a crea o variabil global ea se declar n afara oricrei funcii, fr cuvntul cheie STATIC.
float x ; //var. global a in tot progr am ul stati c float y ; //var . globa la in fisier ul curen t int main(v oi d) { . . . . . . . }

Variabilele globale mresc complexitatea unui program deoarece ele pot fi modificate de orice funcie. Se recomand utilizarea lor numai cnd este strict necesar.

6 Structuri de date: LISTE


6.1. Definiie
Multe aplicaii impun ca informaiile prelucrate cu calculatorul s fie organizate sub forma unor liste de date. O list ordonat sau liniar este o structur de date omogen, cu acces secvenial format din elemente de acelai tip, ntre care exist o relaie de ordine determinat de poziia relativ a elementelor; un element din list conine o informaie propriu-zis i o informaie de legatur cu privire la elementele adiacente (succesor, predecesor); primul element din list nu are predecesor iar ultimul nu are succesor. De exemplu, n lista candidailor nscrii la concursul de admitere, un element conine urmtoarele informaii specifice: - nume, prenume, iniiala tatlui; - numarul legitimaiei de candidat; - notele obinute la probele 1, 2; - media la bacalaureat; - media notelor; Fiecare element al listei mai conine informaii cu privire la poziia sa n list (predecesorul i succesorul).
Nr. crt. 1 2 3 4 Numele i prenumele Nr. leg. 1/P 1/A 5/M 6/M . . . Nota 1 Nota 2 Media bac. 7.5 9.5 8.5 7.5 . . . 9.5 9 9.5 10 . . . 8.67 9.37 9.89 9.56 . . . Media gen. 8.54 9.28 9.22 8.95 . . . .

Popescu I. Ion Lucian Anton Gh. Adrian Liviu Moraru V. Vasile Maraescu Gh. Liviu Costin . . . . .

Fiecare informaie poate reprezenta o ''cheie'' de acces pentru cutare, comparaii, reordonare etc. De exemplu, lund drept cheie media, se poate crea o nou list n care candidaii s apar n ordinea descresctoare a mediei. Coninutul unei liste se poate modifica prin urmtoarele operaii: { adugarea de noi elemente la sfritul listei;

{ inserarea de noi elemente n interiorul listei; { tergerea unui element din list; { schimbarea poziiei unui element ( modificarea unei nregistrri greite); { iniializarea unei liste ca list vid, fr elemente, n vederea completrii ei; Alte operaii ce pot fi efectuate asupra listelor sunt cele de caracterizare, care nu modific structura listelor, ci doar furnizeaz informaii despre ele: { determinarea lungimii listei ( numrul de elemente); { localizarea unui element care ndeplinete o anumit condiie; Operaii mai complexe: { separarea n dou sau mai multe liste secundare dup un anumit criteriu; { combinarea a dou sau mai multe liste ntr-una singur; { crearea unei liste noi, prin selectareaelementelor unei liste pe baza unui anumit criteriu.

6.2. Reprezentarea listelor n memorie


n memorie, listele pot fi reprezentate prin structuri statice (tablouri) sau prin structuri dinamice ( liste nlnuite), folosind pointeri. n cazul reprezentrii statice, elementele sunt indexate prin asocierea unui indice i se depun n locaii succesive de memorie. Avantaje: timpul de acces este acelai la oricare element din list, accesul se realizeaz prin intermediul indicilor iar implementarea este simpl. Dezavantaje: lungimea fix a listei (este obligatorie declararea ei), introducerea i tergerea unui element n/din interiorul listei implic deplasarea tuturor elementelor pe alte poziii dect cele iniiale.

Zon de memorie Parametrii de acces


Tablou de pointeri

Element 1 Element 2 Element 3 Element 4

sfrit

curent

P1 (adres) P2 P3 P4

Pn-1 Pn

Element n-1 Element n

Figura 1

n cazul unei alocri statice (mai eficiente) a unei liste (fig.1), se construiete un tablou de pointeri care permit accesul la informaia util (elementele listei). Accesul la elemente se realizeaz prin doi parametri: curent (indic poziia n tablou a adresei elementului curent) i sfrit (indic poziia n tablou a adresei ultimului element). n cazul reprezentrii dinamice, prin liste nlnuite, elementele listei pot fi dispersate n toat memoria disponibil (se utilizeaz eficient zonele libere) iar conectarea elementelor listei se realizeaz prin pointeri care se adaug informaiei utile din fiecare element. Avantajele reprezentrii dinamice sunt: lungimea variabil a listei (pe parcursul programului de implementare, modificare), introducerea i tergerea fr modificarea poziiei pentru celelalte elemente. Principalul dezavantaj este durata de acces la un element care depinde de poziia sa n list. O structur dinamic de list conine patru parametri de acces (fig. 2): - lungimea listei (lungime), de tip ntreg, reprezint numrul de elemente; - adresa primului element (nceput ), de tip pointer; - adresa elementului curent (curent), de tip pointer; - adresa ultimului element (sfrit ), de tip ponter. Pentru ca operaiile de inserare i tergere s se fac la fel i pentru elementele de la capetele listei, se pot folosi nc dou elemente false (santinele) plasate la nceput i la sfrsit. Astfel, toate elementele utile din list au att predecesor ct i succesor.

Parametrii listei

lungime

nceput

curent

sfrit

... NIL informaie util

...

NIL NIL

inf.

inf. 2

... ... Fig. 2

inf. k

... ...

inf. n

Element 1

Fiecare element al listei conine informaia propriu-zis i doi pointeri: unul reprezint adresa elementului urmtor - pointer de legtur iar cellalt reprezint adresa de localizare a informaiei (nume de persoan i date personale, linia de tabel pentru un candidat, datele de identificare ale unei cri n bibliotec etc.) Pentru introducerea (inserarea) unui element nou n list, se modific valoarea pointerului de legtur din elementul predecesor i se copiaz vechea valoare a acestui pointer (adresa elementului urmtor) n pointerul de legtur al noului element . Cutarea unui element se face prin parcurgerea secvenial a listei, pn cnd pointerul curent ia valoarea dorit sau o component a informaiei corespunde unei condiii. Lista din fig.2 este o list simplu nlnuit - parcurgerea listei se poate face ntr-un singur sens, dat de pointerii de legtur. Pentru parcurgere n ambele sensuri, se introduc cte doi pointeri de legtur n fiecare element (cte unul pentru fiecare sens de parcurgere). Se obine astfel o list dublu nlnuit (fig.3), care prezint avantaje suplimentare privind operaiile curente prin micorarea timpului de acces la un element al listei cnd elementul curent de acces este anterior sau posterior. Prin legarea ntre ele a elementelor de la capetele unei liste dublu nlnuite se confer listei o structur circular i se pot elimina elementele "false" de marcare a capetelor (santinele).

Parametrii listei NIL NIL informaie util

lungime

nceput ... ...

curent

sfrit ... ...

NIL NIL

inf.

inf. 2

... ... Fig. 3

inf. k

... ...

inf. n

Element 1

6.3. Implementarea operaiilor de baz pentru liste simplu nlnuite


Presupunem c s-a definit un tip de date DATA, care este specific informaiei dintr-un element al listei i depinde de aplicaie (DATA poate fi un tip simplu sau tip structurat - tablou, structur etc.) Definim structura C pentru un element al listei, astfel:
typed ef struct elem { DATA data; struc t elem *next; } ELEME NT , *LINK ;

Tipul ELEMENT corespunde unui element al listei iar tipul LINK unui pointer la un element al listei. Lista este specificat printr-o variabil de tip LINK care indic primul element al listei. O list vid se va specifica printr-un pointer NULL. Cmpul next al ultimului element al listei va conine NULL, indicnd c nu mai urmeaz nici un element. Se observ c o list simplu nlnuit este o structur ordonat, care poate fi parcurs direct de la primul element ctre ultimul (ntr-un singur sens). Parcurgerea n ambele sensuri nu se poate face direct, dar se poate realiza prin folosirea unei structuri adiionale de tip stiv.

Totui, aceast variant nu este prea des folosit. Cnd sunt necesare parcurgeri n ambele sensuri, se folosesc liste dublu nlnuite. Pentru semnalarea situaiilor de eroare se va folosi urmtoarea funcie, care tiprete irul primit i apoi foreaz oprirea programului:
void err_e xi t( co ns t char *s) { fprin tf (s tder r, "\n %s \n", s); exit( 1) ; }

O prim operaie care trebuie implementat este cea de creare a unui element printr-o funcie n ew _el( ) . Spaiul pentru elementele listei se creeaz la execuie, prin funcia standard m al loc( ) . Este avantajos ca funcia de creare element s iniializeze cmpurile d at a i nex t ale elementului creat. Aceast funcie va ntoarce un pointer la elementul creat.
typed ef int DATA; struc t el {DATA data; struct el *next; }; typed ef struct el ELEME NT , *LINK; LINK { new_el (D AT A x, LINK p) LINK t = (LINK) malloc (siz eo f( EL EM EN T)); if (t==N ULL) err_e xi t( "n ew_e l: Eroare de alocar e "); t->da ta = x; t->ne xt = p; retur n t; }

Se observ testarea corectitudinii alocrii cu mall oc () , ca i conversia la tipul LINK i folosirea lui s iz eo f() pentru a obine dimensiunea unui element. Se observ de asemenea c tipul DATA este implementat ca un tip simplu sau ca o structur (ceea ce este recomandat), atunci se poate face atribuirea t- >d at a = x , fr probleme. Iat un prim exemplu de creare a unei liste, pornind de la o structur nrudit, anume de la cea de tablou. Funcia primete adresa tabloului i numrul de elemente, ntorcnd un pointer la lista creat.
LINK arr_t o_ li st _i (DATA *tab, int n) { LINK t = NULL, p; if (n>0) t=p=ne w_ el (* ta b+ +, NULL); else retur n NULL; while (--n)

{ p->ne xt = new_e l( *t ab ++ , NULL); p = p->nex t; } retur n t ; }

Dac n nu este zero, se creaz un prim element i se memoreaz n t i p , apoi se creaz succesiv elemente, incrementnd pointerul ta b i avansnd pointerul p la urmtorul element. n final se ntoarce t. O a doua variant, mai clar, arr_ to _l is t_ r() , folosete recursivitatea, testndu-se cazul de baz ( n == 0 ), n care se ntoarce NUL L , altfel se ntoarce un pointer la un element creat cu new _e l( ) , cu parametrii * ta b (elementul curent al tabloului) i un pointer ntors de aceeai funcie ar r_ to _lis t_ r( ) , apelat cu parametrii ta b+ 1 i Ar fi o greeal ca n loc de t ab +1 s se scrie ++ta b , --n . deoarece codul ar depinde de ordinea de evaluare (care nu este garantat !), iar tab apare n ambii parametri. n particular, n Borland C, funcia ar fi incorect (sare peste primul element).

LINK arr_t o_ li st _r (DAT A *tab, int n) { if (n==0) retur n NULL ; e ls e re tu rn ne w_ el(* ta b, arr _t o_ li st _r(t ab +1 , --n)) ; }

6.4. Tiprirea unei liste


Se utilizeaz o funcie recursiv pentru tiprire, care nu este dependent de tipul DATA (acest tip depinde de aplicaie). Funcia tiprete cmpul data dintr-un element.
void print _d at a( DA TA x) { print f( "% 6d", x) ; } void print _l is t( LI NK t) { if (t==N ULL) printf (" NULL \n"); else { print _d at a(t- >d at a) ;

pritf (" -> "); print _l is t(t- >n ex t) ; } }

Pentru testarea funciei de tiprire se poate utiliza programul:


void main( vo id ) { DATA a[]={0 0, 11, 22, 33, 44, 55, 66, 77, 88, 99}; print _l is t(ar r_ to _l is t_i( a, 10); print _l is t(ar r_ to _l is t_r( a, 10); }

Formele generale ale funciilor de parcurgere a listelor liniare n variant iterativ i recursiv sunt:
void parc_ li st _r (L INK t) { if(t= =N UL L) { print f( "L is ta este vid ! \n"); retur n; } else { prelu cr ar e( t->d at a) ; parc_ li st _r (t-> ne xt ); } } void parc_ li st _i (L INK t) { if(t= =N UL L) { print f( "L is ta este vid ! \n"); retur n; } while (t != NULL ) { prelu cr ar e( t->d at a) ; t=t-> ne xt ; } } S-a considerat o funcie pr el uc ra re() , care

realizeaz operaiile dorite asupra datelor din fiecare element; funcia depinde de tipul concret al datelor i deci este dependent de aplicaie.

6.5. Operaii asupra listelor liniare


Pentru o list liniar nu se poate implementa un algoritm de cutare binar (prin divizarea listei i cutarea n seciuni) ci numai un algoritm de cutare secvenial. Funcia de mai jos caut un element dat ntr-o list, ntorcnd un pointer la primul element gsit, sau NULL dac lista nu conine elementul dat. Se utilizeaz funcia cmp() de

comparare a dou elemente, ntorcnd un ntreg negativ, zero sau pozitiv, la fel ca strcmp(). O asemenea funcie este strict necesar, deoarece n C, structurile nu se pot compara.
int cmp(DA TA a, DATA b); LINK list_ sr c_ i( LI NK t, DATA x) { while (t != NULL && cmp(t ->da ta , x)!=0) t=t-> ne xt ; retur n t; } LINK list_ sr c_ r( LI NK t, DATA x) { if(t= =N UL L || cmp(t -> data , x)==0) retur n t; else retur n list_src _r (t -> ne xt ,x); }

Ordinea n care apar condiiile conectate prin || sau && este esenial: dac t este NULL, comparaia dintre t->data (care nu exist) i x nu trebuie fcut. Avantajul esenial al structurilor nlnuite fa de cele secveniale (tablouri) apare la operaiile de inserare i tergere de elemente. La un tablou unidimensional, dac se terge un element, toate elementele care urmeaz trebuie deplasate cu o poziie spre stnga, pentru a pstra forma continu a tabloului (fr "goluri"). Similar, dac se insereaz un element, toate elementele care urmeaz trebuie deplasate spre dreapta cu o poziie, pentru a se realiza un spaiu pentru noul element. La structurile nlnuite, operaiile de inserare i tergere se realizeaz fr schimbarea poziiei celorlalte elemente; se modific doar cmpurile de adres care asigur "nlnuirea".

p q Fig.4 Inserare element

p Fig.5 tergere element

6.5.1. Inserarea unui element dup un element dat Se consider c pointerul p adreseaz elementul din list dup care se va face inserarea iar q adreseaz noul element, care se insereaz . Dac p=NULL nu avem dup cine insera iar dac avem q=NULL, nu avem ce insera. Dac p este adresa ultimului element din list (adic p->next = NULL), se conecteaz noul element (q) la acesta i atunci q devine ultimul element.
void ins_d up a( LI NK p, LINK q) { if(q= =N UL L || p==NU LL ) retur n; q->ne xt =p ->ne xt ; p->ne xt =q ; }

Inserarea se face simplu, prin dou atribuiri de pointeri (fig. 4). Trebuie observat un fapt esenial, c n instruciunile de atribuire expresiile din stnga sunt toate obinute cu operatorul ->, deci se modific efectiv lista. O atribuire de forma p= . . . ntr-o asemenea funcie, unde p este un parametru formal, nu are nici un efect n exteriorul funciei (transfer prin valoare). 6.5.2. tergerea unui element de dup un element dat
void del_d up a( LI NK p) { LINK q if(p= =N UL L || p->ne xt ==NU LL ) retur n; q=p-> ne xt ; p->ne xt =q ->ne xt ; free( q) ;

Dac p este NULL (nu exist element) sau p->next este NULL (adic nu exist urmtorul element), nu avem nimic de fcut. Dac elementul adresat cu p exist i nu este ultimul, se salveaz adresa de legtur n p->next i apoi se elibereaz memoria ocupat de elementul care se terge, adresat temporar cu q (fig.5). 6.5.3. tergerea tuturor elementelor unei liste
void del_l is t( LI NK t) { LINK p; while (t != NULL ) { p=t; t=t-> ne xt ; free( p) ; } }

Dup eliberarea spaiului indicat de un pointer, acel spaiu nu mai poate fi folosit. Ca urmare, se salveaz t ntr-o variabil temporar p, se atribuie lui t adresa urmtorului element i abia apoi se distruge elementul curent. Dac s-ar scrie direct free(t), nu mai avem acces la urmtorul element.

7 Structuri de date:

ARBORI

Organizarea liniar de tip list este adecvat pentru aplicaiile n care datele (elementele din list) formeaz o mulime omogen i deci se afl pe acelai nivel. n multe aplicaii, este strict necesar organizarea ierarhic pentru studiul i rezolvarea problemelor. Exemple: - planificarea meciurilor ntr-un turneu sportiv de tip cup (fig.1); - organizarea ierarhic a fiierelor n memoria calculatorului; - structura de conducere a unei ntreprinderi, minister etc.; - organizarea administrativ a unei ri.
Finala

SF1

SF2

sft.1

sft2

sft3

sft4

O1

O2

O3

O4

O5

O6

O7

O8

Figura 1.

n unele aplicaii, implementarea structurilor ierarhice n programele de calculator este singura cale de rezolvare. Un arbore este o structur ramificat format dintr-un nod A (rdcina) i un numr finit de arbori (subarbori) ai lui A. - orice nod din arbore este rdcin pentru un subarbore iar orice arbore poate deveni subarbore; - doi subarbori pot fi n relaie de incluziune, cnd un subarbore este inclus n cellalt sau de excluziune , cnd nu au noduri comune. Definiii: nod = punct n care se ntlnesc doi subarbori; nivel = numrul de noduri parcurse de la rdcin pn la un nod; rdcin = nivel 1; descendent = primul nod al unui subarbore; nod terminal = nod fr descendeni; nlimea unui nod = numrul maxim de niveluri;

arbore binar = arbore n care toate nodurile au 2 descendeni; a rdcina

b d h i j e k l f m

c g n o

subarbore stng

subarbore drept Fig. 2 Arbore binar

Orice arbore binar are doi subarbori: stng i drept. Arborii binari pot fi arbori de cutare i arbori de selecie; ei se caracterizeaz prin faptul c fiecare nod areo"cheie" reprezentat printr-o informaie specific de identificare a nodului. Cheia permite alegerea unuia din cei doi subarbori n funcie de o decizie tip "mai mic", "mai mare", etc. Un arbore este echilibrat dac pentru orice subarbore diferena dintre nalimile subarborilor si este cel mult 1.

7.1. Parcurgerea arborilor


Prin parcurgerea arborelui nelegem inspectarea (vizitarea) fiecrui nod i prelucrarea informaiei specifice lui. Pentru un arbore dat, corespunztor unei anumite aplicaii, se impune o anumit ordine de parcurgere. n programe se utilizeaz algoritmi de parcurgere sistematic a arborilor implementai sub form de proceduri. Procedurile eficiente de parcurgere genereaz liste dinamice cu nodurile ce urmeaz a fi examinate, care sunt reactualizate dup examinarea fiecrui nod; cnd lista devine vid operaia de parcurgere se ncheie (au fost examinate toate nodurile). Posibilitile de parcurgere sunt: 1. n lime , examinnd nodurile pe niveluri, n acelai sens. 2. n adncime: de la rdcin spre nodurile terminale sau invers; n cazul parcurgerii n lime algoritmul conine operaiile:

- se examineaz nodul rdcin i se formeaz lista (coada) descen- denilor si ntr-o anumit ordine ( exemplu: de la stnga la dreapta) ; - se examineaz primul nod din coada de ateptare i se adaug la coad descendenii si n ordinea stabilit; - se repet operaia precedent pn cnd coada devine vid.

intrare

nod examinat a b c d e f g h i j k

coada b c d c d d e f e f g f g h i g h i j h i j k i j k j k k ---------

g ieire

Fig. 3 Parcurgerea n lime

n cazul parcurgerii n adncime , algoritmul conine operaiile: - se examineaz nodul rdcin i se formeaz lista (stiva) descen- denilor si ntr-o anumit ordine ( exemplu: de la stnga la dreapta) ; - se examineaz primul nod din stiva de ateptare i se introduc n stiv descendenii nodului curent pn la cei terminali (ntregul subarbore); - se repet operaia precedent pn cnd stiva devine vid.

Rdcin

intrare
informaie ataat

ieire d

h
Subarbore stng Subarbore drept

Fig. 4 Parcurgerea n adncime

Fig. 5

7.2. Implementarea arborilor binari

Un arbore binar are n general 3 componente: nodul rdcin, subarbore stng, subarbore drept. Cazul limit este un arbore vid, reprezentat printr-o constant simbolic "ArboreVid". Tipul informaiei ataate nodurilor dintr-un arbore este specificat n fiecare aplicaie. De aceea vom considera c informaia din fiecare nod este adresat indirect prin intermediul unui pointer (fig. 5). Cei doi subarbori sunt de asemenea adresai indirect, fie prin pointeri (fig. 5), fie prin intermediul indicilor de tablou n cazul implementrii statice. Operaiile fundamentale specifice arborilor binari sunt: a) Operaii care furnizeaz informaii despre un arbore: { testarea existenei unui arbore ( dac nu exist, este vid); { furnizarea adresei ctre un subarbore; { evaluarea numrului de noduri; b) Operaii care modific structura unui arbore: { inserarea unui nod; { tergerea unui nod;

{ modificarea informaiei dintr-un nod; { tergerea arborelui (eliberarea spaiului de memorie). Observaii: 1. Operaia de cutare nu este inclus n operaiile fundamentale pentru c aceasta poate avea diferite variante, n funcie de tipul arborelui prelucrat. 2. Arborii sunt utilizai (n mod frecvent) pentru a memora informaii care se modific. De aceea, pentru reprezentarea lor se prefer soluia dinamic, bazat pe utilizarea pointerilor. n implementarea arborilor se utilizeaz o metod asemntoare cu cea de la liste, prin construirea unui fiier cu operaiile fundamentale i includerea acestuia n programele de aplicaii. 3. n afara operaiilor prezentate anterior, pot fi definite i altele, necesare n diferite aplicaii. Deoarece numrul de legturi ale unui nod este cunoscut (cel mult 2), se utilizeaz doi pointeri pentru nodurile din stnga i din dreapta, adic rdcinile celor doi subarbori. Un subarbore vid, va fi reprezentat prin pointerul NULL. Presupunnd c s-a definit un tip de date DATA pentru cmpul de informaie, urmtoarea structur C definete tipurile de date NODE i BTREE (referin la NODE):
struc t node { DATA d; struc t node *left; struc t node *right ; typed ef struc t node NODE, *BTREE ;

O prim funcie este cea care construiete un nod.


BTREE new_no de (D AT A d) { BTREE t=(BTR EE ) malloc (s iz eo f( NO D)); if(t= =NUL L) err_e xit( "n ew _n od e: Eroar e de aloca re ") ; t->d= d; t->le ft=t -> ri gh t= NU LL ; retur n t; }

Este util n aplicaii o funcie constructor de nod, care primete att cmpul de date ct i cele dou cmpuri de legtur:
BTREE init_n od e( DA TA d, BTREE left, BTREE right) { BTREE t=new_no de (d ); t->le ft =l eft; t->ri gh t= righ t; retur n t; }

Parcurgerea arborilor binari se face n trei moduri : { n preordine (RSD - rdcin, stnga, dreapta), adic se viziteaz rdcina, subarborele stng i apoi subarborele drept; cei doi subarbori vor fi tratai ca arbori n procesul de parcurgere, aplicnd acelai algoritm - RSD. { n inordine (SRD - stnga, rdcin, dreapta), adic se parcurge subarborele stng, se viziteaz rdcina i apoi subarborele drept; cei doi subarbori vor fi tratai ca arbori n procesul de parcurgere, aplicnd acelai algoritm - SRD. { n postordine (SDR - stnga, dreapta, rdcin), adic se parcurge subarborele drept, cel stng i apoi se viziteaz rdcina; cei doi subarbori vor fi tratai ca arbori n procesul de parcurgere, aplicnd acelai algoritm - SDR. Pentru arborele binar din fig. 2, aplicnd metodele de mai sus, rezult urmtoarele iruri de noduri: - preordine: a b d h i e j k c f l m - inordine: h d i b j e k a l f m c - postordine: h i d j e k b l m f n o de parcurgere g n o ; n g o ; g c a ;

Definiiile celor trei metode de parcurgere sunt recursive, adic se aplic la fel pentru orice subarbore; acest fapt sugereaz o implementare recursiv a funciilor de parcurgere. Considerm o funcie, visit() care examineaz informaia dintr-un nod, cu prototipul:
void visit (B TR EE );

Cele trei metode de parcurgere, se implementeaz n mod natural, astfel:


void par_r sd (B TR EE t) { if(t! =N UL L) { visit (t );

par_r sd (t -> left ); par_r sd (t -> righ t) ; } } ______ __ __ _____ __ __ __ _____ __ __ __ __ __ ____ _ void par_s rd (B TR EE t) { if(t! =N UL L) { par_s rd (t -> left ); visit (t ); par_s rd (t -> righ t) ; } } void par_s dr (B TR EE t) { if(t! =N UL L) { par_s dr (t -> left ); par_s dr (t -> righ t) ; visit (t ); } } _____ ____ __ __ __ __ __ __ __ __ __ __ __ ____ __ __ __ _

Pentru afiarea nodurilor parcurse (prin nume nod), sub form indentat, se poate folosi o funcie pri nt _t re e( ) , recursiv, care scrie un numr variabil de spaii, nainte de afiarea unui nod, n funcie de nivelul nodului respectiv.
void print _t (B TR EE t, int n)// funct ie ajuta toar e { int i; for(i =0 ; i<n; i++) print f( " "); if(t! =N UL L) { print _n od e( t->d ); // funct ie de afisar e nod print _t (t -> left , n+1); print _t (t -> righ t, n+1); } else print f( "A rb ore vid!" ); } void print _t re e( BT REE t) { print _t (t , 0); putch ar (' \n') ; }

7.3. Arbori de cutare


Arborii de cutare sunt arbori binari n care exist o relaie de ordine pe mulimea elementelor de tip DATA, acestea fiind cmpurile de date din noduri. Categoria arborilor de cutare este caracterizat de urmtoarele proprieti eseniale: { cmpurile de date din noduri conin valori distincte; { un arbore vid este, prin convenie, arbore de cutare; { rdcina unui arbore de cutare conine o valoare mai mare dect toate valorile din subarborele stng i mai mic dect toate valorile din subarborele drept; { subarborii stng i drept sunt arbori de cutare. O important proprietate a arborilor de cutare, care decurge din definiie, este aceea c parcurgerea n inordine produce o list de elemente sortate cresctor, n raport cu cmpul DATA. Aceast proprietate este utilizat de algoritmii de sortare intern. A doua proprietate important a arborilor de cutare, care d chiar denumirea lor, este posibilitatea implementrii unui algoritm eficient de cutare a unui element, pe baza valorii din cmpul DATA: se compar elementul cutat cu data din rdcin; apar trei posibiliti: { acestea coincid - elementul a fost gsit; { elementul cutat este mai mic dect data din rdcin - atunci cutarea continu n subarborele stng; { elementul cutat este mai mare dect data din rdcin - atunci cutarea continu n subarborele drept; Presupunem c s-a definit o funcie de comparaie (care este dependent de tipul DATA, deci de aplicaie):
int cmp_da ta (D AT A a, DATA b);

care returneaz o valoare negativ dac a< b , zero dac a=b sau o valoare pozitiv, dac a>b . Funcia de cutare, n variant recursiv, se poate scrie astfel:
BTREE cauta (BTREE t, DATA x) { int y; if(t= =NUL L || (y=cm p( x, t-.d) )= =0) retur n t; t=(y< 0) ? t->le ft : t->rig ht ; retur n cauta( t, x) ; } Funcia ca uta( ) primete un pointer t la rdcina arborelui i o valoare x i returneaz pointerul la nodul gsit (care conine x ) sau NULL dac valoarea x nu exist n arborele t .

Dac arborele de cutare este echilibrat (adic fiecare subarbore are aproximativ acelai numr de noduri n stnga i dreapta), numrul de comparaii necesare pentru a gsi o anumit valoare este de ordinul lui log2 (n ) , unde n este numrul de noduri. Dac arborele de cutare este neechilibrat, atunci cutarea este asemntoare celei dintr-o list simplu nlnuit, caz n care numrul de comparaii necesare, poate ajunge la n. Petru a elimina restricia ca nodurile s aib elemente distincte, se modific tipul DA TA , introducnd, pe lng valoarea propriu-zis, un ntreg care reprezint numrul de repetri ale valorii respective. Definim tipul informaiei din nod ca fiind KEY iar tipul DATA devine:
typed ef struc t {KEY key; int count;} DATA; Definiia tipurilor N OD E i BT RE E nu se schimb dar funcia de construcie a unui nod se modific, primind acum o valoare de tip KE Y

i iniializnd cu 1 cmpul count al nodului creat. Inserarea unui element ntr-un arbore de cutare Din definiia arborelui de cutare, rezult c adugarea unui nod se face n funcie de valoarea cmpului de informaie; pentru adugarea unui nod, este necesar aadar s se determine locul n care se insereaz. Se fac deci dou operaii: cutarea locului i inserarea propriu-zis. Algoritmul de baz utilizat n construcia arborilor de cutare este numit cutare i inserare . Se d arborele t i un element x. Dac x este n t, se incrementeaz cmpul count corespunztor valorii x; dac nu, x este inserat n t ntr-un nod terminal, astfel nct arborele s rmn de cutare .

Considerm c s-a definit o funcie de comparare, cm p_ ke y( ) care returneaz o valoare negativ, zero sau o valoare pozitiv, dup cum relaia dintre argumente este a<b, a=b, a>b .
int cmp_ke y( KE Y a, KEY b);

O implementare recursiv a algoritmului de cuare i inserare este:


BTREE cauta_ si _i ns r(BT RE E t, KEY x) { int c; if(t= =N UL L) retur n new_no de (x ); if((c =cmp _k ey (x , (t->d ). ke y) )< 0) t->le ft =c auta _s i_ in sr (t-> le ft , x); else if(c> 0) t->ri gh t= ca uta_ si _i ns r( t- >rig ht , x); else (t->d).c ou nt ++ ; retur n t; }

Funcia primete pointerul t la rdcina arborelui i cheia x. Dac s-a ajuns la un nod terminal sau arborele este vid, rezult c x nu este n arbore i ca urmare se construiete un nod nou cu valoarea x. Dac (t != NULL ) , se aplic algoritmul de cutare, apelnd recursiv funcia cu subarborele stng, respectiv drept. Dac se identific valoarea x n arbore, atunci se incrementeaz cmpul count al nodului n care s-a gsit x. n final se returneaz pointerul la arborele modificat. 7.3.1. Sortarea unui sir, bazat pe arbore de cutare Se construiete un arbore de cutare cu elementele irului de date, considerate ca elemente ale tabloului tab[]; apoi se parcurge n inordine arborele creat returnnd elementele sale, n ordine, n acelai tablou.
void sort( KE Y tab[] , int n) { BTREE t=NULL; int i for(i =0 ; i<n; i++) t=cau ta_s i_ in sr (t , tab[i ]) ; index =0; parcu rge( t, tab); delet e(t) ; }

Funcia parcurge() preia elementele din arborele t i le plaseaz n tabloul v[]:


stati c int index; void parcu rg e( BT RE E t, KEY v[]) { int j; if(t! =N UL L) { parcu rg e( t->l ef t, v); for(j =0 ; j<(t-> d) .c ou nt; j++) v[ind ex ++ ]= (t-> d) .k ey ; parcu rg e( t->r ig ht , v); } }

Se parcurge t n inordine, copiind n v cheia din rdcin, de attea ori ct este valoarea lui count. Este esenial declararea indicelui de tablou, index, n clasa s tati c ex te rn al i iniializarea lui cu 0, nainte de a apela funcia p ar cu rg e( ) n cadrul funciei sor t( ) ; se asigur astfel incrementarea sa corect pe parcursul apelurilor recursive ale funciei parcur ge () . Funcia d el et e( ) (utilizat n so rt() ), elibereaz spaiul de memorie alocat arborelui t, dup copierea datelor sortate, fiind i un exemplu de parcurgere n postordine:
void delet e( BT RE E t) { if(t! =N UL L) { delet e( t- >l eft) ; delet e( t- >r ight ); free( t) ; } }

Apelul funciei free() trebuie s fie dup apelurile recursive, deoarece, dup execuia lui free(t), variabila t nu mai exist. Acest algoritm de sortare, combinat cu implementarea arborelui prin alocare secvenial i anume chiar n tabloul care trebuie sortat, duce la o metod eficient de sortare intern.

8 Tehnici de sortare
8.1. Introducere, definiii
Prin sortare se nelege ordonarea unei mulimi de obiecte de acelai tip, pe baza unei componente de tip numeric, ce caracterizeaz fiecare obiect.

De regul, obiectele sunt organizate sub form de fiier sau tablou, n ambele cazuri ele formeaz un ir. Lungimea irului este dat de numrul de obiecte (n). Teoretic, obiectele sunt considerate nregistrri (n C sunt structuri): R 0 , R 1 , . . . , R n1 ; Fiecare nregistrare R k , are asociat o "cheie" C k , n funcie de care se face sortarea. Pe mulimea cheilor se definete o relaie de ordine, cu proprietile: { oricare ar fi dou chei C i , C j are loc numai una din relaiile: C i < C j, C i = C j, C i > C j ; { relaia de ordine este tranzitiv. Un ir este sortat dac oricare ar fi i, j c 0, 1, . . . , n 1 , i < j , avem C i [ C j . Aceast definiie implic o sortare n sens cresctor pe mulimea cheilor. n cele ce urmeaz, algoritmii de sortare vor fi implementai pentru sortare cresctoare, fapt ce nu micoreaz gradul de generalitate; dac dorim ca irul final s fie sortat descresctor, el va fi citit i nregistrat de la componenta n-1 ctre 0. Un algoritm de sortare se numete stabil, dac nregistrrile cu chei egale rmn, n irul sortat n aceeai ordine relativ n care se gseau n irul iniial ( nu se schimb ordinea nregistrrilor egale). Metodele de sortare se pot clasifica n dou mari categorii: { metode de sortare intern, n care nregistrrile se afl n memoria intern i sunt rapid accesibile; { metode de sortare extern, n care nregistrrile se afl pe suport extern (disc) iar timpul de acces (mare) face inacceptabil accesul repetat la nregistrri. Dac nregistrrile ocup spaiu mare n memorie (multe locaii de memorie / nregistr.), se prefer construirea unui tablou de adrese ale nregistrrilor. n loc de muta nregistrri, se mut adrese n tabel, cu o important economie de timp. Dac privim adresele nregistrrilor ca indici, tabloul de adrese conine n final permutarea indicilor, care permite accesul la nregistrri n ordinea cresctoare a cheilor. Aceast metod se numete sortare prin tablou de adrese . n cazul sortrii interne, dac nregistrrile nu ocup volum mare de memorie, ele vor fi mutate efectiv iar metoda se numete sortare de tablouri .

8.2. Funcii polimorfice

Aceste funcii sunt construite pentru a se putea aplica oricror tipuri de date. Evident c prelucrrile concrete vor fi diferite de la un tip de date la altul dar aceste aspecte concrete se pot transfera funciilor specifice de prelucrare. Transmitnd funciei polimorfice un pointer la funcia de prelucrare i utiliznd peste tot tipul de pointer universal void *, se poate realiza uor o funcie polimorfic. De exemplu, funcia standard, de bibliotec qsort() implementeaz algoritmul de sortare intern Quicksort, avnd prototipul:
void qsort (v oi d *tab, size_t n, size_t dim, int (*cmp) (c on st void *, const void *));

Se sorteaz tabloul tab, cu dimensiunea n, fiecare element din tablou avnd dimensiunea dim iar cmp este un pointer la o funcie care primete doi pointeri la dou elemente ale tabloului, returnnd valoare<0, 0 sau valoare>0 dup cum este mai mare al doilea, sunt egale sau este mai mare primul element. Exemple: 1. Pentru sortarea unui tablou de ntregi, funcia de comparaie trebuie s fie:
int cmp_in t( co ns t void *a, const void *b) { retur n *((int *) a - *((int *) b; }

Se convertesc pointerii (void *) la (int*), se ia apoi coninutul fiecruia (deci doi ntregi), se face diferena lor, care se returneaz ca rezultat. Apelul va fi n acest caz:
int tab_in t[ 10 0] ; qsort (tab _i nt , 100, sizeof (i nt ), cmp_i nt );

2. Pentru sortarea unui tablou de 100 de iruri de caractere, fiecare cu lungimea maxim de 50 de octei, declarat prin:
char a[100] [5 0] ;

n condiiile n care se dorete s se modifica efectiv poziia din memorie a celor 100 de iruri, funcia de comparaie va fi:
int cmp_si r( co ns t void *a, const void *b) { retur n strcmp (a , b); }

Se utilizaez funcia standard de comparaie a irurilor strcmp(); apelul funciei de sortare va fi:
qsort (a, 100, 50, cmp_s ir );

ceea ce pune n eviden c un element al tabloului este de 50 de octei. 3. Definim o structur i un tablou de structuri:
typed ef struc t {int key; char *sir} STRUCT _1 ; STRUC T_1 tab[10 0] ;

Funcia de comparaie pentru cutarea unui element n tablou, dup cheia numeric key, se definete acum astfel:
int cmp_st r_ 1( co ns t void *key, const void *elem) { retur n *((int *) key) - ((STR UC T_ 1 *) elem) -> key; }

Dac valoarea returnat este zero, elementul cutat a fost gsit. Aici key este adresa unui ntreg (cheia numeric) iar elem este adresa unui element al tabloului, deci adresa unei structuri de tipul STRUCT_1, deci este vorba de tipuri diferite. Se convertete pointerul key la (int *) i apoi se ia coninutul, apoi se convertete pointerul elem la (STRUCT_1 *) i se ia cmpul key al structurii indicate de elem. Se ntoarece diferena celor doi ntregi. Dac dorim acum s sortm tabloul de structuri de mai sus, cu funcia polimorfic qsort(), dup cheia key, trebuie modificat funcia de comparaie, deoarece se primesc de data asta adresele a dou elemente de acelai tip (elementele tabloului).
int cmp_st r_ 2( cons t void *a, const void *b) { retur n ((STRU CT _1 *)a)- >k ey - ((STR UC T_ 1 *)b) - >key; }

Pentru generalitate, toi algoritmii de sortare vor fi implementai prin funcii polimorfice, nct s se poat sorta orice tip de tablou, fr modificri n program. Pentru aceasta, vom considera o funcie extern care compar dou nregistrri, pe baza adreselor lor din memorie (de tip void * pentru generalitate) i care ntoarce o valoare ntreag negativ, zero sau o valoare pozitiv, n funcie de relaia dintre cele dou nregistrri (<, =, >).

Definim, pentru aceasta, tipul de date:


typed ef int (*PFC MP) (const void *, const void *) ;

adic un pointer la funcia de comparaie. Toi algoritmii vor fi implementai dup prototipul:
void sort( vo id *v, size_ t n, size_ t size, PFCMP cmp);

n care v este adresa de nceput a tabloului de nregistrri, n este numrul de nregistrri, size este dimensiunea n octei a unei nregistrri iar cmp este pointerul la funcia care compar dou nregistrri. Dup apelul funciei de sortare, tabloul v va conine nregistrrile n ordinea cresctoare a cheilor. Procednd n acest mod, partea specific a problemei (structura nregistrrii, poziia i natura cheii, tipul relaiei de ordine) este preluat de funcia de comparaie i detaat de algoritmul propriu-zis, ceea ce confer algoritmului un mare grad de generalitate. Pentru transferul nregistrrilor dintr-o zon de memorie n alta, considerm dou funcii externe, swa p( ) - care schimb ntre ele poziiile din memorie a dou nregistrri i c op y( ) - care copiaz o nregistrare dat la o adres de salvare.
void swap( vo id *v, size_ t size, int i, int j); void copy( vo id *a, void *b, size_t size) ; Prima funcie schimb ntre ele nregistrrile v[ i] i v[ j] iar a

doua copiaz nregistrarea de la adresa b (surs) la adresa a (destinaie). Dimensiunea fiecrei nregistrri este de si ze octei. Calculul adresei se face prin relaia ( BY TE * )v + i *s iz e , unde tipul B YT E este definit de:
typed ef unsig ne d char BYTE;

Metodele de sortare intern se clasific n trei categorii: { sortare prin interschimbare; { sortare prin inserie; { sortare prin selecie. Fiecare din aceste categorii cuprinde att metode elementare, n general neeficiente pentru blocuri mari de date, ct i metode evoluate, de mare eficien. Metodele elementare au avantajul c sunt mai uor de neles i au implementare simpl. Ele se pot folosi cu succes pentru tablouri de dimensiuni relativ reduse (sute, mii de nregistrri).

8.3. Eficiena algoritmilor de sortare


Este natural problema comparrii algoritmilor. Ce este un algoritm eficient i de ce unul este superior altuia ? Analiza eficienei unui algoritm se face, de regul n trei situaii: ir iniial sortat, ir aleator i pentru ir iniial sortat n ordine invers. Pentru algoritmii care nu necesit spaiu suplimentar de memorie, comparaia se face dup numrul de operaii efectuate, care n esen sunt: comparaii de chei i transferuri de nregistrri. Transferurile se pot detalia n interschimbri i copieri (o interschimbare necesit 3 copieri). Evident, numrul de operaii depinde de dimensiunea n a tabloului care este supus sortrii. Natura acestei dependene este cea care difereniaz algoritmii de sortare. Se definete o mrime care depinde de n, numit ordinul algoritmului O(n) . Dac numrul de operaii se exprim printr-o funcie polinomial de grad k, se spune c ordinul este O(n k ). De exemplu, dac numrul de operaii este n(n-1)/2, ordinul este O(n 2 ) . Algoritmii care apar n domeniul prelucrrii datelor sunt de ordin polinomial, logaritmic sau combinaii ale acestora. n algoritmii de sortare, apar ordinele n 2 i n*log(n). Un algoritm bun de sortare este O(n*log(n)); algoritmii elementari sunt de ordin O(n 2 ).

8.4. Sortare prin interschimbare: Bubblesort


Cea mai simpl este interschimbarea direct sau metoda bulelor. Pentru v[i] fixat, se compar v[j] cu v[j-1] pentru j=n-1, n-2, . . . , i. i se face un schimb reciproc de locuri n tablou, atunci cnd nu se respect condiia de sortare cresctoare v[j]>=v[j-1]. La sfritul primei etape, pe locul v[0] ajunge, evident cel mai mic element. Dup a dou trecere, pe locul v[1] va ajunge , cel mai mic element din cele rmase, s.a.m.d. Se procedeaz analog pn la ultimul element, care nu se mai compar cu nimic, fiind cel mai mare. Numrul de interschimbri este n(n-1)/2, n cazul cel mai defavorabil, cnd irul iniial este sortat invers (dar noi nu tim !).

O cretere a eficienei se poate realiza astfel: dac la o trecere prin bucla interioar (de comparri), nu s-a fcut nici o inversare de elemente, nseamn c irul este sortat i algoritmul trebuie oprit. Acest lucru se realizeaz prin introducerea unei variabile logice sor ta t care este iniializat cu 1 i pus n 0 la orice interschimbare. Se reduce numrul de operaii n cazul unui tablou iniial sortat (la n-1) i cresc performanele n cazul unui ir aleator. Denumirea metodei provine din faptul c elementele v[i], de valoare mare, se deplaseaz ctre poziia lor final (urc n ir), pas cu pas, asemntor bulelor de gaz ntr-un lichid.
void bubbl e_ so rt (v oid *v, size_t size, PFCMP cmp) { int i, j, sortat ; BYTE *a=(B YT E *) v; sorta t= 0; for(i =1 ; i<n && !sorta t; i++) { sorta t= 1; for(j =n -1 ; j>=i; j--) if(cmp( a+ (j -1 )* size , a+j*si ze )>0) { swap( a, size, j-1, j); sortat =0 ; } } }

8.5. Sortarea prin partiionare i interschimbare: Quicksort


Se poate ncadra ntr-o tehnic mai general, "Divide et impera" (mparte i stpnete), deoarece se bazeaz pe divizarea irului iniial n iruri din ce n ce mai scurte. Algoritmul Quicksort, fundamemtat n 1962 de C. A. R. Hoare, este un exemplu de algoritm performant de sortare, fiind de ordinul n*log(n). Este un algoritm recursiv, uor de implementat n C. Funcia care realizaez sortarea primete ca date de intrare o parte a tabloului ce trebuie sortat, prin adresa de nceput i doi indici left i right . Iniial funcia se apeleaz cu indicii 0 i n-1. Se alege un element arbitrar al tabloului v, numit pivot, care se noteaz cu mark, uzual m ar k = v [( le ft +r ig ht )/ 2] . Se divide tabloul n dou pri n raport cu mark: toate elementele mai mici dect mark trec n stnga iar cele mai mari dect mark trec n dreapta.

n acest moment elementul mark se afl pe poziia final iar tabloul este partiionat n dou tablouri de dimensiuni mai mici. Dac notm cu k indicele pivotului, putem apela aceeai funcie de sortare cu limitele le ft i k- 1 pentru partea stng i cu limitele k+1 i right pentru partea dreapt. Cnd se realizeaz condiia left>=right, algoritmul se ncheie. Exist mai multe variante de implementare; n exemplul de mai jos, se utilizeaz doi indici, i i j, care sunt iniializai cu left, respectiv cu right. Ct timp v[i]<mark, incrementm i, apoi ct timp v[j]>mark, decrementm j. Acum, dac i<=j, se face interschimbarea v[i] cu v[j], actualiznd similar indicii i i j. Procesul continu pn cnd i>j. S-a obinut urmtoarea partiionare: - toate elementele v[ le ft], v[ le ft +1 ], ..., v[ i- 1] sunt mai mici ca mark; - toate elementele v [j +1 ],v[ j+ 2] ,. .. ,v[r ig ht ] sunt mai mari ca mark; - toate elementele v[i],. .. ,v [j ] sunt egale cu mark; Acum se apeleaz aceeai funcie cu indicii le ft,j , respectiv i , right (dac left<j i i<right). Pentru eficien, variabilele intens folosite n etapa de partiionare (i i j) sunt declarate n clasa register.
void quick _s or t(BY TE *v ,s iz e_ t size,i nt left, int right ,PFC MP cm p) { regis ter int i, j; BYTE *mark ; i=lef t; j=rig ht ; switc h(j- i) { case 0: retur n; case 1: if(cm p( v+ i* si ze, v+j*si ze )> 0) swap(v, size, i, j); return ; defau lt : break; } mark= (BYT E* )m al lo c( si ze ); copy( mark , v+((le ft+r ig ht )/ 2) *s ize, size) ; do { while (c mp (v+i *s iz e, mark) <0 ) i++; while (c mp (v+j *s iz e, mark) >0 ) j--; if(i< =j ) swap(v , size, i++, j--); } while (i<=j); if(le ft<j ) quick_s or t( v, size, left, j, cmp); if(i< righ t) quick_s or t( v, size, i, right, cmp); free( mark ); }

void Quick (v oi d *v, size_t n, size_t size, PFCMP cmp) { quick _s or t(v, size, 0, (int) n-1, cmp); }

Analiza algoritmului Quicksort aleatoare, el este de ordin O(nlog(n)).

arat

c,

cazul

datelor

You might also like