Sunteți pe pagina 1din 70

UNIVERSITATEA POLITEHNICA TIMIOARA

LIMBAJE I TEHNICI DE PROGRAMARE


NOTE DE CURS

2003

CUPRINS
CAPITOLUL 1 1.1 Scurt ghid pentru limbajele de programare 1.2 De ce sunt utilizate limbajele de programare? 1.3 Definirea unui program 1.4 Prile componente ale unui program C 1.5 Utilizarea variabilelor pentru memorarea datelor unui program 1.6 Despre instruciuni 1.7 Efectuarea calculelor matematice n C 1.8 Determinarea valorii de adevr a expresiilor 1.9 Operatorul de condiionare 1.10 Structura funcional a programelor n C 1.11 Cmpul de aciune al variabilelor CAPITOLUL 2 2.1 Un prim program C 2.2 Funcii, instruciuni, tipuri 2.3 Tablouri 2.4 iruri de caractere 2.5 Tem CAPITOLUL 3 3.1 Prelucrarea fiierelor 3.2 Compilarea independent a fiierelor 3.3 Tem CAPITOLUL 4 4.1 Elemente de baz despre pointeri 4.2 Alocarea dinamic a memoriei 4.3 Implementarea listelor simplu nlnuite BIBLIOGRAFIE

CAPITOLUL 1 1.1 Scurt ghid pentru limbajele de programare


Toate slujbele bune solicit n zilele noastre multe cunotine legate de calculatoare. Atunci cnd se face o comparaie, angajaii care tiu s realizeze lucrri noi pe calculator au un avans fa de cei care abia tiu s utilizeze aplicaiile existente. Cu alte cuvinte, tiind s scriei propriile dumneavoastr programe pe calculator vei avea numai de ctigat. Un program pe calculator reprezint un set de instrucii prin care se comunic unui calculator cum s efectueze un anumit grup de operaii. Pentru a scrie programe, avei nevoie de un limbaj de programare. Acesta este foarte asemntor unui limbaj uman, constnd din cuvinte, fraze i reguli sintactice de care avei nevoie pentru a-i comunica unui calculator cum s realizeze ceea ce dorii. Spre exemplu, limba englez conine cuvinte, fraze i reguli de sintax care v sunt necesare pentru a-i spune unui chelner s mai aduc ap mineral. Nu putei comanda nimic unui chelner englez dac nu tii s vorbii englezete. n acelai mod, nu vei putea comunica nimic unui calculator dac nu cunoatei un limbaj pe care acesta s l neleag, un limbaj de programare. Exist multe limbaje de programare utilizate frecvent n aceti ani i multe altele care au fost abandonate pe parcursul evoluiei sistemelor de calcul. Chiar dac fiecare limbaj s-a orientat pe anumite direcii, fiind destinat s rezolve probleme din domenii specifice, toate au cteva caracteristici comune pe care va trebui s le nelegei dac dorii s scriei programe competitive.

1.2 De ce sunt utilizate limbajele de programare?


Componenta din calculatorul dumneavoastr responsabil de rezolvarea problemelor, numit, de obicei, unitate central de prelucrare (Central Processing Unit - CPU), nu nelege nici engleza i nici un alt limbaj uman. n schimb, accept instruciuni scrise n limbaj main. Limbajul main este un set de instruciuni fundamentale care depind de tipul calculatorului pe care n utilizai. O instruciune scris n acest limbaj ar putea s comunice unitii centrale s adune dou numere i s memoreze rezultatul ntr-un anumit loc. O unitate central recunoate cteva duzini de asemenea comenzi, fiecare dintre ele fiind destinat s ndeplineasc o sarcin mic i foarte specializat. Se pot scrie programe n limbaj main, primele calculatoare fiind programate astfel. Procesul, ns, este foarte greu i obositor. Limbajul main nu se scrie n cuvinte, ci n iruri formate din combinaii de 0 i 1 C numere
3

binare C care reprezint unica form de informaie acceptat i neleas de CPU. Spre exemplu, exist situaii cnd unitatea central trebuie s atepte dup alte componente ale sistemului pentru a executa ceva, cu alte cuvinte, trebuie s nu fac nimic. Comanda primit de CPU n acest caz este: 0100111001110001 Gndii-v ce ar nsemna dac ar trebui s lucrai cu sute de astfel de coduri! Nici primii programatori nu s-au artat foarte ncntai de aceast idee, motiv pentru care au inventat un fel de prescurtare, prin care fiecare instruciune scris n limbaj main este reprezentat printr-un cuvnt scurt sau o abreviere. De exemplu, codul prescurtat pentru niruirea binar de mai sus este NOP (no operation). Acest mod de programare a fost numit limbaj de asamblare. Limbajele de asamblare au fost utilizate ntr-o msur destul de mic datorit gradului ridicat de dificultate. Chiar i cele mai simple operaii, cum ar fi efectuarea operaiilor aritmetice cu dou numere, necesit parcurgerea ctorva etape. Deoarece multe operaii trebuiau efectuate n mod repetat, a devenit necesar scrierea unui mic program separat, destinat unei singure operaii. Apoi, cineva a venit cu idea s scrie un limbaj de programare care s fie format din aceste mici programe. Folosind denumiri sugestive pentru operaii, un asemenea limbaj se putea citi aproape ca n englez. Deoarece au un nivel ridicat de abstractizare n raport cu limbajul de asamblare, aceste limbaje sunt cunoscute sub denumirea de limbaje de nivel nalt. Printre primele limbaje mai importante de programare s-au numrat COBOL i FORTRAN, urmate de BASIC, Pascal, LISP, C i altele. Limbajul C a fost inventat de Dennis Ritchie de la AT&T Bell Laboratories, n anul 1972 (C a reprezentat a treia alternativ, primele dou numindu-se A, respectiv B). Dei este un limbaj de nivel nalt, C pstreaz un numr de caracteristici specifice limbajului de asamblare. C este destinat s asigure puterea i flexibilitatea unui limbaj main fr s sacrifice uurina utilizrii limbajelor de nivel nalt. Avantajele acestui limbaj de programare sunt: este puternic: programele scrise n C pot realiza un numr mare de calcule ntr-un numr mic de etape; este portabil: poate fi instalat pe o mulime de calculatoare diferite; este ntr-un permanent progres. Limbajul C prezint i cteva dezavantaje: - are structur rigid: programele scrise n C tind s fie foarte formale ca structur. Autorul francez Marcel Proust a scris c poeii talentai i gsesc cele mai mree versuri din cauza tiraniei rimei. n acelai mod, structura precis a limbajului C v foreaz s facei diferite lucruri ntr-un anumit mod, iar de aici pot s apar programe remarcabile; - are date dificil de memorat: limbajul C lucreaz greoi cu irurile de caractere;
4

- este dificil de nvat: datorit structurii rigide i a tratrii echivoce a unor date, limbajul C este considerat dificil de ctre nceptori.

1.3 Definirea unui program


Construirea unei cldiri necesit o organizare considerabil din partea constructorului. Construirea unui program robust i eficient se face n aceeai manier. Procesul de creare a unui program n C const n parcurgerea a trei etape: definire, proiectare i implementare. n prima etap, programatorul determin ceea ce ar trebui s fac programul (i, probabil la fel de important, ce nu ar trebui s fac) i ce tipuri de date vor fi utilizate. n a doua etap, programatorul utilizeaz specificaiile pentru a crea o secven de pai care vor fi urmai pentru soluionarea problemei. Acest lucru este necesar pentru c st n natura calculatorului s rezolve problemele pas cu pas, efectund la fiecare etap o operaie distinct asupra datelor. Aceast secven de pai poart denumirea de algoritm. n final, paii se transform ntr-un program propriu-zis, utiliznd programul C i instrumentele asociate lui. Ultimul lucru care mai trebuie luat n consideraie atunci cnd proiectai un program este modul n care l vor folosi utilizatorii. Aceast parte a programului este numit interfaa cu utilizatorul. Aceasta precizeaz modul n care, pe de o parte, ajung datele n program i, pe de alt parte, utilizatorul i comunic programului ce s fac i cnd. De exemplu: meniurile derulante, tastele pentru comenzi i elementele care pot fi deplasate pe ecran, prezente n programe precum Microsoft Windows, sunt elemente ale unei interfee grafice cu utilizatorul (Graphical User Interface C GUI). Pentru a scrie programul avei nevoie de un editor de texte. Dup scriere, programul va trebui salvat, adic se va crea o copie a acestuia, creia i se va da un nume, i care va fi stocat pe discul calculatorului. Ce poate s fac acest program dup ce a fost scris? Absolut nimic. nainte de a putea fi folosit, el va trebui transformat de o serie de programe speciale a cror datorie este s l fac inteligibil pentru calculator. Pentru ca un program s fie utilizabil, trebuie parcurse trei etape: compilarea, editarea legturilor i lansarea n execuie. Am spus, ntr-unul dintre paragrafele anterioare, c un calculator lucreaz doar cu instruciuni scrise sub form de iruri binare i c limbajele au fost elaborate pentru a facilita comunicarea cu programatorul. Conversia dintre un limbaj de nivel nalt i cod main este realizat de un program special numit compilator. S vedem cum lucreaz un compilator. El parcurge tot programul, linie cu linie, asigurndu-se, astfel, c este scris n form corect. Atunci cnd gsete o eroare, compilatorul se oprete din prelucrare i respinge programul. De regul, el afieaz pe ecran un mesaj prin care comunic tot ceea ce tie despre eroarea respectiv. Acest lucru s-ar putea s nu nsemne prea mult, dar,
5

cel puin, ar trebui s v arate care sunt liniile cu probleme i s v spun ce crede el c este greit n cazul fiecrui punct semnalat. Trebuie subliniat faptul c erorile de concepie nu sunt semnalate de compilator. Dac programul nu conine nici o eroare de sintax, compilatorul l va traduce n limbaj main. n aceast etap, el ia programul surs i l transform ntr-un program obiect. Codul surs al unui program este exact ceea ce ai scris, i se afl ntr-un fiier cu extensia .c sau .cpp (spre exemplu ProgramulMeu.c). Codul obiect este o versiune parial tradus a programului dumneavoastr, care va fi neleas de calculator (ProgramulMeu.obj). n acest stadiu, programul nu este gata de a fi utilizat, deoarece vor trebui ncorporate informaii suplimentare, dependente de sistem. Acest lucru este realizat de ctre un alt program special numit editor de legturi. Acesta ofer programului dumneavoastr toate informaiile necesare pentru a funciona pe un anumit tip de calculator. Rezultatul final al acestei operaii este un program executabil (ProgramulMeu.exe) care va putea fi lansat n execuie.

1.4 Prile componente ale unui program C


Un program C ncepe ntotdeauna cu un antet. Antetul i spune compilatorului de C care sunt cerinele generale ale programului, motiv pentru care elementele care l compun se numesc directive preprocesor. Cea mai des ntlnit astfel de directiv este include, care i precizeaz compilatorului c programul va avea nevoie de un fiier special, cum sunt stdio.h sau math.h, care conin funcii de bibliotec, pentru a putea rula corect. Un program tipic C are n antet cel puin o directiv include. Un program C are nevoie de locuri pentru stocarea datelor cu care va lucra, indiferent c este vorba de parametrii introdui de ctre un utilizator sau de rezultatele programului. Aceste locuri sunt create n poriunea pentru declaraii de variabile a programului. O posibil declaraie de variabile este urmtoarea: int operand1, operand2; float rezultat; Corpul oricrui program C conine cel puin o funcie, denumit main(). Aceasta este partea din program care execut cu adevrat operaiile dorite. O funcie este o seciune a unui program, defint de un nume i care efectueaz o anumit operaie. Funcia main() arat astfel: void main() { //corpul funciei }
6

Corpul funciei este ntotdeauna delimitat de acolade i conine linii scrise n limbajul de programare C. Numim aceste linii instruciuni. Ele efectueaz munca programului i corespund cu paii din algoritmul iniial. Un exemplu de instruciuni este urmtorul: printf("Introduceti valoarea primului operand: \n"); scanf("%d", &operand1); printf("Introduceti valoarea celui de-al doilea operand: \n"); scanf("%d", &operand2); rezultat = (operand1 + operand2) / 2; n exemplul de mai sus se citesc doi operanzi i se calculeaz media lor. Se poate observa c fiecare instruciune se termin cu simbolul ; , lipsa lui fiind semnalizat ca eroare la compilare. Pe lng instruciuni, ntr-un program C, de regul, se ntlnesc linii cu un coninut de genul: /*Programul calculeaza media a doua numere citite de la tastatura*/ sau // Programul calculeaza media a doua numere citite de la tastatura Aceste propoziii par s contrazic tot ce am spus pn acum despre faptul c C nu este echivalent cu un limbaj natural. De fapt, aceste rnduri sunt destinate programatorului i nu programului. Liniile de program delimitate de simbolurile /*i */ (pentru un bloc format din mai multe linii) sau de // (pentru o singur linie) poart denumirea de comentarii. Ele sunt folosite pentru a marca diferite pri ale unui program i pentru a explica unui utilizator scopul lor. Compilatorul de C ignor liniile marcate ca fiind comentarii.

1.5 Utilizarea variabilelor pentru memorarea datelor unui program


Dup cum am mai spus, calculatoarele efectueaz operaii cu date. Spre exemplu, cnd tastai cteva cuvinte de la tastatur, ele sunt trimise ctre calculator, hrnesc programul de prelucrare de texte cu care lucrai i sunt transmise ca un ecou pe ecran. n acest moment putei terge acele cuvinte, le putei modifica, salva etc. Se nate ntrebarea: unde se afl n tot acest timp respectiva informaie? Exist un loc n calculator n care sunt memorate datele n timp ce lucrai, denumit memorie de lucru i materializat prin memoria cu acces aleator (Random Acces Memory - RAM). Deoarece anumite operaii pot fi repetate pentru informaii diferite, zona de stocare trebuie s fie capabil s manevreze date diferite la momente de timp diferite. Partea din RAM care este preluat de un program nu are, la nceput, nici o caracteristic, fiind ca un teren neexplorat. Programul este cel care trebuie
7

s o pregteasc pentru stocarea datelor, iar n C acest lucru este realizat de ctre variabile. O variabil este o locaie de memorie, care poart un nume i este destinat stocrii datelor de un anumit tip. Ea este un nlocuitor al unei date care i poate modifica valoarea ntr-un program. Variabilele sunt create la nceputul unui program n zona destinat declarrii variabilelor. Un exemplu de declarare de variabile: char initiala; int x,z; Prima linie de cod creeaz o variabil de tip caracter, numit iniial. Aceast variabil poate memora un singur caracter alfanumeric. A doua instruciune creeaz dou variabile numere ntregi, x i y, unde vor fi memorate numere ntregi. Se poate observa c o instruciune de declarare de variabile are dou pri: prima parte specific tipul variabilei, iar a doua parte d un nume zonei de stocare. tip_variabila nume_variabila; Primul caracter n numele unei variabile trebuie neaprat s fie o liter sau un caracter de subliniere. Dup primul caracter pot fi utilizate orice combinaii de litere i cifre, dar nu pot fi utilizate caractere care nu sunt alfanumerice (adic #, $ etc.). Limbajul C face diferen ntre litere mari i mici, pentru el Nume i nume sunt dou variabile diferite. De regul, variabilele sunt folosite dup ce li s-a atribuit o valoare. Iniializarea unei variabile reprezint atribuirea unei anumite valori iniiale, imediat dup crearea acesteia. Spre exemplu: int x = 0; Atribuirea unei valori unei variabile se face cu ajutorul unei instruciuni de atribuire (care poate s fac parte din declararea variabilei, ca n exemplul anterior). Ori de cte ori o valoare este atribuit unei variabile, vechiul coninut este distrus. Forma general a instruciunii de atribuire este: nume_variabil = valoare; O alt categorie de date este reprezentat de datele care nu i schimb valoarea dup iniializare, pe parcursul unui program, denumite constante. Pentru a putea fi utilizate, ele trebuie mai nti declarate. n C, exist dou tipuri de constante: simbolice i literale. n exemplu, printf("Introduceti primul operand: \n); tot ceea ce se afl ntre ghilimele este o constant literal. Constantele simbolice sunt create cu ajutorul cuvntului cheie const. Declaraiile de constante se fac ntotdeauna la nceputul programului. const float pi = 3.1416; Forma general a unei instruciuni de declarare a constantelor este: const tip nume_constanta = valoare;
8

1.6 Despre instruciuni


Conceptul de expresie, ca i cel de variabil, este mprumutat din algebr. O expresie este un grup de simboluri reprezentnd o anumit valoare. Ea poate conine numere, caractere pentru operaii aritmetice (adunare, mprire etc.) i chiar nume de variabile. Rezultatul final al unei expresii este obinut prin evaluarea expresiei. n urmtorul exemplu, expresia este evaluat la 7.
( 4 + 10 ) / 2

Exist trei tipuri de expresii care prelucreaz datele: - matematice: au ca rezultat un numr (ntreg sau virgul mobil); - de tip text: au ca rezultat un ir de caractere; - logice: au valoare 1 sau 0 (interpretat ca adevrat sau fals). Aa cum simbolurile dintr-o expresie se combin pentru a forma expresia final, expresiile sunt alturate pentru a forma unitatea fundamental a unui program C, instruciunea, care este o singur line de program i care ndeplinete o anumit sarcin. La baza fiecrei instruciuni din limbajul C exist un cuvnt sau un simbol, care comunic scopul su. El este numit cuvnt cheie al instruciunii. Un cuvnt cheie este un cuvnt prin care se solicit efectuarea unei anumite operaii. Cuvintele cheie se consider rezervate, ceea ce nseamn c ntr-un program C nu putei utiliza nici un cuvnt cheie n alt scop (ca nume de variabil sau constant). Exist tipuri diferite de instruciuni pentru efectuarea diferitelor funcii dintr-un program: - instruciuni de declarare a variabilelor: creeaz o variabil (sau mai multe), indicnd un nume i un tip; int x; - instruciuni de atribuire: atribuie date variabilelor; x = 10; - instruciuni de control al programului: controleaz ordinea de execuie a instruciunilor din program. Probabil c cea mai puternic clas de instruciuni din C este cea care controleaz modul de execuie a unui program. Printre altele, aceste instrucii pot da programelor capacitatea de a lua decizii i de a trece la aciune pe baza condiiilor curente.

1.7 Efectuarea calculelor matematice n C


Limbajul C este dotat cu un set specific de operatori matematici, prezeni n majoritatea limbajelor de programare. Astfel, dispunem de urmtorii operatori aritmetici elementari: Operatori unari + + Operator de incremetare a unei valori - - Operator de decrementare a unei valori Operatori binari + adunare - scdere * nmulire / mprire % modulo Operatorii unari lucreaz doar asupra unei date, n timp ce operatorii binari lucreaz cu dou date. Operatorii de incrementare i decremetare efectueaz operaii de adunare i scdere, dar ntr-un mod mai limitat i mai compact fa de + i -. Cu aceti operatori pot fi utilizate doar variabile, nu constante sau expresii. Aceti operatori adaug sau scad o unitate din variabila la care se aplic. Astfel,
x ++

este identic cu
x= x + 1

iar
x--

este identic cu
x= x - 1

Amplasarea operatorului fa de variabil este, de asemenea, important. Acesta poate fi pus imediat la stnga sau imediat la dreapta variabilei a crei valoare este modificat. Poziionarea operatorului afecteaz momentul efecturii operaiei de imcrementare sau decrementare. Atunci cnd este la stnga variabilei operaia se efectueaz pe loc, dac este la dreapta, operaia este efectuat dup ce variabila a participat la calcularea restului instruciunii. Astfel, dac:
x = 5; y = x + +;

dup efectuarea instruciunilor se obine y = 5 i x = 6 . Pe de alt parte, dac secvena de mai sus se modific astfel nct:
10

x = 5; y = + + x;

dup efectuarea instruciunii se obine y = 6 i x = 6 deoarece valoarea lui x a crescut nainte de a se efectua instruciunea de atribuire. De asemenea, limbajul C mai pune la dispoziie i urmtoarele artificii:
x = x op c

este identic cu
x op = c

unde c este o constant, iar op este una dintre operaiile + , - , * , / .

1.8 Determinarea valorii de adevr a expresiilor


Cnd am fcut clasificarea expresiilor, am spus c unul dintre tipurile posibile este cel de expresie logic. Rezultatul unei astfel de expresii este ntotdeauna 1 sau 0, reprezentnd valorile logice Adevrat sau Fals. Deoarece aceste expresii urmeaz regulile logicii matematice, ele sunt denumite expresii logice. O mare parte a expresiilor din logica formal au fost emise de matematicianul englez George Boole, cu mai bine de un secol n urm. Din acest motiv, un ntreg capitol al logicii matematice a fost denumit, n cinstea sa, algebr boolean. American Heritage Dictionary, ediia a treia, definete acest tip de logic astfel: Un mod de tratare a variabilelor n sisteme logice combinatorii, cum ar fi propoziiile i elementele logice ale calculatoarelor, prin operaii AND (i); OR (sau); NOT (negat); IF (dac); THEN (atunci) i EXCEPT ( n afar de). Expresiile logice sunt compuse din nume de variabile i constante literale unite prin operatori. Puterea lor const n posibilitatea de a controla modul n care ruleaz programul dumneavoastr. Orice expresie logic poate avea unul dintre cele dou rezultate (1 sau 0). O instruciune n C care utilizeaz aceste rezultate pentru a lua o decizie, determin ramificarea execuiei programului. O expresie logic simpl poate fi utilizat mpreun cu operatori relaionali: expresie1 simbol expresie2 n relaia de mai sus, expresie1 i expresie2 sunt expresii matematice valide, iar simbol este unul dintre simbolurile operatorilor operaionali din C. Limbajul C are ase operatori relaionali, prezentai n tabelul de mai jos.

11

Operator
==
!=

Semnificaie este egal cu nu este egal cu (diferit) este mai mare dect este mai mic dect este mai mare sau egal cu este mai mic sau egal cu

> <
>= <=

Orice expresie logic, atunci cnd utilizeaz oricare dintre aceti operatori matematici, poate fi gndit ca o ntrebare: Este aceast relaie adevrat? Deseori, este foarte util combinarea rezultatelor a dou sau mai multe teste logice. Astfel, se creeaz condiii complexe n care se execut anumite operaii. Limbajul C ofer trei operatori logici cu ajutorul crora se pot combina expresiile raionale, prezentai n tabelul de mai jos.

Operator AND OR NOT

Simbol && || !

Primii doi operatori unesc dou expresii, iar operatorul NOT se aplic unei singure expresii. Un mod de exprimare a rezultatelor unei operaii logice ca OR (SAU) sau AND (I) este tabela de adevr. O astfel de tabel prezint, sub form de coloane, rezultatele obinute dintr-o expresie logic, pentru toate
12

combinaiile posibile ale valorilor de intrare. Tabele de adevr pentru AND i OR sunt prezentate mai jos.

P 0 0 1 1

q 0 1 0 1

AND 0 0 0 1

OR 0 1 1 1

n tabelul de mai sus, p i q sunt reprezentri convenionale ale celor dou variabile de intrare. Pentru exemplificare, s evalum urmtoarele expresii pentru x = 200 i y= 2: a) ( x > = 200 ) & & ( y = = 1 ) b) ( x > = 200 ) || ( y = = 1 ) n cazul primei expresii, valoarea este 0, deoarece cea de-a doua expresie relaional este 0, adic fals, iar operatorul AND cere ca ambii termeni s fie adevrai pentru ca rezultatul final s fie adevrat. Pentru cel de-al doilea exemplu, valoare expresiei este 1 deoarece operatorul OR cere ca cel puin unul dintre termenii expresiei s fie adevrat (n cazul nostru primul termen) pentru ca rezultatul final s fie adevrat.

1.9 Operatorul de condiionare


Limbajul C pune la dispoziia utilizatorilor un operator interesant, operatorul de condiionare, ?. Acesta combin funciile operatorului de atribuire, =, cu rezultatul unei expresii logice. Una dintre cele dou valori este atribuit variabilei n discuie, n funcie de rezultatul expresiei. Forma general a unei instruciuni de atribuire condiionat este: variabila = expresie_de _test ? expresie1 : expresie2; expresie_de _test este evaluat: dac este 1, valoarea din expresie1 este pus n variabila, altfel valoarea din expresie2 este pus n variabila.
13

Revenind la exemplul anterior, s evalum expresia:


z = ( x > = 200 ) ? y : 1

Deoarece x are valoarea 200 , evaluarea expresiei relaionale va fi adevrat, adic 1, ceea ce va avea ca efect atribuirea valorii lui y , adic 2 , variabilei z . n caz contrar, variabila z ar fi primit valoarea 1 .

1.10 Structura funcional a programelor n C


n general, un program n C poate fi scris ca un singur bloc de instruciuni. Problema este c, pe msur ce crete complexitatea programului, blocul respectiv ar fi din ce n ce mai mare i mai dificil de ntreinut. De asemenea, localizarea unei erori care blocheaz rularea programului poate fi extrem de dificil, dac nu chiar imposibil. Pentru a evita aceste dificulti, cea mai bun soluie pare s fie mprirea programelor n uniti mai mici, al cror avantaj major este faptul c pot fi reutilizate dac este nevoie. De fapt, programele n C au fost gndite s fie formate din seturi de uniti care pot fi cuprinse n programe complexe. O funcie, precum main(), este una dintre cele care construiesc blocul. Procesul de preluare a unei sarcini a programului i de rezolvare a ei n cadrul blocurilor logice care compun acel program este denumit proiectare de sus n jos. Proiectarea de sus n jos se refer la transformarea temelor mari n teme mai mici. La un moment dat, se ajunge n situaia n care un grup de sarcini nu mai poate fi remprit. Ce rezult sunt funciile din program. O funcie este o poriune discret a unui program n C care poart un nume i care execut o anumit parte dintr-o sarcin. Se spune despre funcii c sunt apelate. Funcia poate primi date din orice seciune a programului n care este apelat i poate s returneze date ctre acesta. Programele n C solicit un anumit grad de structurare i sunt formate din cel puin o funcie. Unele funcii sunt definite de utilizator, altele sunt predefinite i sunt disponibile n biblioteci, putnd fi accesate cu ajutorul directivei include. Cea mai mare parte a funciilor bibliotec au fost la nceput funcii utilizator. Ca atare, structura fundamental a oricrei funcii i modul de apelare ntr-un program sunt identice. Forma general a unei funcii este: specificator_de _tip nume_funcie (list_parametri) { corpul funciei } specificator_de_tip indic tipul datei returnat de funcie. O funcie poate returna orice tip de date cu excepia tablourilor. Dac nu este precizat tipul,
14

compilatorul presupune c funcia returneaz un numr ntreg. Returnarea datei din funcie se face prin intermediul cuvntului cheie return. list_parametri este o list de nume de variabile i tipuri asociate acestora, separate prin virgul i care primesc valorile argumentelor la apelul funciei. Se poate ntmpla ca o funcie s nu aib parametri, caz n care lista de parametri este vid. Cu toate acestea, parantezele sunt necesare. n cazul declaraiilor de variabile, mai multe variabile pot fi declarate ca fiind de acelai tip folosind o list de nume de variabile, separate prin virgul. float a, b, c; char x, y; Spre deosebire, toi parametrii unei funcii trebuie declarai individual, fiecare declaraie trebuie s conin numele i tipul fiecrui parametru, chiar dac toi parametrii sunt de acelai tip. medie_aritmetica(int a, int b, int c); Ca urmare, o funcie conine trei pri: - prototipul: spune compilatorului de C c n program va fi ntlnit definiia unei funcii. Toate prototipurile de funcii trebuie s apar nainte de nceputul funciei main(). - definiia: apare dup ncheierea funciei main(). Definiia funciei conine o singur instruciune care d numele funciei, tipul de date pe care le ateapt atunci cnd este apelat i tipul datelor pe care le va returna atunci cnd este apelat. - corpul: este locul n care funcia opereaz efectiv. Corpul funciei este nchis ntre acolade i urmeaz imediat dup definiia funciei. Cele trei etape nu sunt obligatorii. Dac ntreaga definiie a funciei apare nainte de funcia main() (sau de funcia care o apeleaz), nu mai este nevoie s se precizeze prototipul funciei. Pentru exemplificare, sunt prezentate n continuare prototipul, definirea, corpul i modul de apelare al funciei aduna(), care nsumeaz dou valori ntregi primite ca paramentri la apelare i returneaz ctre apelant rezultatul. int aduna(int op1, int op2); //Prototipul funciei void main(void) { int x, y, rezultat; printf("Introduceti valoarea primului operand: \n"); scanf("%d", &x); printf("Introduceti valoarea celui de-al doilea operand: \n"); scanf("%d", &y);
15

rezultat = aduna(x, y); //Apelarea funciei printf("Rezultatul este: %d\n ", rezultat); } int aduna(int op1, int op2) //Definiia funciei { //Corpul funciei int rez; rez = op1 + op2; return rez; } Se poate observa din exemplu de mai sus c, prototipul funciei i definiia ei seamn foarte mult. Diferena dintre ele const n faptul c prototipul funciei se ncheie cu ; , iar definiia funciei este ntotdeauna urmat de corpul funciei. Funciile de bibliotec se folosesc n acelai mod ca i funciile utilizator. Codul unei astfel de funcii este coninut ntr-un fiier antet, precum stdio.h, math.h, stdlib.h etc. Un fiier antet este un fiier text care conine att prototipul funciei, ct i descrierea ei. Includerea unui fiier antet ntr-un program, folosind include, face ca funciile definite n acel fiier s fie disponibile n programul respectiv. Cteva exemple de funcii bibliotec sunt scanf(), printf(), puts(), definite n fiierul antet stdio.h, cos(), sin(), definite n fiierul antet math.h. Numele variabilelor care apar n prototipul i n definiia funciei sunt foarte importante: ele asigur mijlocul de transmitere a datelor ctre o funcie pentru ca aceasta s opereze cu ele. Aceste variabile sunt numite argumente. Nu este obligatoriu s apar acelai nume i n instruciunea care apeleaz funcia, dar tipul datei trebuie s fie aceeai. Se poate observa acest lucru i din exemplul anterior unde funcia aduna() are dou argumente intregi, op1 i op2, iar la apel primete ca argumente variabilele x i y, tot ntregi. Tipul de variabil asociat cu un argument este unul dintre tipurile de variabile C: int, long, float i char. n plus, la funcii mai exist tipul de date void, care precizeaz faptul c funcia nu are parametri sau nu returneaz nimic. Cteva exemple de astfel de prototipuri de funcii: void afiseaza(char lit); int unNumar(void); void scrie(void); Apelarea unei funcii este foarte uoar. Pentru a face acest lucru, pur i simplu i scriei numele n locul potrivit i i furnizai argumentele necesare. Argumentele dintr-o instruciune de apelare a unei funcii se numesc parametri.
16

Parametrii sunt inclui ntre parantezele care urmeaz numele funciei. Pentru a nelege care este diferena dintre argumente i parametri, trebuie s va tii c argumentul este ca un loc rezervat pentru o dat, iar parametrul este cel care a fcut rezervarea i care deine data.

1.11 Cmpul de aciune al variabilelor


Limbajul C, ca majoritatea limbajelor de programare, permite restrngerea cmpului de aciune a variabilelor. Spre exemplu, o variabil poate fi utilizat doar de o anumit funcie. Chiar dac ntr-o alt funcie apare o variabil cu exact acelai nume, cele dou nu au nici o legtur ntre ele, acionnd ca dou variabile distincte, cu nume diferite. n funcie de locul i de modul n care este declarat o variabil, ea va avea un cmp de aciune specific: o funcie sau un grup de funcii n care poate fi utilizat. Acest cmp este denumit sfer de aciune a variabilei. Sfera de aciune a unei variabile se refer la spaiul n care diferite funcii dintr-un program pot avea acces la coninutul ei. O variabil la care poate avea acces o funcie este vizibil pentru acea funcie. Probabil c cea mai important utilizare a sferei de aciune este de a pstra datele izolate n timpul unei proceduri pentru a nu fi afectate de altele. Astfel, o variabil declarat ntr-o funcie este vizibil doar n acea funcie i se spune c ea este local funciei care a creat-o. Aceast variabil este folosit atta timp ct funcia este activ, dup care ea devine inaccesibil pentru alte funcii. De asemenea, variabilele declarate ntr-o funcie, cum este main(), spre exemplu, nu sunt vizibile n funciile pe care aceasta le apeleaz. Exist posibilitatea de a declara variabile n afara oricrei funcii, denumite variabile externe. Declararea unor astfel de variabile precede funcia main(), ca n exemplul urmtor: int x; void main(void) { int y; ..... } Variabila y este intern funciei main() i vizibil doar n interiorul ei. Variabila x este extern funciei main() i este accesibil tuturor funciilor programului. Variabilele externe sunt denumite i variabile globale. Unul dintre principiile cele mai importante ale programrii structurate este acela c funciile trebuie izolate, pe ct posibil, una de cealalt. O funcie
17

nu trebuie s afecteze alte funcii din program i nici variabilele pe care le utilizeaz ele. O funcie accept date prin intermediul listei de argumente i returneaz rezultate prin instruciuni return. Tot ce face n acest interval este strict personal i particular. Avantajul acestui mod de programare const n reutilizarea funciilor i n alt context. Programnd n acest mod, este necesar utilizarea datelor locale, deoarece este singurul mod de a izola datele funciilor. Dac mai multe funcii au nevoie de o anumit variabil, atunci se preteaz declararea ei ca variabil global.

CAPITOLUL 2
2.1 Un prim program C Prima parte a acestui capitol i propune familiarizarea cu caracteristicile limbajului de programare C. n acest scop este prezentat i analizat un scurt program. Acesta va fi editat, salvat, compilat i rulat. Salvarea se face utiliznd opiunea Save As (pentru prima salvare) respectiv Save (pentru urmtoarele salvri ale unui program) din meniul File. Pentru compilare se apas simultan tastele Alt i F9, iar pentru a rula programul se folosesc simultan tastele Ctrl i F9. Se recomand folosirea Help-ului pentru a afla descrierea prototipului i funcionrii instruciunilor i funciilor. #include <stdio.h> #include <conio.h> #define Dialog '-' void main(void){ char nume[20]; putchar(Dialog); printf("Buna ziua, studenti!\n"); putchar(Dialog); printf("Cum va numiti?\n"); putchar(Dialog); printf("(Introduceti numele) "); scanf("%s",nume); putchar(Dialog); printf("Buna ziua, domnule/doamna %s. Bine ati venit!", nume);
18

getch(); } Se observ c programul conine trei linii precedate de caracterul #, linii care constituie de fapt directive preprocesor, i o funcie care poart numele main. n general, un program n C are urmtoarea structur: directive preprocesor declaraii i definiii globale funcii Preprocesorul efectueaz operaii naintea compilrii propriu-zise, operaii ce sunt specificate prin directive preprocesor. Acestea au ntotdeauna ca prim caracter simbolul #. n programul prezentat anterior exist dou tipuri de directive: include i define. Cele dou directive include au ca efect includerea n program a fiierelor stdio.h i conio.h. Prezena acestor directive este impus de necesitatea declarrii funciilor de bibliotec apelate: putchar(), printf(), scanf() declarate n fiierul stdio.h - i getch() - declarat n fiierul conio.h. Despre funciile de biblioteca vor fi prezentate mai multe aspecte n capitolul urmtor. Cel de-al doilea tip de directiv preprocesor prezentat n acest program este directiva define. Prezena ei are ca efect substituirea textual n cadrul programului a identificatorului Dialog cu caracterul '-'. Dup cele trei directive preprocesor, programul continu cu funcia numit main. Orice program C trebuie s conin o funcie numit main, execuia programului ncepnd cu execuia acestei funcii. n programul prezentat funcia main afieaz texte de salut i comunicare prefixate de caracterul ' - ' (liniu de dialog). Utiliznd variabila nume, este citit(prin intermediul funciei scanf) i apoi afiat(cu funcia printf) numele interlocutorului. 2.2 Funcii, instruciuni, tipuri 2.2.1 Funcii de bibliotec Limbajul C a fost conceput ca un limbaj relativ redus, evitndu-se elementele care nu au fost considerate strict necesare sau care ar putea reduce flexibilitatea i viteza de execuie. Din aceast cauz nu sunt ncorporate faciliti de prelucrare direct a irurilor de caractere, mulimilor, listelor,
19

tablourilor. De asemenea nu sunt prevzute n limbaj faciliti de intrare / ieire (adic funcii de citire de la tastatur sau dintr-un fiier i afiare pe monitor sau scriere n fiier), alocare dinamic a memoriei sau metode de acces la fiiere. Pentru aceste operaii se ofer colecii standard de funcii de bibliotec. Acestea se afl n fiiere antet ("header"), care au extensia .h i se gsesc n directorul INCLUDE. Acest paragraf i propune trecerea n revist i exemplificarea celor mai uzuale funcii de bibliotec. Funcii de intrare / ieire getchar Declarat n: stdio.h Prototip: int getchar(void); Efect: returneaz un caracter citit de la tastatur sau constanta simbolic EOF dac s-a ntlnit sfritul fiierului de intrare (perechea CTRL-Z). Observaie: funcia ateapt apsarea tastei Enter. putchar Declarat n: stdio.h Prototip: int putchar(int c); Efect: afieaz caracterul c pe ecran; returneaz caracterul afiat sau valoarea EOF la detecia unei erori. gets Declarat n: stdio.h Prototip: char *gets(char *s); Efect: citete un ir de caractere s de la tastatur pn la ntlnirea caracterului newline; returneaz adresa irului citit sau valoarea NULL dac s-a citit CTRLZ. puts Declarat n: stdio.h Prototip: int puts(char *s); Efect: afieaz pe ecran irul s.
20

printf Declarat n: stdio.h Prototip: int printf(char *format, arg1, arg2, ...); Efect: afieaz pe ecran valorile din lista de argumente, conform formatului specificat. irul format poate conine caractere ordinare, care vor fi afiate ca atare, i descriptori de format prefixai de caracterul %. Un descriptor de format poate conine, n ordine, urmtoarele: - un semn minus care indic alinierea la stnga n cadrul formatului a valorii afiate - un numr care specific lungimea minim a cmpului de afiare - un punct care separ lungimea cmpului de afiare de precizia de afiare (de exemplu numrul de zecimale pentru valorile reale) n tabelul urmtor sunt artate caracterele de conversie cele mai uzuale ce pot aprea n descriptorii de format: Caracter d, I O x, X U C S F Lf Valoare ntreg zecimal ntreg octal fr semn ntreg hexazecimal fr semn ntreg zecimal fr semn caracter ir de caractere real simpl precizie real dubl precizie

n continuare sunt prezentate trei modaliti de afiare a irului de caractere "hello, world", utiliznd diverse formate: %s :hello, world: (irul se afieaz pe un cmp egal cu lungimea irului) %-15s:hello, world : (irul se afieaz pe un cmp de lungime 15, aliniat la stnga) %15s : hello, world: (irul se afieaz pe un cmp de lungime 15, aliniat, implicit, la dreapta) scanf Declarat n: stdio.h
21

Prototip: int scanf(char *format, adr1, adr2, ...); Efect: citete caractere de la tastatur, conform formatului, i nscrie valorile citite la adresele specificate. Funcia returneaz numrul de cmpuri citite cu succes sau valoarea EOF la ntlnirea sfritului fiierului de intrare. irul format poate conine: - caractere blank sau tab care sunt ignorate - caractere ordinare care trebuie s fie citite corespunztor de la intrare - specificatori de conversie, la fel ca i printf. Specificatorii de conversie determin modul de conversie a cmpurilor de intrare. Un cmp de intrare se definete ca o secven de caractere, altele dect spaiile albe ale limbajului: blank, tab. newline, carriage return. De exemplu, pentru citirea unei linii de forma: 26 Oct 2002 se va scrie urmtoarea secven: int zi, an; char luna[4]; scanf("%d %s %d", &zi, luna, &an); Se observ c la variabilele zi i an s-a folosit operatorul de adres &; la variabile lun el nu apare deoarece n limbajul C numele unui tablou este echivalent cu adresa sa (este un pointer ctre primul su element). getch Declarat n: conio.h Prototip: int getch(void); Efect: citete un caracter de la tastatur fr s-l afieze pe ecran; returneaz caracterul citit. getche Declarat n: conio.h Prototip: int getche(void); Efect: citete un caracter de la tastatur i-l afieaz pe ecran; returneaz caracterul citit.

22

Funcii de conversie Sunt prezentate n continuare dou funcii de conversie a unui ir de caractere ntr-o valoare ntreag, respectiv ntr-o valoare real. Aceste funcii sunt declarate n fiierul antet stdlib.h. Prototipul funciei int atoi(char *s); Double atof(char *s); Efect convertete irul s ntr-o valoare ntreag convertete irul s ntr-o valoare real

Funcii matematice Aceste funcii sunt declarate n fiierul antet math.h. Prototipul funciei Efect Double acos(double x); arccosinus de x Double asin(double x); arcsinus de x Double atan(double x); arctangent de x Double atan2(double x, double arctangent de y/x y); double ceil(double x); cel mai mic ntreg mai mare sau egal cu x double cos(double x); cosinus de x double exp(double x); exponeniala ex double fabs(double x); valoarea absolut a lui x double floor(double x); cel mai mare ntreg mai mic sau egal cu x double log(double x); ln de x double log10(double x); lg de x double pow(double x, double x la puterea y y); Double sin(double x); sinus de x Double sqrt(double x); radical din x Double tan(double x); tangent de x
23

2.2.2 Structuri alternative i repetitive if Sintax: if (<condiie>) <list de instruciuni 1> else <list de instruciuni 2> Efect: Testeaz condiia; dac ea este adevrat, se execut lista de instruciuni 1, iar dac este fals, lista de instruciuni 2. case Sintax: switch ( <variabil> ){ case <constant 1> : <list de instruciuni 1>; [break;] case <constant 2> : <list de instruciuni 2>; [break;] . . . default : < list de instruciuni >; } Efect: Se compar pe rnd valoarea variabilei cu valoarea uneia dintre constante. Dac sunt egale, se va executa lista de instruciuni aferent ramurii respective. Dac dup secvena de instruciuni executat apare break, atunci switch-ul va fi prsit ( variabila nu va mai fi comparat cu restul constantelor). Constantele trebuie s fie unice. E ilegal s existe duplicate ale constantelor n aceeai instruciune switch. Dac variabila nu este egal cu nici una dintre constante, atunci se va intra pe ramura default.

24

for Sintax: for ( [<iniializare>] ; [<condiie>] ; [<incrementare>] ) <list de instruciuni> Efect: Lista de instruciuni este executat repetitiv pn cnd condiia devine fals. naintea primei iteraii, n seciunea de iniializare, are loc iniializarea variabilelor pentru bucl. Aici pot aprea att expresii, ct i declaraii. O variabil declarat n seciunea de iniializare este ns vizibil doar n interiorul blocului instruciunii for. Dup fiecare iteraie, n seciunea de incrementare, se incrementeaz un contor al buclei. Fiecare din cele trei expresii de mai sus este opional. Dac lipsete condiia, ea se consider ntotdeauna adevrat. while Sintax: while ( <condiie> ) <list de instruciunit> Efect: Lista de instruciuni este executat repetitiv atta timp ct condiia este adevrat. Testul se face naintea execuiei listei de instruciuni, deci dac la evaluarea condiiei pentru prima bucl, aceasta este fals, atunci nu se va executa nimic din corpul instruciunii while. Dac nu este specificat nici o condiie, clauza while este echivalent cu: while(true) deci condiia se va considera adevrat. do Sintax do <list de instruciuni> while ( <condiie> ); Efect

25

Se execut repetitiv lista de instruciuni pn cnd condiia devine fals. Deoarece condiia este testat la sfritul buclei, chiar dac ea este fals, se va executa cel puin o iteraie. 2.2.3 Tipuri definite de utilizator Pe lng tipurile predefinite (int, char, double) utilizatorul poate s-i descrie propriile tipuri de date, cu ajutorul crora va defini ulterior variabile. Acest lucru se face utiliznd cuvntul cheie typedef, astfel: Sintax: typedef <definiie tip> <identificator> ; Efect: Asociaz numele simbolic <identificator> la un tip de dat definit n seciunea <definiie tip>. n continuare sunt prezentate tipurile structur i enumerare, foarte des folosite de utilizatori. Tipul structur Sintax: struct [<nume structur>] { [<tip> <nume variabil[, nume variabil, ...]>] ; . . . } [<nume variabile de tip structur>] ; Efect: Structura este folosit pentru a grupa mai multe variabile ntr-o singur nregistrare. <nume structur> este o etichet opional care asociaz un nume tipului structur definit de utilizator. <nume variabile de tip structur> este de asemenea opional i definete variabile de tipul descris prin structur.
26

Ambele clauze specificate anterior avnd un caracter opional, se impune ca cel puin una din ele s apar. Adic fie se d un nume structurii (pentru ca mai apoi s poat fi definite variabile de acest tip), fie sunt direct definite variabilele la sfritul structurii, fie se folosesc ambele variante. Dac nu apare nici una din cele dou clauze, nseamn c structura s-a definit fr a fi (sau fr a putea fi) folosit la declararea variabilelor. Elementele dintr-o nregistrare se definesc numind un tip, urmat de una sau mai multe variabile (separate prin virgul). Accesul la elementele din interiorul unei structuri se face utiliznd selectorul de nregistrare ' . ' . Exemplu: Se definete, folosind structura, un tip numit persoana care conine: - un cmp nume de 20 de caractere - un cmp ntreg care descrie vrsta Se definesc apoi dou variabile de acest tip. typedef struct{ char nume[20]; int varsta; }persoana; //s-a definit tipul de dat persoana // se definesc dou variabile x i y de acest tip persoana x,y //pentru a accesa numele i vrsta unei persoane se procedeaz astfel: x.nume='Ionescu'; &x.varsta=20 Tipul enumerare Sintax: enum [<nume tip>] {<nume constant> [= <value>], ...} [list de variabile]; Efect: Enumerarea se folosete pentru a defini o mulime de constante. <nume tip> este o etichet opional care denumete mulimea.
27

Urmeaz apoi o enumerare de constante care, opional, pot avea asociate valori. Valorile primite de constante sunt ntregi. Dac valoarea lipsete, atunci ea se consider egal cu valoarea constantei precedente la care se adaug unu. Dac i valoarea primei constante lipsete, ea se consider egal cu zero. Lista de variabile este de asemenea opional. n continuare este prezentat un program care exemplific unele din noiunile descrise n acest paragraf. Programul citete de la tastatur un numr real x i un caracter ce reprezint opiunea utilizatorului privind operaia ce trebuie efectuat cu numrul respectiv, dup care se afieaz rezultatul operaiei. Opiunile posibile sunt: 'e' - e la puterea x 'l' - ln din x 'r' - radical din x 'm' - valoarea absoluta a lui x (modulul) Programul pune la dispoziia utilizatorului un meniu din care se poate alege una din opiuni. n funcia main se citete numrul x i apoi se intr ntr-o bucl while care se repet de un numr infinit de ori (deoarece pentru while nu este prevzut o condiie, ci apare cifra 1 care definete valoarea true). La nceputul fiecrei iteraii se afieaz meniul. Opiunea utilizatorului este citit de la tastatur cu funcia getch(). Deoarece literele introduse pot fi att litere mari ct i mici, pentru a nu se testa ambele cazuri, opiunea a fost transformat n liter mic. Acest lucru se realizeaz de ctre funcia tolower(), care se afl n fiierul header <ctype.h>. n continuare, folosind case-ul, n funcie de opiunea introdus se afieaz rezultatul operaiei efectuate. Dac opiunea introdus este greit, se va afia mesajul "Opiune incorect". Ieirea din program se realizeaz introducnd opiunea 'i'. n acel moment se apeleaz funcia exit(0) pentru ieirea din while-ul infinit. #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <math.h> #include <ctype.h>
28

void meniu(void){ puts("\ne - e la puterea x"); puts("\nl - logaritm natural din x"); puts("\nr - radical din x"); puts("\nm - modul din x"); puts("\ni - iesire din program"); } void main(void){ int x; char opt; clrscr(); printf("\nIntroduceti numarul x "); scanf("%d",&x); while(1){ meniu(); opt = tolower(getch()); switch(opt){ case 'e':printf("\n%7.4f", exp(x)); break; case 'l':printf("\n%5.2f", log(x)); break; case 'r':printf("\n%5.2f", sqrt(x)); break; case 'm':printf("\n%f", fabs(x)); break; case 'i': exit(0); default: puts("Optiune incorecta"); } getch(); } }

29

2.3 Tablouri Tablourile sunt structuri de date omogene (elementele au acelai tip) i statice, care pot fi unidimensionale (iruri) sau bidimensionale (matrice) i se declar astfel: - tablouri unidimensionale <tip de baz> <nume tablou> <[dimensiune]> Tipul de baz poate s fie un tip predefinit sau un tip definit de utilizator, iar dimensiunea reprezint numrul de elemente care alctuiesc irul. exemplu: int tab[5]; definete un ir format din 5 elemente ntregi. - tablouri bidimensionale <tip de baz> <nume tablou> <[dimensiune1][dimensiune2]> n cazul matricelor, dimensiune1 reprezint numrul de linii, iar dimensiune2 numrul de coloane. exemplu: int matrice[3][4]; definete o matrice de numere ntregi cu 3 linii i 4 coloane. Matricele pstreaz proprietile matematice cunoscute: - adunarea - se poate aplica matricelor de aceeai dimensiune, este asociativ i comutativ - nmulirea - numrul de coloane pentru prima matrice trebuie s fie egal cu numrul de linii pentru a doua matrice - este asociativ, distributiv fa de adunare i necomutativ Parcurgerea tablourilor se face folosind structuri repetitive (for i while). Pentru a accesa elementele unui tablou se folosesc indici, care pornesc de la 0. Fie tabloul tab, definit anterior, ale crui valori sunt:

30

tab

0 10

1 3

2 -5

3 7

4 -12

Elementul cu valoarea 7 se afl pe poziia 3 i va fi accesat sub forma tab[3]. n continuare este prezentat un scurt program n care se definete un tablou de 5 ntregi. n funcia main sunt citite de la tastatur elementele tabloului, sunt tiprite pe ecran, iar apoi se caut elementul cu valoare maxim i se tiprete, mpreun cu poziia pe care a fost gsit. #include <stdio.h> #include <stdlib.h> #include <conio.h> int tab[5]; int m, ind_m; void main(void){ int i; for(i=0;i<5;i++){ printf("\ntab[%d]=",i); scanf("%d",&tab[i]); } m=tab[0]; ind_m=0; for(i=0;i<5;i++){ printf("%d ",tab[i]); if (m<tab[i]){ m=tab[i]; ind_m=i; } } printf("\n max=%d si a aparut pe pozitia %d",m,ind_m); getch(); }

31

Urmtorul program prezentat definete o matrice x cu n linii i m coloane, unde n i m sunt constante i au valorile 2, respectiv 3. Pentru claritatea programului, se scriu funcii de citire i tiprire a elementelor matricei, funcii care sunt apelate din main, dup care se parcurge matricea numrndu-se elementele negative. #include <stdio.h> #include <stdlib.h> #include <conio.h> #define n 3 #define m 2 int x[n][m]; void citire(){ int i,j; for(i=0;i<n;i++){ for(j=0;j<m;j++){ printf("\nx[%d,%d]=",i,j); scanf("%d", &x[i][j]); } } } void scriere(){ int i,j; for(i=0;i<n;i++){ for(j=0;j<m;j++) printf("%d ",x[i][j]); printf("\n"); } } void main(void){ int i,j; int negativ=0; printf("\nIntroduceti elementele matricei:");
32

citire(); clrscr(); printf("\nMatricea introdusa este:\n"); scriere(); for(i=0;i<n;i++) for(j=0;j<m;j++) if(x[i][j]<0) negativ++; if(negativ==0) printf("\nIn matricea introdusa nu exista numere negative"); else printf("\nIn matricea introdusa sunt %d numere negative", negativ); getch(); } 2.4 Operaii cu caractere i iruri de caractere 2.4.1 Caractere Tipul predefinit destinat n C lucrului cu caractere este tipul char. Biblioteca C standard ofer o gam larg de funcii pentru manipularea caracterelor. Aceste funcii sunt grupate n dou categorii: - funcii de citire i scriere a caracterelor (declarate n fiierul stdio.h) - funcii de clasificare i conversie a caracterelor O parte din funciile primei categorii au fost prezentate n Capitolul 2. n continuare vor fi tratate cteva funcii din a doua categorie. int isalfa(int c); Efect: returneaz o valoare nenul dac c este liter. int isdigit(int c); Efect: returneaz o valoare nenul dac c este cifr. int isalnum(int c); Efect: returneaz o valoare nenul dac c este liter sau cifr. int isupper(int c);
33

Efect: returneaz o valoare nenul dac c este liter mare. int islower(int c); Efect: returneaz o valoare nenul dac c este liter mic. int isspace(int c); Efect: returneaz o valoare nenul dac c este un caracter spaiu (spaiu, tab linie nou). int toupper(int c); Efect: returneaz litera majuscul ce corespunde lui c dac c este liter mic. int tolower(int c); Efect: returneaz litera mic ce corespunde lui c dac c este liter mare. 2.4.2 iruri de caractere n limbajul C nu exist prevzut tipul de date ir de caractere. Pentru memorarea irurilor se utilizeaz tablouri cu tipul de baz char, care au pe ultima poziie, ca marc de sfrit a irului, caracterul '\0' Pe lng definirea unui ir de caractere ca i tablou de caractere, n prelucrarea irurilor se utilizeaz deseori tipul pointer la caracter (mai multe despre pointeri n Capitolul 4). De exemplu: char sir[30]; // tablou de 30 de caractere char *psir; // pointer la caracter Astfel, sir este o variabil de tipul tablou de caractere pentru care se rezerv spaiu de memorie de 30 de octei (fiecare caracter este reprezentat pe 8 bii), n timp ce psir este un pointer la caracter care poate primi ca valoare adresa unui caracter (n particular, adresa primului element dintr-un ir de caractere). Trebuie reinut c ori de cte ori de lucreaz cu variabile iruri de caractere ntrun program, trebuie s existe fie o definiie de forma celei prezentate pentru variabila sir, prin care se rezerv static (n timpul compilrii), spaiul de memorie necesar variabilei, fie s se aloce dinamic memoria necesar. Neexistnd tipul ir de caractere, n C nu sunt prezeni nici operatorii pentru iruri. n schimb, biblioteca standard conine numeroase funcii pentru
34

prelucrarea irurilor de caractere. O parte a funciilor pentru citirea / scrierea irurilor, declarate n fiierul antet stdio.h, au fost prezentate n Capitolul 2. n continuare vor fi trecute n revist funciile pentru compararea, copierea, concatenarea irurilor, .a.m.d. Acestea sunt declarate n fiierul string.h. Ele primesc adresele irurilor prelucrate prin intermediul parametrilor de tipul pointer la caracter. int strcmp(char *s1, char *s2); Efect: compar irurile s1 i s2 i returneaz un numr mai mic dect zero dac s1<s2, 0 dac s1 egal cu s2 i un numr mai mare dect zero dac s1>s2. int strncmp(char *s1, char *s2, int n); Efect: identic cu strcmp, dar se compar irurile s1 i s2 pentru cel mult n caractere. char *strcpy(char *d, char *s); Efect: copiaz irul surs s n irul destinaie d i returneaz adresa irului destinaie. char *strncpy(char *d, char *s, int n); Efect: copiaz maxim n caractere de la surs s la destinaia d i returneaz adresa irului destinaie. int strlen(char *s); Efect: returneaz lungimea irului fr a numra caracterul terminator ('\0'). char *strcat(char *d, char *s); Efect: concateneaz cele dou iruri i returneaz adresa irului rezultat. Se va prezenta n continuare un program care evideniaz lucrul cu irurile de caractere. Se citesc trei iruri a, b i c. Dac a este identic cu b, atunci a va fi nlocuit cu c, altfel, a va fi concatenat cu c. Verificarea egalitii irurilor a i b se face utiliznd funcia strcmp(). Conform descrierii anterioare, aceast funcie returneaz valoarea 0 dac irurile sunt egale. Deoarece zero este asociat valorii logice false, n if-ul n care se testeaz egalitatea celor dou iruri trebuie s apar valoare negat a rezultatului returnat de strcmp(). Negarea se face utiliznd '!'.
35

#include <stdio.h> #include <conio.h> #include <string.h> char a[10]; char b[10]; char c[10]; void main(void){ printf("\na= "); scanf("%s", a); printf("\nb= "); scanf("%s", b); printf("\nc= "); scanf("%s", c); if(!strcmp(a,b)) //daca sunt identice, adic dac strcmp() returneaz 0 strcpy(a,c); else strcat(a,c); printf("\nSirul a devine: %s",a); getch(); } 2.5 Tem 1. Se citesc dou numere reale x i y. Se cere calcularea i afiarea valorii expresiei E(x,y) definit astfel:
x e + | 5 y 7 | , x > y E ( x, y ) = | 3 x y | +10, x y

2. Se citete o matrice ptrat cu n linii. S se verifice dac: - matricea este unitate (1 pe diagonala principal, 0 n rest) - este simetric (a[i][j] = a[j][i]) - este antisimetric (a[i][j] = -a[j][i])
36

3. Se citete un ir de caractere de la tastatur. Se cere s se afieze irul de la dreapta la stnga. (Indicaie: irul fiind un tablou de caractere, poate fi parcurs ca un tablou obinuit folosind indicii, deci se poate accesa fiecare caracter al irului).

CAPITOLUL 3 3.1 Prelucrarea fiierelor


Ce se ntmpl cu datele din memoria calculatorului atunci cnd se ntrerupe curentul, accidental sau deliberat? Pur i simplu ele dispar. Vulnerabilitatea datelor din RAM este cauza unor preocupri permanente. n definitiv, nimeni nu dorete s introduc fiecare dat utilizat ori de cte ori se nchide calculatorul. Din acest motiv, este necesar o modalitate de memorare permanent a datelor, pus la dispoziie de discul calculatorului. Suprafaa magnetic a discului poate s memoreze combinaiile binare din RAM pentru o perioad lung de timp. Discul unui calculator poate fi imaginat ca un fiet cu dosare. La nivelul cel mai de jos sunt pstrate fiierele. Un fiier este o colecie de date de un anumit tip i care poart un nume. Mai multe fiiere pot fi grupate mpreun ntr-un director. Un director poate s conin, pe lng fiiere, i directoare, formndu-se o ierarhie. Chiar dac exist o varietate mare de fiiere disponibile (documente, foi de calcul electronic, baze de date etc), toate acestea se reduc la dou tipuri de baz: text i binar. Fiierele text constau din secvene de caractere ASCII (American Standard Code for Information Interchange, standard special pentru calculatoare care asociaz fiecrui caracter - liter, cifr i semn de punctuaie o valoare numeric diferit, pentru facilitarea portabilitii informaiilor), organizate pe linii. Codul surs al unui program C este un astfel de exemplu. Sfritul fiecrei linii este marcat de caracterul de sfrit de linie \n, iar sfritul fiierului de caracterul sfrit de fiier (EOF). Un fiier binar (denumit i fiier formatat) poate conine orice tip de date care nu au sens n afara rolului pe care l au n programul care le interpreteaz. Pe orice sistem de operare, pentru a lucra cu fiierele, trebuie parcurse patru etape: - pregtirea fiierului pentru utilizare: acest proces este denumit deschiderea unui fiier;
37

- dup deschidere, pot fi citite date din fiier. n acest fel se transfer de pe discul calculatorului n RAM de unde se lucreaz cu ele. Deoarece n timpul lucrului nu se ntmpl nimic cu fiierul iniial de pe disc, acest pas este similar cu fotocopierea coninutului unui dosar din fiet; - dup modificarea datelor copiate n RAM, dac se dorete pstrarea acestor modificri, se rescrie fiierul de pe disc; - dup utilizarea unui fiier, acesta trebuie nchis pentru a putea fi utilizat i de altcineva. n C, spre deosebire de alte limbaje de programare, operaiile de intrare/ieire nu se efectueaz pe baza unui tip de date i a unor funcii ncorporate n limbaj. Deoarece aceste prelucrri depind de caracteristicile sistemului de operare, ele sunt furnizate de funcii din biblioteca standard a limbajului. Funciile utilizate pn acum (de exemplu getchar(), printf(), gets(), scanf() etc.) opereaz cu echipamentele standard: monitorul i tastatura. n continuare, vom trece n revist funciile care permit transferul datelor memorate n fiierele de pe disc. Prin operaia de deschidere a fiierului, i se asociaz acestuia un canal de intrare/ieire sau flux (stream), se creaz o variabil structur de tipul FILE (definit n stdio.h) i se rezerv spaiu pentru zona tampon utilizat la transferul datelor. Structura de tip FILE conine informaii dependente de implementare: adresa i lungimea zonei tampon de date, modul de utilizare a fiierului, indicatorul de sfrit de fiier, indicatorul de poziie etc. Deschiderea/nchiderea fiierelor La lansarea n execuie a unui program se deschid trei fiiere cu canale de tip text: - stdin: pentru intrarea standard, asociat implicit tastaturii; - stdout: pentru ieirea standard, asociat implicit ecranului monitorului; - stderr: ieirea standar pentru erori, asociat implicit ecranului monitorului. Deschiderea fiierului se face folosind funcia fopen() cu prototipul urmtor: FILE *fopen (const char *filename, const char *mode); Efect - se deschide fiierul cu numele filename (care poate include i specificarea cii), i se asociaz un stream i se returneaz un pointer (o variabil care conine adresa din memorie a unei alte variabile) ctre structura FILE creat, sau NULL dac fiierul nu poate fi deschis. Acest pointer este folosit n operaiile ulterioare ale fiierului.
38

Mod r

Scop Acces exclusiv pentru citire. Dac fiierul nu exist, funcia fopen() cu acest mod va returna NULL. Acces exclusiv pentru scriere. Dac fiierul nu exist, funcia fopen() cu acest mod l va crea. Dac fiierul exist deja, coninutul anterior va fi ters. Acces pentru adugare de date. Dac fiierul nu exist, funcia fopen() cu acest mod l va crea. Dac fiierul exist deja, noile date vor fi adugate la sfritul vechiului coninut. Acces pentru citire i scriere. Dac fiierul nu exist, funcia fopen() cu acest mod l va crea. Dac fiierul exist deja, se va scrie peste coninutul anterior, pornind de la nceputul fiierului. Acces pentru citire i scriere. Dac fiierul nu exist, funcia fopen() cu acest mod l va crea. Dac fiierul exist deja, coninutul anterior va fi ters. Acces pentru citire i adugare de date. Dac fiierul nu exist, funcia fopen() cu acest mod l va crea. Dac fiierul exist deja, noile date vor fi adugate la sfritul vechiului coninut.

r+

w+

a+

Simbolurile din tabelul de mai sus sunt destinate deschiderii fiierelor n modul text. Pentru deschiderea n modul binar, se va aduga litera b la aceste coduri. nchiderea unui fiier deschis cu fopen() se realizeaz cu funcia fclose(), al crei prototip este urmtorul: int flose(FILE *fp); Argumentul funciei, fp, este pointerul returnat de funcia fopen(). Prin nchiderea fiierului, se elibereaz zona tampon alocat acestuia. Funcia
39

returneaz valoarea 0, dac operaia de nchidere s-a efectuat cu succes, respectiv EOF (-1) dac a avut loc o eroare la nchidere. n anumite situaii, este important de aflat momentul n care se ajunge la sfritul fiierului. Acest lucru se realizeaz cu ajutorul funciei feof(), al crei prototip este: int feof(FILE *fp); Argumentul funciei, fp, este pointerul returnat de funcia fopen(). Funcia returneaz o valoare nenul n cazul n care s-a ntlnit sfritul fiierului, sau 0 n caz contrar. Pentru a afla, la un moment dat, poziia indicatorului de fiier se folosete funcia fgetpos() cu prototipul: int fgetpos(FILE *fp, long int *poz); Efect - pentru fiierul fp va nscrie valoarea indicatorului de poziie n variabila adresat prin pointerul poz i returneaz 0 n caz de succes. Dac se dorete modificarea indicatorului de poziie, se utilizeaz funcia setpos() cu prototipul urmtor: int setpos(FILE *fp, const long int *poz); Efect - pentru fiierul fp se atribuie indicatorului de poziie valoarea variabilei adresat prin pointerul poz i returneaz 0 n caz de succes. Repoziionarea indicatorului de poziie se poate face i cu funcia fseek(): int fseek(FILE *fp, long offset, int whence); Pentru fiierul indicat de pointerul fp, repoziioneaz indicatorul fiierului la valoarea whence+offset. whence poate avea urmtoarele valori: SEEK_SET = 0 - indic nceputul de fiier SEEK_CUR = 1 - indic poziia curent SEEK_END = 2 - indic sfritul de fiier Dac se dorete golirea coninutului unui flux de ieire, se folosete funcia fflush(), al crei prototip este: int fflush(FIILE *fp); Efect - funcia va scrie coninutul datelor stocate n memoria tampon n fiierul asociat cu fp. Funciile de intrare/ieire de tip caracter, pentru fiiere, sunt mai puin utilizate. Prototipurile lor sunt:
40

int getc(FILE *fp); Efect - returneaz urmtorul caracter citit din fiierul fp sau EOF, dac se ntlnete sfritul fiierului. int ungetc(int c, FILE *fp); Efect - repune caracterul c n fiierul fp. int putc(int c, FILE *fp); Efect - scrie n fisierul fp caracterul c. Dac operaia a reuit, funcia va returna caracterul scris, altfel va returna EOF. Transferul irurilor de caractere se face folosind urmtoarele funcii: char *fgets(char *s, int n, FILE *fp); Efect - citete maxim n - 1 caractere din fiierul fp, sau pn la \n inclusiv, le depune n sirul s, adaug la sfrit `\0A. Funcia va returna irul rezultat, n caz de reuit, sau NULL, n caz de eroare. int fputs(const char *s, FILE *fp); Efect - funcia scrie irul s n fiierul fp, fr caracterul \0. n caz de eroare, funcia returneaz valoarea EOF. La fel ca i n cazul lucrului cu intrarea/ieirea standard (tastatura, respectiv monitorul), exist funcii de citire i scriere cu format. int fscanf(FILE *fp, const char *format [, adresa, Y]); Efect - funcia citete date din fiierul fp, conform formatului specificat; returneaz numrul cmpurilor de intrare pentru care citirea, conversia i memorarea s-au efectuat corect, sau EOF dac s-a ntlnit sfritul fiierului. int fprintf(FILE *fp, const char *format [, argument Y]); Efect - funcia scrie date, conform formatului, n fiierul fp. Returneaz numrul de octei transferai sau EOF, n caz de eroare. n continuare, sunt prezentate cteva programe exemplificative pentru lucrul cu fiiere.
41

1. S se scrie un program care afieaz lungimea celei mai lungi linii din fiierul text dat. # include <stdio.h> void main(void) { FILE *f; char c; int lg_max, lc_curenta; lg_max = lg_curenta = 0; if(!(f=fopen("Test.txt", "r"))) { puts("Fiierul Test.txt nu poate fi deschis!"); return; } while((c = getc(f)) != EOF) if(c == '\n') { if(lg_max <lg_curenta) lg_max = lg_curenta; lg_curenta = 0; } else lg_curenta++; fclose(f); printf("\n Linia cea mai lung are lungimea %d .", lg_max); } 2. S se scrie un program care copiaz un fiier binar surs n alt fiier binar destinaie. # include <stdio> void main (void) { FILE *fs, *fd; if((fs = fopen("Sursa ", "rb ")) == NULL)
42

{ printf("Fiierul surs nu poate fi deschis!"); exit(1); } if((fd = fopen("Dest ", "wb ")) == NULL) { printf("Fiierul destinaie nu poate fi deschis!"); exit(1); } while(1) { c = getc(fs); if( foef(fs)) break; putc(c, fd); } fclose(fs); fclose(fd); } 3. S se realizeze un program care pstreaz evidena unei grupe de studeni. Datele despre studeni (numele, vrsta, media) se pstreaz sub forma unui fiier text. Programul va permite urmtoarele opiuni: a, A C adugarea unui nou student n fiier; l, L C listarea datelor tuturor studenilor; m, M C modificarea datelor unui student; x, X C terminarea programului. #include <stdio.h> #include <conio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #define LNUME 20 typedef struct{ char nume[LNUME];
43

int varsta; float medie; }student; char fisier[13]; void adauga(void) //Adauga datele unui nou student in fisier { FILE *f; student s; if(!(f = fopen(fisier, "at "))) { puts("\n Fisierul nu poate fi deschis "); return; } printf("\n Dati numele studentului: "); scanf("%s", s.nume); printf("\n Dati varsta studentului: "); scanf("%d", s.varsta); printf("\n Dati media studentului: "); scanf("%f ", s.medie); fflush(stdin); fprintf(f, "%20s %2d %6.2f \n ", s.nume, s.varsta, s.medie); fclose(f); } void listare(void) //Afiseaza datele tuturor studentilor { FILE *f; student s; if(!(f = fopen(fisier, "rt "))) { puts("\n Fisierul nu poate fi deschis "); return; } while(fscanf(f, "%s %d %f ", s.nume, &s.varsta, &s.medie) != EOF) printf("\n %-20s %-2d %-6.2f ", s.nume, s.varsta, s.medie); fclose(f); } void modifica(void)
44

//Modifica datele unui anumit student { FILE *f; student s; int gasit = 0; char n[LNUME]; if(!(f = fopen(fisier, "r+t "))) { puts("\n Fiierul nu poate fi deschis "); return; } printf("\n Dati numele studentului: "); gets(n); while(fscanf(f, "%s %d %f ", s.nume, &s.varsta, &s.medie) != EOF) if(!strcmp(n, s.nume)) { printf("\n %-20s %-2d %-6.2f ", s.nume, s.varsta, s.medie); gasit = 1; break; } if(!gasit) printf("\n Studentul %s nu exista in fisier!", n); else { printf("\n Dati numele studentului: "); scanf("%s", s.nume); printf("\n Dati varsta studentului: "); scanf("%d", s.varsta); printf("\n Dati media studentului: "); scanf("%f ", s.medie); fflush(stdin); fseek(f, -30, SEEK_CUR); fprintf(f, "%20s %2d %6.2f \n ", s.nume, s.varsta, s.medie); } fclose(f); } void meniu(void) //Afiseaza meniul {
45

puts("\n a, A --- adaugare student "); puts("\n m, M --- modificare date student "); puts("\n l, L --- listare studenti "); puts("\n x, X --- parasire program "); } void main(void) { char opt; puts("Dati numele fisierului: "); gets(fisier); while(1) { clrscr(); meniu(); puts("Ce optiune doriti?"); opt = tolower(getch()); switch(opt) { case 'a': adauga(); break; case 'm': modifica(); break; case 'l': listare(); break; case 'x': exit(0); default: puts("Comanda gresita!"); } } } Se poate observa c cele trei operaii principale efectuate asupra fiierului (adugare, listare i modificare) sunt implementate n trei funcii distincte. Funcia adauga() nscrie n fiier datele unui student sub forma unei nregistrri de 30 de octei (20 pentru nume, un spaiu, 2 pentru vrst, un spaiu, 6 pentru medie). Acest lucru este folosit n funcia modific pentru repoziionarea indicatorului de poziie la nceputul nregistrrii care urmeaz s fie modificat.

46

3.2 Compilarea independent a fiierelor. Tipuri abstracte


Programele C foarte mari sunt, de regul, scrise pe mai multe fiiere. Utilitatea unei astfel de structurri este justificat de urmtoarele avantaje: - fiierele pot fi compilate independent. Acest aspect este foarte util n faza de corectare a erorilor sintactice i semantice, cnd doar fiierul eronat trebuie recompilat. Dac ntregul program este plasat ntr-un singur fiier, atunci orice modificare sau corecie impune recompilarea ntregului program; - structurarea programelor pe mai multe fiiere permite simularea mecanismului de ncapsulare a datelor. Variabilele i funciile care au clasa de memorare static pot fi accesate doar n cadrul fiierului n care ele au fost definite. Variabilele i funciile definite cu clasa de memorare extern sunt definite ntr-un alt fiier dect cel curent. pot fi accesate n fiierul curent. Astfel, fiierele apar ca un mecanism de control al vizibilitii obiectelor. Pentru a compila i a lega mpreuna mai multe fiiere, este folosit modul proiect. n mediul Borland C, acest lucru presupune parcurgerea urmtorilor pai: - selectarea opiunii Project i apoi a opiunii Open Project; - fixarea numelui fiierului project; - cu ajutorul opiunii Add Item se adaug fiierele la proiect; - se selecteaz opiunea necesar pentru obinerea programului executabil. Mediul Borland C compileaz automat toate fiierele enumerate i le leag mpreun; - selectnd opiunea Project i apoi obiunea Close Project se ncheie lucrul n acest mod. Spre exemplu, versiunea pe mai multe fiiere a programului de la Exemplu 3, este prezentat n continuare. Programul este mprit n trei fiiere: a) Studenti.h - conine definiiile constantelor i a tipurilor de date folosite n program, precum i declaraiile funciilor folosite; b) Studenti.cpp - conine implementarea funciilor; c) Studenti_main.cpp - conine programul principal. Coninutul fiierului Studenti.h este: #define LNUME 20 typedef struct{ char nume[LNUME]; int varsta;
47

float medie; }student; void adauga(char *fisier); void listare(char *fisier); void modifica(char *fisier); void meniu(void); Coninutul fiierului Studenti.cpp este: #include <stdio.h> #include <conio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #include "Studenti1.h" static FILE *f; static student s; void adauga(char *fisier) //Adauga datele unui nou student in fisier { if(!(f = fopen(fisier, "at "))) { puts("\n Fisierul nu poate fi deschis "); return; } printf("\n Dati numele studentului: "); scanf("%s", s.nume); printf("\n Dati varsta studentului: "); scanf("%d", &s.varsta); printf("\n Dati media studentului: "); scanf("%f", &s.medie); fflush(stdin); printf("\n %-20s %-2d %-6.2f", s.nume, s.varsta, s.medie); fprintf(f, "%s %d %f\n ", s.nume, s.varsta, s.medie); fclose(f); } void listare(char *fisier)
48

//Afiseaza datele tuturor studentilor { if(!(f = fopen(fisier, "rt "))) { puts("\n Fisierul nu poate fi deschis "); return; } while(fscanf(f, "%s %d %f ", s.nume, &s.varsta, &s.medie) != EOF) printf("\n %-20s %-2d %-6.2f ", s.nume, s.varsta, s.medie); fclose(f); } void modifica(char *fisier) //Modifica datele unui anumit student { int gasit = 0; char n[LNUME]; if(!(f = fopen(fisier, "r+t "))) { puts("\n Fisierul nu poate fi deschis "); return; } printf("\n Dati numele studentului: "); gets(n); while(fscanf(f, "%s %d %f ", s.nume, &s.varsta, &s.medie) != EOF) if(!strcmp(n, s.nume)) { printf("\n %-20s %-2d %-6.2f ", s.nume, s.varsta, s.medie); gasit = 1; break; } if(!gasit) printf("\n Studentul %s nu exista in fisier!", n); else { printf("\n Dati numele studentului: "); scanf("%s", s.nume); printf("\n Dati varsta studentului: "); scanf("%d", &s.varsta);
49

printf("\n Dati media studentului: "); scanf("%f", &s.medie); fflush(stdin); printf("\n %-20s %-2d %-6.2f", s.nume, s.varsta, s.medie); fseek(f, -30, SEEK_CUR); fprintf(f, "%20s %2d %6.2f \n ", s.nume, s.varsta, s.medie); } fclose(f); } void meniu(void) //Afiseaza meniul { puts("\n a, A --- adaugare student "); puts("\n m, M --- modificare date student "); puts("\n l, L --- listare studenti "); puts("\n x, X --- parasire program "); } Coninutul fiierului Studenti_main.cpp este: #include <stdio.h> #include <conio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #include "Studenti1.h" char fisier[13]; void main(void) { char opt; puts("Dati numele fisierului: "); gets(fisier); while(1) { //clrscr(); meniu();
50

puts("Ce optiune doriti?"); opt = tolower(getche()); switch(opt) { case 'a': adauga(fisier); break; case 'm': modifica(fisier); break; case 'l': listare(fisier); break; case 'x': exit(0); default: puts("Comanda gresita!"); } } } Pot fi fcute urmtoarele comentarii: - variabilele i funciile care nu sunt declarate ca statice, sunt implicit externe; - exist i fiiere antet declarate de utilizator. Pentru a fi vizibile ntr-un program, ele trebuie incluse cu ajutorul clauzei #include, dar marcat de simbolurile " " i nu de simbolurile < >, ca pentru bibliotecile standard. - fiierul Studenti.h este vzut ca un tip de date abstract, care este o entitate ce poate fi manipulat doar prin operaiile care definesc acel tip. Avantajul folosirii unor astfel de tipuri const n faptul c modul de implementare a operaiilor descrise poate fi schimbat fr, ns, s afecteze restul programului.

3.3 Tem
1. Scriei o funcie care copiaz coninutul unui fiier surs (de tip text) ntr-unul destinaie (de tip text). Funcia are ca parametri numele celor dou fiiere: sursa i destinaia. Funcia returneaz valoarea 1 dac operaia a decurs normal, altfel returneaz valoarea 0. De asemenea, se cere i coninutul funciei main() care apeleaz aceast funcie. 2. Se consider c fiierul Intrare.txt conine numere ntregi, cte unul pe rnd. Se cere s se citeasc numerele din fiier, s se determine numrul maxim i numrul minim, precum i media aritmetic a tuturor numerelor citite. Rezultatul va fi scris n fiierul Iesire.txt sub forma: Au fost citite n numere Numrul minim este: min
51

Numrul maxim este: max Media aritmetic a tuturor numerelor citite este: media 3. n fiierul Sudenti.in sunt memorate date sub forma: <Nume student>; <Prenume student>;<Grupa>;<Nr_de_absente>. Se cere: a) S se determine studentul cu cele mai multe absene, precum i cu cele mai puine. b) S se determine numrul de absene pentru fiecare grup n parte. c) S se scrie n fiierul Exmatr.out lista acelor studeni care au peste 100 de absene. Indicaie: programul va avea un meniu principal care va conine opiunile: a, A C adugare studeni la fiierul Studenti.in; l, L C listarea tuturor studenilor; m, M C gsirea studentului cu cele mai multe absene; p, P C gsirea studentului cu cele mai puine absene; g, G C listarea studenilor dintr-o anumit grup; e, E C scrierea fiierului Exmatr.out; x, X C prsirea programului. De asemenea, se cere ca programul s fie mprit pe mai multe fiiere.

CAPITOLUL 4
4.1 Elemente de baz despre pointeri Indiferent de tipul de date coninut de variabile, ele au o trstur comun fundamental: orice variabil este un simplu nume pe care compilatorul C l asociaz cu o anumit adres din memoria calculatorului. De aceea, n plus fa de nume, variabilele au o adres i un coninut la acea adres. Pentru un calculator, tot ce se gsete n memoria sa sunt date, indiferent dac reprezint coninutul unei variabile sau adresa ei. Aceasta nseamn c adresa unei variabile poate fi tratat ca oricare alt tip de dat: poate fi regsit, prelucrat, iar rezultatele pot fi memorate. Acesta este modul exact n care programul acioneaz atunci cnd lucreaz cu coninutul variabilelor. Acest lucru se poate realiza i n mod explicit utilizndu-se pointerii.
52

O variabil de tip pointer poate fi privit ca o adres ntr-o agend: ea indic localizarea real a unei alte variabile, specificate. Un pointer este o variabil care conine adresa din memoria a unei alte variabile. Se mai spune c pointerii "indic" o alt variabil, deoarece un program poate utiliza coninutul unui pointer pentru a regsi acea variabil. Declararea pointerilor Un pointer este un alt tip de variabil. Prin urmare, nainte de a fi utilizat, acesta trebuie declarat. El are un nume i un tip de date, ca orice alt variabil. Singura diferen la declararea unui pointer fa de celelalte variabile este apariia operatorului asterisc (*). Forma general a instruciunii de declarare a unui pointer este: nume_tip *nume_pointer; Asteriscul este foarte important; el indic limbajului C c s-a creat un pointer i nu o variabil. n C asteriscul este un operator de indirectare. Atunci cnd se creeaz un pointer, se precizeaz ctre ce tip de variabil indic el. Urmtoarea instruciune creaz un pointer n virgul mobil: float *p; Declararea unui pointer se poate combina cu declararea altor variabile, astfel: float *p, nota; S-a creat un pointer p al unei variabile n virgul mobil i o variabil obinuit n virgul mobil, numit nota. Iniializarea pointerilor Este o operaie foarte important i trebuie realizat corect. Ea presupune stabilirea unei valori iniiale pentru un pointer. Iniializarea pointerilor se realizeaz cu o instruciune de atribuire, utiliznd operatorul de adres (&). Forma general pentru iniializarea unui pointer este: nume_pointer = &nume_variabil; Semnul & determin adresa variabilei. Acest rezultata este apoi memorat de ctre pointer. Dup executarea instruciunii, pointerul indic localizarea variabilei. Urmtoarea linie determin ca pointerul p s indice variabila n virgul mobil nota. p = &nota; De reinut faptul c pointerul i variabila trebuie s fie de acelai tip.
53

Dup executarea unei instruciuni ca cea de mai sus, vor exista dou variabile. Una este un pointer al unei variabile n virgul mobil, cealalt este o variabil n virgul mobil. Pointerul conine adresa din memorie a variabilei n virgul mobil. Utilizarea pointerilor Un pointer poate fi utilizat n locul variabilei pe care o indic. De asemenea, poate fi reiniializat oricnd pentru a indica spre o alt variabil. Numele unui pointer poate fi utilizat n orice expresie care permite introducerea unui nume de variabil. Dac intereseaz chiar variabila indicat, se va utiliza din nou operatorul de indirectare pentru a meniona c referirea se face la variabil i nu la coninutul pointerului. Urmtoarele dou instruciuni sunt aproape identice, doar c n linia a doua a aprut operatorul de indirectare: p=125; *p=125; Prima instruciune memoreaz valoarea 125 n pointerul p. Acum pointerul indic spre locaia 125 a octetului din memorie - cine tie ce se afl acolo! A doua instruciune pune valoarea 125 n variabila indicat de pointerul p. Utiliznd operatorul de indirectare mpreun cu pointerul, este acelai lucru cu utilizarea numelui variabilei. Astfel, n urmtorul grup de instruciuni, ultimele dou au acelai efect: ambele pun valoarea 9.50 n variabila nota. float *p, nota; p = &nota; *p = 9.50; nota = 9.50; n afara instruciunii de declarare a unei variabile, folosit de fapt pentru a crea un pointer, operatorul de indirectare se refer la coninutul variabilei - nu la coninutul pointerului nsui (coninutul real al unui pointer este o adres, nu o dat). Relund analogia cu agenda de adrese, un pointer este una din adresele din agend, de exemplu prima de la litera D. Coninutul de acolo este o adres. La acea adres se afl persoana cutat (coninutul variabilei).

54

4.2 Alocarea dinamic a memoriei Variabilele sunt de dou tipuri: statice i dinamice. Variabilele statice sunt referite prin nume i li se aloc memorie la nceput, n faza de compilare a programului. Variabilele dinamice nu se refer prin nume ci prin intermediul variabilelor pointer ataate i li se aloc memorie n mod dinamic, la execuia programului, ntr-o parte a memoriei numit heap. Memoria ocupat de o asemenea variabil trebuie eliberat explicit de programator prin apelul unei funcii n momentul n care variabila nu mai e folosit. Alocarea memoriei pentru o variabil dinamic se face utiliznd funcia malloc. Efectul ei este acela c n memoria heap se aloc un bloc de o dimensiune specificat ca parametru al funciei. n cazul n care alocarea s-a fcut cu succes, funcia returneaz un pointer ctre noul bloc alocat. Dac nu exist suficient spaiu n memorie, funcia va returna valoarea NULL. Eliberarea memoriei se realizeaz prin apelul funciei free. Aceasta primete ca parametru un pointer ctre un bloc de memorie i nu returneaz nimic.

Structuri dinamice de date Sunt structuri de date a cror numr de componente se modific n timpul execuiei programului. Prin modificare se nelege faptul c dimensiunea structurii crete i / sau scade. n structurile dinamice de date pe lng informaia util e nevoie s existe i o informaie de legtur care s permit realizarea nlnuirii unui element cu elementul urmtor, eventual cu cel precedent. Un element dintr-o structur dinamic de date poate fi reprezentat schematic astfel:
Informaie util Informaie de legtur

elementul precedent urmtor

elementul

Figura 1. Element al unei structuri dinamice Cele mai folosite structuri dinamice de date sunt: stiva, coada i lista simplu nlnuit.
55

Stiva Este o list liniar de tip special n care adugarea sau scoaterea unui element se face la un singur capt al listei, numit vrful stivei. Elementul introdus primul n stiv se numete baza stivei. Informaia de legtur a fiecrui element reprezint adresa elementului pus anterior n stiv. Excepie face baza stivei a crei informaie de legtur este NULL. Principiul de acionare asupra stivei este LIFO - Last In First Out (ultimul intrat - primul ieit). Coada Este o list liniar n care elementele se adaug la un capt al ei (sfrit) i se suprim la cellalt capt (nceput) i funcioneaz pe principiul FIFO First In First Out (primul intrat - primul ieit)

Lista simplu nlnuit Este o structur dinamic n care toate elementele sunt de acelai tip, sunt concomitent n memorie i ntre ele exist o relaie de ordine. Lista este referit prin intermediul primului sau element (care va fi numit rdcin) Asupra unei liste simplu nlnuite se pot efectua urmtoarele operaii: a) Parcurgerea - se face pornind de la rdcin, prelucrnd informaia util a fiecrui element i trecnd la elementul urmtor folosind informaia de legtur. Schematic, lista arat astfel:

........... rdcin Figura 2. Structur dinamic de tip list simplu nlnuit

b) Inserarea - reprezint introducerea unui nou nod n list. Aici trebuie tratate separat dou cazuri: inserarea la nceputul listei sau dup un nod dat.
56

- Inserarea unui nod la nceputul listei - n acest caz, pointerul rdcin se va modifica, indicnd spre nodul introdus. Paii care trebuie efectuai sunt sugerai de figura 3. Primul pas (1) const n efectuarea legturii de la noul nod la rdcin, dup care, n pasul (2) se va tia legtura dintre rdcin i fostul prim nod i se va "ndrepta" pointerul rdcin astfel nct el s indice spre noul nod. nod nou

(1) (2) (2) rdcin Figura 3. Inserarea unui nod la nceputul listei simplu nlnuite ...........

- Inserarea unui nod dup un nod dat. Legturile care trebuie fcute n acest caz sunt artate n figura 4. Se presupune c inserarea se face dup nodul q2. Date fiind legturile care exist, se cunoate i nodul care urmeaz dup q2. Fie acesta q1. n primul pas se realizeaz legtura de la noul nod la nodul q1, iar pasul (2) determin ca nodul q2 s nu mai indice spre q1 ci spre nodul introdus q2 (2) ........... rdcin (2) (1) nod nou Figura 4. Inserarea unui nod dup un nod, dat ntr-o list simplu nlnuit q1

c) tergere - este necesar i aici tratarea difereniat a celor dou cazuri: tergerea nodului de la nceputul listei sau tergerea unui alt nod.
57

- tergerea primului nod al listei este descris n figura 5. n urma tergerii, rdcina va indica spre al doilea nod din structur, legtura spre primul fiind distrus.

........... rdcin Figura 5. tergerea primului nod dintr-o list simplu nlnuit - tergerea unui nod din interiorul listei poate fi urmrit n figura 6. Se presupune c nodul care se dorete a fi ters este q1. Parcurgerea trebuie fcut astfel nct s se rein i nodul precedent, q2. Pentru a realiza excluderea nodului q1 din list, trebuie realizat legtura de la nodul q2 la nodul care urmeaz dup q1 (nod a crui adres se afl n informaia de legtur a nodului q1). q2 q1 ........... rdcin Figura 6. tergerea unui nod din interiorul unei liste simplu nlnuite

n capitolul urmtor vor fi implementate operaiile asupra unei liste simplu nlnuit, lundu-se un exemplu concret.

4.3 Implementarea listelor simplu nlnuite Se va realiza un program care rspunde la urmtoarele comenzi: 'A' citete o linie de forma identificator numr unde numr este un numr ntreg. Ca rezultat, se reine n eviden identificatorul mpreun cu numrul asociat. Identificatorii vor fi pstrai n eviden n mod ordonat.
58

'T' citete o linie ce conine un identificator. Dac acesta apare n eviden, se tiprete valoarea asociat lui; n caz contrar se tiprete un mesaj de eroare. 'S' citete o linie ce conine un identificator i l terge din eviden. 'L' se tipresc identificatorii din eviden n ordine alfabetic, mpreun cu valorile asociate lor. '+' - se citesc dou linii, fiecare coninnd un identificator. n cazul n care ambii se afl n eviden, se tiprete suma lor. n caz c unul sau ambii identificatori lipsesc din eviden, se afieaz un mesaj de eroare. '-' - se citesc dou linii, fiecare coninnd un identificator. n cazul n care ambii se afl n eviden, se tiprete diferena lor. n caz c unul sau ambii identificatori lipsesc din eviden, se afieaz un mesaj de eroare. '*' - se citesc dou linii, fiecare coninnd un identificator. n cazul n care ambii se afl n eviden, se tiprete produsul lor. n caz c unul sau ambii identificatori lipsesc din eviden, se afieaz un mesaj de eroare. '/' - se citesc dou linii, fiecare coninnd un identificator. n cazul n care ambii se afl n eviden, se tiprete rezultatul mpririi lor. n caz c unul sau ambii identificatori lipsesc din eviden, se afieaz un mesaj de eroare. 'F' se termin programul Sursa programului #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <conio.h> #define Max 10 //lungimea maxima a identificatorilor typedef struct elem{ //tipul pentru un nod al liatei char *id; int valoare; struct elem *urm; } nod; nod *radacina = NULL; //pastreaza inceputul listei /*Functia cautare cauta sirul s in lista al carei inceput
59

e indicat de parametrul lista. Daca sirul apare in lista atunci functia returneaza pointerul la nodul respectiv, in caz contrar returneaza NULL*/ nod *cautare(nod *lista, char *s){ nod *q1; for(q1=lista;q1!=NULL&&strcmp(q1->id,s)<0;q1=q1->urm); /*cautare1*/ if(q1!=NULL && strcmp(q1->id,s)==0) /*cautare2*/ /*daca sirul s a fost gasit in lista*/ return q1; else return NULL; } /*Functia listare parcurge lista si pentru fiecare nod afiseaza identificatorul memorat si valoarea atasata lui. Deoarece lista este ordonata, afisarea identificatorilor este in ordine alfabetica*/ void listare(void){ nod *q; for(q=radacina;q!=NULL;q=q->urm) printf("identificator: %s valoare %d \n", q->id, q->valoare); } /*Functia stergere elemina din lista indicata de pointerul lista, nodul ce are campul id egal cu argumentul s*/ nod *stergere(nod *lista, char *s){ nod *q1, *q2; for(q1=q2=lista;q1!=NULL&&strcmp(q1->id,s)<0;q2=q1,q1=q1->urm); /*stergere1*/ /*se parcurge lista cautandu-se nodul avand campul id egal cu sirul s*/ if(q1!=NULL && strcmp(q1->id,s)==0){/*stergere2*/ //daca s-a gasit nodul if(q1!=q2)/*stergere3*/ //daca nodul nu este la inceputul listei q2->urm=q1->urm; /*se elimina nodul din lista*/
60

else /*stergere4*/ //nodul apare la inceputul listei lista=lista->urm; free (q1->id); free (q1); /*se elibereaza memoria ocupata de nodul sters*/ return lista; //returneaza pointerul catre inceputul listei modificate } else{ printf("Eroare:identificatorul %s nu apare in lista\n", s); return lista; } } /*Functia introducere insereaza un nod in lista ordonata, indicata de parametrul lista. Lista ramane ordonata si dupa inserare. Nodul nou are campul id egal cu sirul indicat de parametrul s, iar campul valoare egal cu parametrul v. Functia returneaza pointerul catre inceputul listei modificate.*/ nod *introducere(nod *lista, char *s, int v){ nod *q1, *q2, *aux; if((aux = (nod*) malloc (sizeof(nod)))==NULL || (aux->id = (char*) malloc (strlen(s)+1))==NULL){ /*introducere1*/ /*daca nu e memorie suficienta se da un mesaj de eroare si executia e incheiata*/ printf("Eroare: memorie insufienta\n"); exit(1); } else{ strcpy(aux->id,s); //se salveaza s in nodul nou aux->valoare = v; // se salveaza v in noul nod } /* noul nod este inserat in lista ordonata astfel incat ea ramane ordonata si dupa inserare. Lista este parcursa cautandu-se primul nod avand campul id mai mare sau egal cu s*/
61

for(q1=q2=lista; q1!=NULL && strcmp(q1->id,s)<0; q2=q1, q1=q1>urm); /*introducere2*/ if(q1!=NULL && strcmp(q1->id,s)==0){ /*introducere3*/ /* daca in lista apare un nod avand campul id egal cu s, atunci se afiseaza un mesaj de eroare si se pastreaza lista nemodificata */ printf("Eroare: %s apare in tabela \n", s); return lista; } else if(q1!=q2){ /*introducere4*/ //daca inserarea nu se face la inceputul listei q2->urm=aux; aux->urm=q1; return lista; } else{ /*introducere5*/ //inserarea se face la inceputul listei aux->urm=lista; return aux; } } /*Functia getlin citeste urmatoarea linie de la tastatura si recunoaste identificatorul si valoarea asociata lui. Identificatorul este returnat in parametrul s, iar valoarea in parametrul val*/ void getlin(char *s, int *val){ int i=0,j; char temp[Max]; gets(temp); //citeste urmatoarea linie de la tastatura /*getlin1*/ //initializeaza valorile argumentelor s[i] = '\0'; *val=0; while(temp[i]!='\0')/*getlin2*/
62

//atata timp cat nu a fost atins sfarsitul sirului if(isalpha(temp[i])){ /*getlin3*/ //daca incepe un identificator j=0; while(isalnum(temp[i])) s[j++]=temp[i++]; /*getlin4*/ //memoreaza identificatorul in s s[j]='\0'; // memoreaza sfarsitul de sir /*getlin5*/ } else if(isdigit(temp[i]))/*getlin6*/ //daca incepe un numar while(isdigit(temp[i])){ /*getlin7*/ //calculeaza valoarea numarului *val=*val*10+temp[i]-'0'; /*getlin8*/ i++; } else i++; //altfel, se trece peste caracterul curent /*getlin9*/ } /*Functia comanda_a realizeaza functionalitatea comenzii a*/ void comanda_a(void){ int val; char s[Max]; getlin(s, &val); /*citeste o linie de la tastatura*/ if(strlen(s)!=0) //daca linia e corecta radacina=introducere(radacina, s, val); else printf("Eroare: linie incorecta\n"); } /*Functia comanda_t realizeaza functionalitatea comenzii t*/ void comanda_t(void){ int val; char s[Max];
63

nod *p; getlin(s, &val); /*citeste o linie de la tastatura*/ if(strlen(s)!=0) //daca linia e corecta if((p=cautare(radacina,s))!=NULL)/*cauta nodul in lista*/ printf("Identificator: %s Valoare: %d \n", p->id, p->valoare); else printf("Eroare: Identificator nedefinit \n"); else printf("Eroare: linie incorecta\n"); } /*Functia comanda_s realizeaza functionalitatea comenzii s*/ void comanda_s(void){ int val; char s[Max]; getlin(s, &val); /*citeste o linie de la tastatura*/ if(strlen(s)!=0) //daca linia e corecta radacina=stergere(radacina, s); /*sterge nodul din lista*/ else printf("Eroare: linie incorecta\n"); } /*Functia comanda_oper executa operatiile legate de comenzile +, -, *, / Se citesc doi operanzi si se executa operatia dorita Rezultatul esta afisat.*/ void comanda_oper(char c){ char s1[Max], s2[Max]; int val; nod *p1, *p2; getlin(s1, &val); /*se citeste primul operand*/ /*comanda_oper1*/ getlin(s2, &val); /*se citeste al doilea operand*/ /*comanda_oper2*/ if((strlen(s1)!=0)&&(strlen(s2)!=0))
64

if(((p1=cautare(radacina,s1))!=NULL)&&((p2=cautare(radacina,s2))!= NULL)){ /*comanda_oper3*/ /*se verifica daca operanzii apar in lista*/ switch(c){ /*in functie de tipul comenzii*/ case '+': val = p1->valoare + p2->valoare; break; case '-': val = p1->valoare - p2->valoare; break; case '*': val = p1->valoare * p2->valoare; break; case '/': if(p2->valoare!=0) val = p1->valoare / p2->valoare; else{ printf("Eroare: Impartire la 0 \n"); return; } break; } printf("Rezultatul operatiei este: %d \n", val); } else printf("Operand nedefinit \n"); else printf("Eroare: linie eronata \n"); } /*Functia meniu afiseaza meniul programului, citeste comanda si apeleaza subrutina corespunzatoare.*/ void meniu(void){ char o; while(1){ /*meniu1*/ clrscr(); /*se afiseaza meniul programului*/ printf("a - adauga in evidenta un id si valoarea asociata \n");
65

printf("t - tipareste valoarea asociata unui id \n"); printf("s - sterge un id \n"); printf("l - listeaza identificatorii si valorile asociate lor \n"); printf("+ - calculeaza suma pentru doi identificatori \n"); printf("- - calculeaza suma pentru doi identificatori \n"); printf("* - calculeaza suma pentru doi identificatori \n"); printf("/ - calculeaza suma pentru doi identificatori \n"); printf("f - termina programul \n"); printf("\n Introduceti optiunea:"); o = getchar(); getchar();/*meniu2*/ switch(tolower(o)){ /*meniu3*/ case 'a':printf("Introduceti, pe o singura linie, identificatorul si valoarea asociata \n"); comanda_a(); break; case 't':printf("Introduceti, pe o singura linie, identificatorul \n"); comanda_t(); break; case 's':printf("Introduceti, pe o singura linie, identificatorul \n"); comanda_s(); break; case 'l': listare(); break; case '+': case '-': case '*': case '/': printf("Introduceti, pe doua linii separate, identificatorii \n"); comanda_oper(o); break; case 'f': return; default: printf("Eroare: Comanda inexistenta \n"); } getchar(); } } /*Programul principal apeleaza functia meniu.*/ void main(void){
66

meniu(); } Comentarea programului Programul memoreaz identificatorii i valorile asociate lor ntr-o eviden, pe care o folosete conform funcionalitii cerute. Evidena este realizat printr-o list simplu nlnuit ordonat. Fiecare nod al listei conine un cmp pentru pstrarea identificatorului, care este indicat de char *id, un cmp pentru memorarea valorii asociate identificatorului int valoare i un cmp pentru a crea nlnuirea cu nodul urmtor din list struct elem *urm. Lista este ordonat cresctor funcie de cmpurile id ale nodurilor. Programul principal apeleaz funcia meniu. Aceasta afieaz n mod repetat, datorit instruciunii while(1) de pe linia /*meniu1*/, opiunile pe care le ofer programul. n continuare se citete de la tastatur opiunea dorit (linia /*meniu2*/). n funcie de opiunea aleas (linia /*meniu3*/) se selecteaz rutina ce implementeaz funcionalitatea dorit. Rutina comanda_a citete de la tastatur un identificator i valoarea asociat lui, dup care l introduce n eviden. Citirea se face apelnd funcia getlin. n cazul n care lungimea identificatorului citit este 0, se afieaz un mesaj de eroare. n caz contrar, identificatorul i valoarea asociat lui sunt introduse n lista ordonat prin apelarea rutinei introducere. Funcia comanda_t citete un identificator folosind getlin. Dac lungimea identificatorului este 0, atunci se afieaz mesajul c linia e incorect. Dac ns lungimea e diferit de 0, atunci identificatorul este cutat n list (apelnd funcia caut). Dac identificatorul apare n list, atunci se afieaz valoarea asociat lui, n caz contrar se tiprete un mesaj de eroare adecvat. Comanda_s citete o linie ce conine un identificator (folosind getlin). Dac linia citit este corect (adic lungimea identificatorului este nenul), atunci identificatorul este eliminat din eviden prin apelul lui sterge. Comanda_oper citete doi identificatori (liniile /*comanda_oper1*/ i /*comanda_oper2*/) i efectueaz o operaie aritmetic cu aceti operanzi. Felol operaiei dorite este transmis prin parametrul char c al rutinei comanda_oper. Dac identificatorii sunt coreci (au lungimile diferite de 0) atunci ei sunt cutai n eviden (linia /*comanda_oper3*/). Dac ambii apar, atunci n funcie de c, se efectueaz operaia dorit, iar rezultatul este afiat. Citirea unei linii de la tastatur se face prin rutina getlin. Linia /*getlin1*/ citete n variabila local temp o linie de la tastatur. Ciclul while de pe linia/*getlin2*/ parcurge tabloul temp. Dac gsete o litera (linia /*getlin3*/),
67

atunci nseamn c a gsit nceputul unui identificator. Ciclul whilw de pe linia /*getlin4*/ preia ntregul identificator i l memoreaz n tabloul al crui nceput este indicat de parametrul char s. Linia /getlin5*/ memoreaz caracterul \0 ce indic sfritul identificatorului. Dac la parcurgerea lui temp este ntlnit o cifr (linia /*getlin6*/), atunci a fost descoperit nceputul numrului care apare n linia de text. Acest numr este preluat prin ciclul while de pe linia /*getlin7*/, calculndu-se simultan si valoarea numeric asociat lui. Linia /*getlin8*/ face conversia din irul de caractere asociat numrului, n valoarea lui. Calcularea valorii numerice se face folosind variabila ntreag a crei adres este transmis prin parametrul int val. Linia /getlin9*/ sare peste acele caractere care nu fac parte din identificator sau numr. Funcia cautare parcurge lista al crei nceput este transmis prin parametrul nod*lista, cutnd nodul al crui identificator este egal cu irul indicat de parametrul char s. Dac un astfel de nod este gsit, atunci cautare returneaz pointerul ctre el. n caz contrar, returneaz NULL. Parcurgerea listei se face prin ciclul for de pe linia /*cautare1*/. Variabila q1 este iniializat astfel nct s indice nceputul listei. La sfritul fiecrei iteraii, q1 este mutat ctre urmtorul nod. Condiia de reluare a ciclului este ca lista s nu fi fost parcurs n ntregime (condiia q1!=NULL de pe linia /*cautare1*/) i identificatorul s nu fi fost gsit. Deoarecelista este ordonat cresctor este suficient s fie efectuat testul strcmp(q1->id,s)<0 (linia /*cautare1*/). Linia /*cautare2*/ verific dac identificatorul a fost gsit. Pentru asta este necesar ca q1!=NULL (altfel s-a ajuns la sfritul listei fr a-l gsi) i irul memorat n nodul curent s fie egal cu cel indicat de s. Rutina listare parcurge toate nodurile din list i, pentru fiecare, afieaz identificatorul reinut i valoarea ataat lui. Funcia stergere elimin, din lista indicat de parametrul nod lista, nodul al crui identificator este egal cu irul indicat de parametrul char s. Funcia returneaz pointerul ctre nceputul listei modificate. Dup eliminare lista rmne ordonat. Pentru eliminarea unui nod sunt folosii doi pointeri care parcurg lista. q1 indic nodul curent, n timp ce q2 pe cel anterior lui. Acest lucru este necesar deoarecela eliminarea nodului indicat de q1, nlnuirea urm a nodului anterior (care este indicat de q2) se modific spre nodul urmtor lui q1. Ciclul for de pe linia /stergere1/ parcurge lista. Pointerii q1 i q2 sunt iniializai s indice spre nceputul listei din care se face tergerea. La fiecare reluare a ciclului, q2 ia vechea valoare a lui q1, n timp ce q1 se mut spre nodul urmtor. Condiia de reluare a ciclului for este ca lista s nu fi fost parcurs n ntregime (q1!=NULL) i nodul s nu fi fost gsit (strcmp(q1->id,s)<0). Dac nodul a fost gsit (linia /*stergere2*/), atunci se verific dac el este sau nu primul nod al listei. Dac nodul nu este chiar nceputul listei (linia
68

/*stergere3*/) atunci nlnuirea urm a nodului anterior celui indicat de q1 se modific spre nodul urmtor celui indicat de q1. Dac ns nodul indicat de q1 este chiar primul nod din list, atunci variabila lista este schimbat spre al doile nod. Memoria ocupat de nodul eliminat este eliberat. Dac ns nu exist un nod care s aib identificatorul egal cu irul indicat de s, atunci stergere tiprete un mesaj de eroare. Rutina introducere creaz un nod nou care conine irul indicat de parametrul char *s, iar cmpul lui de valoare este egal cu parametrul int v. Nodul este inserat n lista indicat de parametrul nod lista, astfel nct dup introducere, lista rmne ordonat. Funcia introducere returneaz nceputul listei modificate. Linia /*introducere1*/ aloc memorie pentru un nou nod. Dac alocarea nu este posibil, se afieaz un mesaj de eroare i execuia programului este ncheiat. n caz contrar, cmpurile id i valoare sunt iniializate corespunztor. Linia /*introducere2*/ parcurge lista cutnd poziia n care noul nod trebuie introdus. Pentru introducerea unui nod ntr-o list ordonat sunt necesari doi pointeri; unul indic nodul n faa cruia se face inserarea, iar al doilea pe cel anterior lui. Dac noul nod nu este inserat ca prim nod n list (linia /*introducere4*/), atunci nlnuirea de la nodul anterior (indicat de q2) se schimb ctre nodul nou, iar nlnuirea noului nod se ndreapt spre nodul indicat de q1. Dac nodul devine primul n list (linia /*introducere5*/) atunci nlnuirea lui urm este modificat spre nodul indicat de q1. n acest caz, valoarea returnat de funcia introducere (adic nceputul listei modificate) este tocmai pointerul spre nodul recent introdus. Dac n list apare deja un nod al crui identificator este egal cu cel care se dorete a fi introdus (linia /*introducere3*/), atunci se afieaz un mesaj de eroare i se pstreaz lista nemodificat.

69

BIBLIOGRAFIE Hicks, Clint: Utilizare C, Editura Teora, Bucureti, 1996 Ilin, Alina; Doboli, Alexa: Tehnici de Programare ndrumtor de laborator, Centrul de multiplicare Universitatea Politehnica Timioara, 1996 Schildt, Herbert: C-manual complet, Editura Teora, Bucureti, 1998

70

S-ar putea să vă placă și