Sunteți pe pagina 1din 79

UNIVERSITATEA DUNREA DE JOS GALAI FACULTATEA DE ECONOMIE I ADMINISTRAREA AFACERILOR

Adrian Lupac

PROGRAMAREA CALCULATOARELOR

ISBN 978-606-8216-24-9

Editura EUROPLUS Galai, 2010

CUPRINS
1. Noiuni de baz n programarea calculatoarelor........................................................................... 4
Stabilirea corectitudinii i eficienei soluionrii problemei de rezolvat ....................................................................5 Algoritmul ..................................................................................................................................................................6 Descrierea algoritmilor ...............................................................................................................................................7 Programul .................................................................................................................................................................10

2. Introducere n limbajul C noiuni de baz ................................................................................ 12


Tipuri de date de baz...............................................................................................................................................14

Constante i variabile n limbajul C ..........................................................................................................17


Preluarea datelor de la tastatur................................................................................................................................20

3. Operatori aritmetici ....................................................................................................................... 21


Operatori de incrementare i decrementare ..............................................................................................................21 Operatori relaionali i operatorii logici ...................................................................................................................23 Conversii n C...........................................................................................................................................................23 Operatorul cast .........................................................................................................................................................25

4. Instruciuni de control ale programului....................................................................................... 26


Instruciunea vid i instruciunea expresie .............................................................................................26 Instruciunea if ............................................................................................................................................27 Instruciunea switch....................................................................................................................................31

5. Instruciuni repetitive .................................................................................................................... 33


Instruciunea for .........................................................................................................................................33

6. Instruciuni repetitive .................................................................................................................... 37


Instruciunea while .....................................................................................................................................37 Instruciunea do-while................................................................................................................................39 Instruciunea break i instruciunea continue..........................................................................................39

7. Funcii de intrare/ieire standard ................................................................................................. 41


Funciile getch() i getche()......................................................................................................................................41 Funcia printf() .........................................................................................................................................................41 Funcia scanf() ..........................................................................................................................................................45

8. Funcii n C.................................................................................................................................... 47
Definirea unei funcii................................................................................................................................................47 Prototipul funciilor ..................................................................................................................................................48 Apelul funciilor .......................................................................................................................................................49 Variabile locale i variabile globale .........................................................................................................................50

9. Structuri n limbajul C .................................................................................................................. 57 10. Preprocesare. Directive preprocesor........................................................................................... 60


Directiva preprocesor #define....................................................................................................................60

11. Fiiere n C .................................................................................................................................. 63


STREAM-ul.................................................................................................................................................63 Bazele fiierelor ...........................................................................................................................................64

Funciile feof() i ferror()............................................................................................................................69

12. Funcii text de nivel nalt ............................................................................................................ 72


Citirea i scrierea datelor binare ...............................................................................................................74 Accesul random...........................................................................................................................................77

1. Noiuni de baz n programarea calculatoarelor


Programarea este o component a informaticii care are ca principal obiectiv realizarea de programe care s constituie soluiile oferite cu ajutorul calculatorului unor probleme date. Programatorii sunt acele persoane apte s implementeze ntr-un limbaj de programare algoritmul propus ca soluie la respectiva problem, ce se preteaz a fi rezolvat cu ajutorul unui sistem de calcul. Dup nivelul de implicare n efortul de rezolvare a problemelor specialitii n programare pot fi mprii n mai multe categorii: analiti, analiti-programatori, ingineri-programatori, programatori amatori, etc. Cu toii au ns n comun faptul c fiecare trebuie s cunoasc sintaxa i semantica unui limbal de programare i s fie capabil, nu doar s citeasc, ci chiar s scrie codul surs, adic programul propriu-zis. Din acest punct de vedere cunotinele de programare sunt considerate ABC-ul informaticii i sunt indispensabile oricrui profesionist n domeniu i nu numai. Pentru a putea fi rezolvat cu ajutorul unui sistem de calcul, fiecare problem va trebui s treac prin trei etape obligatorii: analiza problemei, proiectarea algoritmului de soluionare i implementarea algoritmului ntr-un program pe calculator. n ultima etap, sub acelai nume, au fost incluse n plus dou subetape cunoscute sub numele de testarea i ntreinerea programului. Aceste subetape nu lipsesc din ciclul de via a oricrui produs-program. Dac etapa implementrii algoritmului ntr-un program executabil este o etap exclusiv practic, realizat n faa calculatorului, celelalte dou etape au un pronunat caracter teoretic. n consecin, primele dou etape sunt caracterizate de un anumit grad de abstractizare. Din punct de vedere practic ns, i n ultim instan, criteriul decisiv ce confer succesul rezolvrii problemei este dat de calitatea implementrii propriu-zise. Mai exact, succesul soluionrii este dat de performanele programului: utilitate, vitez de execuie, fiabilitate, posibiliti de dezvoltare ulterioare, lizibilitate, etc. Cu toate acestea este imatur i neprofesional strategia programatorilor nceptori care, neglijnd primele dou etape, sar direct la a treia, evitnd etapa de analiz i componenta abstract a efortului de soluionare. Ei se justific cu toii prin expresii puerile de genul: Eu nu vreau s mai pierd vremea cu teoria, am s fac programul cum tiu eu. Ct vreme nu va face altcineva altul mai bun dect al meu, nu am de ce s-mi mai bat capul!.

Stabilirea corectitudinii i eficienei soluionrii problemei de rezolvat Este adevrat c etapa care vizeaz implementarea unei probleme este fundamental, dar primele dou etape au o importan capital. Ele sunt singurele ce pot oferi rspunsuri corecte la urmtoarele ntrebri dificile: Exist certitudinea c soluia gsit este i cea corect? Exist certitudinea c problema este rezolvat complet? Ct de eficient este soluia gsit? Ct de aproape este soluia aleas de cea optim? S menionm n plus c literatura de specialitate conine un numr impresionant de probleme capcan pentru nceptori, i nu numai pentru ei. Ele provin majoritatea din realitatea imediat dar pentru fiecare dintre ele nu se cunosc soluii eficiente. De exemplu, este dovedit teoretic c problema, aparent banal pentru un calculator, a proiectrii Orarului optim ntr-o instituie de nvmnt (facultate sau liceu) este o problem intratabil la ora actual (toate programele care s-au realizat pn acum nu ofer dect soluii aproximative fr a putea spune ct de aproape sau de departe este soluia optim de orar). Ci dintre programatorii nceptori n-ar fi surprini s afle c problema att de simpl (ca enun), a crei soluionare tocmai au abandonat-o, este de fapt o problem dovedit teoretic ca fiind intratabil sau chiar insolvabil algoritmic? Partea proast a lucrurilor este c problemele netratabile pot fi cu uurin confundate cu unele uoare la o privire rapid i lipsit de experien. Etapa de analiz este singura care permite dovedirea cu argumente riguroase a corectitudinii soluiei, iar etapa de proiectare este singura care poate oferi argumente precise n favoarea eficienei soluiei propuse. n general problemele concrete din informatic au n forma lor iniial sau n enun o caracteristic pragmatic, fiind foarte ancorate n realitatea imediat. Totui ele conin n formularea lor iniial un grad mare de eterogenitate, diversitate i lips de rigoare. Fiecare dintre aceste defecte este un obstacol major pentru demonstrarea corectitudinii soluiei. Rolul esenial al etapei de analiz este acela de a transfera problema ntr-un plan abstract, adic de a o modela. Acest univers paralel abstract este dotat cu mai mult rigoare i disciplin intern, avnd legi precise, i poate oferi instrumentele logice i formale necesare pentru demonstrarea riguroas a corectitudinii soluiei problemei. Planul abstract n care sunt transportate toate problemele de informatic este planul sau universul obiectelor matematice iar corespondentul problemei n acest plan va fi modelul matematic abstract asociat problemei. Demonstrarea corectitudinii proprietilor ce leag obiectele universului matematic a fost i este sarcina matematicienilor. Celui ce analizeaz problema din punct de vedere informatic i revine sarcina (nu tocmai uoar) de a dovedi printr-o demonstraie 5

constructiv c exist o coresponden precis ntre prile componente ale problemei reale, dezasamblat n timpul analizei, i prile componente ale modelului abstract asociat. Odat descoperit, formulat precis i dovedit, aceast perfect oglindire a problemei reale n planul obiectelor matematice ofer certitudinea c toate proprietile i legturile ce exist ntre subansamblele modelului abstract se vor regsii precis (prin reflectare) ntre prile interne ale problemei reale, i invers. Atunci, soluiei abstracte descoperite cu ajutorul modelului matematic abstract i va corespunde o soluie real concretizat printr-un algoritm ce poate fi implementat ntrun program executabil. Ideea central a etapei a doua proiectarea unui algoritm de soluionare eficient poate fi formulat astfel: din studiul proprietilor i limitelor modelului matematic abstract asociat problemei se deduc limitele inferioare ale complexitii minimale (efortului minimal obligatoriu) inerente oricrui algoritm ce va soluiona problema n cauz. Complexitatea intern a modelului abstract i complexitatea soluiei abstracte se va reflecta imediat asupra complexitii reale a algoritmului, adic asupra eficienei de soluionare a problemei. Acest fapt permite prognosticarea nc din aceast faz faza de proiectare a algoritmului de soluionare a eficienei practice, msurabil ca durat de execuie, a programului. Algoritmul Se tie c la baza oricrui program st un algoritm (care, uneori, este numit metod de rezolvare). Noiunea de algoritm este o noiune fundamental n informatic i nelegerea ei, alturi de nelegerea modului de funcionare a unui calculator, permite nelegerea noiunii de program executabil. Vom oferi n continuare o definiie unanim acceptat pentru noiunea de algoritm:
Definiie. Prin algoritm se nelege o mulime finit de operaii (instruciuni) elementare care executate ntr-o ordine bine stabilit (determinat), pornind de la un set de date de intrare dintr-un domeniu de valori posibile (valide), produce n timp finit un set de date de ieire (rezultate).

Cele trei caracteristici eseniale ale unui algoritm sunt: Determinismul dat de faptul c ordinea de execuie a instruciunilor algoritmului este bine precizat (strict determinat). Acest fapt d una din calitile de baz a calculatorului: el va face ntotdeauna ceea ce i s-a cerut (prin program) s fac, el nu va avea iniiative sau opiuni proprii, el nu-i permite s greeasc nici mcar odat, el nu se va plictisi ci va duce programul la acelai sfrit indiferent de cte ori i se va cere s repete acest lucru. Nu aceeai situaie se ntmpl cu fiinele umane (Errare humanum est). Oamenii pot avea n situaii 6

determinate un comportament non-deterministic (surprinztor). Acesta este motivul pentru care numeroi utilizatori de calculatoare (de exemplu contabilii), datorit fenomenului de personificare a calculatorului (confundarea aciunilor i dialogului simulat de programul ce ruleaz pe calculator cu reaciile unei personaliti vii), nu recunosc perfectul determinism ce st la baza executrii oricrui program pe calculator. Universalitatea dat de faptul c, privind algoritmul ca pe o metod automat (mecanic) de rezolvare, aceast metod are un caracter general-universal. Algoritmul nu ofer o soluie punctual, pentru un singur set de date de intrare, ci ofer soluie pentru o mulime foarte larg (de cele mai multe ori infinit) de date de intrare valide. Aceasta este trstura de baz care explic deosebita utilitate a calculatoarelor i datorit acestei trsturi suntem siguri c investiia financiar fcut prin cumprarea unui calculator i a produsului software necesar va putea fi cu siguran amortizat. Cheltuiala se face o singur dat n timp ce programul pe calculator va putea fi executat rapid i economicos de un numr orict de mare de ori, pe date diferite! De exemplu, algoritmul de rezolvare nvat la liceu a ecuaiilor de gradul doi: ax2+bx+c=0, se aplic cu succes pentru o mulime infinit de date de intrare: (a,b,c)\{0}xx. Finitudinea pentru fiecare intrare valid orice algoritm trebuie s conduc n timp finit (dup un numr finit de pai) la un rezultat. Aceast caracteristic este analog proprietii de convergen a unor metode din matematic: trebuie s avem garania, dinainte de a aplica metoda (algoritmul), c metoda se termin cu succes (ea converge ctre soluie). S observm i diferena: n timp ce metoda matematic este corect chiar dac ea converge ctre soluie doar la infinit (!), un algoritm trebuie s ntoarc rezultatul dup un numr finit de pai. S observm de asemenea c, acolo unde matematica nu ofer dovada, algoritmul nu va fi capabil s o ofere nici el. De exemplu, nu este greu de scris un algoritm care s verifice corectitudinea afirmaiei Conjecturii lui Goldbach: Orice numr par se scrie ca sum de dou numere prime, dar, dei programul rezultat poate fi lsat s ruleze pn la valori extrem de mari, fr s apar nici un contra-exemplu, totui conjectura nu poate fi astfel infirmat (dar nici afirmat!). Descrierea algoritmilor Dou dintre metodele clasice de descriere a algoritmilor sunt Schemele logice i PseudoCodul. Ambele metode de descriere conin doar patru operaii (instruciuni) elementare care au fiecare un corespondent att schem logic ct i n pseudo-cod. 7

n cele ce urmeaz vom descrie doar varianta oferit de pseudo-cod deoarece folosirea schemelor logice s-a redus drastic n ultimii ani. Schemele logice mai pot fi ntlnite sub numele de diagrame de proces n anumite cri de specialitate inginereti. Avantajul descrierii algoritmilor prin scheme logice este dat de libertatea total de nlnuire a operaiilor (practic, sgeata care descrie ordinea de execuie, pleac de la o operaie i poate fi trasat nspre orice alt operaie). Este demonstrat matematic riguros c descrierea prin pseudo-cod, dei pare mult mai restrictiv (operaiile nu pot fi nlnuite oricum, ci trebuie executate n ordinea citirii: de sus n jos i de la stnga la dreapta), este totui perfect echivalent. Deci, este dovedit c plusul de ordine, rigoare i simplitate pe care l ofer descrierea prin pseudo-cod nu ngrdete prin nimic libertatea programrii. Totui, programele scrise n limbajele de asamblare, care sunt mult mai compacte i au dimensiunile mult reduse, nu ar putea fi descrise altfel dect prin scheme logice. 1. Atribuirea var:=expresie; 2. Intrare/Ieire Citete var1, var2, var3, ; Scrie var1, var2, var3, ; sau Scrie expresia1, expresia2, expresia3,; 3. Condiionala Dac <condiie_logic> atunci instruciune1 [altfel instruciune2]; 4. Ciclurile Exist (din motive de uurin a descrierii algoritmilor) trei tipuri de instruciuni de ciclare. Ele sunt echivalente ntre ele, oricare variant de descriere putnd fi folosit n locul celorlalte dou, cu modificri sau adugiri minimale: repet instruciune1, instruciune2, pn cnd <condiie_logic>; ct timp <condiie_logic> execut instruciune; pentru var_contor:=val_iniial pn la val_final execut instruciune; n cazul ciclurilor, grupul instruciunilor ce se repet se numete corpul ciclului iar condiia logic care permite sau nu reluarea execuiei ciclului este denumit condiia de ciclare. Observm c ciclul de tipul Repet are condiia de repetare la sfrit ceea ce are ca i consecin faptul c, corpul ciclului se execut cel puin odat, n mod obligatoriu, nainte de verificarea condiiei logice. Nu acelai lucru se ntmpl n cazul ciclului de tipul ct timp, cnd este posibil ca instruciunea compus din corpul ciclului s nu poat fi executat nici mcar odat. n plus, s mai observm c

ciclul de tipul Pentru pn la conine (n mod ascuns) o instruciune de incrementare a variabilei contor. Limbajele de programare care sunt relativ apropiate de limbajele naturale sunt denumite limbaje de nivel nalt (high-level), de exemplu limbajul Pascal, spre deosebire de limbajele de programare mai apropiate de codurile numerice ale instruciunilor microprocesorului. Acestea din urm se numesc limbaje de nivel sczut (low-level), de exemplu limbajul de asamblare. Limbajul de programare C are un statut mai special el putnd fi privit, datorit structurii sale, ca fcnd parte din ambele categorii. Peste tot unde n pseudo-cod apare cuvntul instruciune el poate fi nlocuit cu oricare din cele patru instruciuni elementare. Aceast substituire poart numele de imbricare. Prin instruciune se va nelege atunci, fie o singur instruciune simpl (una din cele patru), fie o instruciune compus. Instruciunea compus este format dintr-un grup de instruciuni delimitate i grupate n mod precis (ntre acolade { } n C). Spre deosebire de pseudo-cod care permite doar structurile noi formate prin imbricarea repetat a celor patru instruciuni n modul precizat, schemele logice permit structurarea n orice succesiune a celor patru instruciuni elementare, ordinea lor de execuie fiind dat de sensul sgeilor. Repetm c dei, aparent, pseudo-codul limiteaz libertatea de descriere doar la structurile prezentate, o teorem fundamental pentru programare afirm c puterea de descriere a pseudolimbajului este aceeai cu cea a schemelor logice. Forma de programare care se bazeaz doar pe cele patru structuri se numete programare structurat (spre deosebire de programarea nestructurat bazat pe descrierea prin scheme logice). Teorema de echivalen a puterii de descriere prin pseudo-cod cu puterea de descriere prin schem logic afirm c programarea structurat (aparent limitat de cele patru structuri) este echivalent cu programarea nestructurat (liber de structuri impuse). Evident, prin ordinea, lizibilitatea i fiabilitatea oferit de cele patru structuri elementare (i asta fr a ngrdi libertatea de exprimare) programarea structurat este net avantajoas. n fapt, limbajele de programare nestructurat (Fortran, Basic) au fost de mult scoase din uz, ele (limbajele de asamblare) sunt necesare a fi folosite n continuare doar n programarea de sistem i n programarea industrial (n automatizri).

Programul Prin program se nelege un ir de instruciuni-main care sunt rezultatul compilrii algoritmului proiectat spre rezolvarea problemei dorite ce a fost descris ntr-un limbaj de programare (ca i cod surs). Etapele realizrii unui program sunt: editarea codului surs, etap ce se realizeaz cu ajutorul unui program editor de texte rezultatul fiind un fiier C, cu extensia .c (.cpp); compilarea, etapa de traducere din limbajul de programare C n limbajul intern al microprocesorului, i este realizat cu ajutorul programului compilator C i are ca rezultat un fiier obiect, cu extensia .obj (n limbajul C) sau .exe (n limbajul Pascal); link-editarea, etap la care se adaug modului obiect rezultat la compilare diferite module coninnd subprograme i rutine de bibliotec, rezultnd un fiier executabil (aceast etap este comasat n Turbo Pascal sau Borland Pascal cu etapa de compilare), cu extensia .exe execuia (Run), etapa de lansare n execuie propriu-zis a programului obinut, lansare realizat de interpretorul de comenzi al sistemului de operare (command.com pentru sistemele DOS+Windows) Observm c aceste etape sunt complet independente n timp unele de altele i necesit pentru limbajul C utilizarea a patru programe ajuttoare: editor de texte, compilator C, link-editor i interpretorul de comenzi al sistemului de operare. n cazul mediilor de programare integrate (Borland) comandarea acestor patru programe ajuttoare precum i depanarea erorilor de execuie este mult facilitat. OBSERVAIE: Link-editarea reprezint asamblarea unor module precompilate pentru a rezulta un fiier executabil. De exemplu, dac avem mai multe module de program (s zicem diverse funcii) le putem compila pe fiecare n mod separat i n felul acesta putem observa i erorile mai uor iar la sfrit le linkeditm i va rezulta programul. Fiierele precompilate (pe care le vom link-edita) au avantajul c se pot folosi la diferite proiecte pstrnd de exemplu o oarecare paternitate asupra lor, va fi foarte dificil ca cineva s obin codul surs i s-l poat modifica fr s depun un efort considerabil. Deci va putea utiliza funciile dar e destul de dificil s modifici sau s copiezi ntr-un program personal doar cteva din aceste funcii. E mai uoar i depanarea programului pe module. De asemenea, merit subliniat faptul c n timp ce fiierul text C, ce conine codul surs, poate fi transportat pe orice main (calculator) indiferent de micro-procesorul acesteia urmnd a fi compilat la faa locului, n cazul fiierului obiect acesta nu mai poate fi folosit dect pe maina 10

(calculatorul) pentru care a fost creat (datorit instruciunilor specifice micro-procesorului din care este compus). Deci, pe calculatoare diferite (avnd micro-procesoare diferite) vom avea nevoie de compilatoare C diferite. n plus, s remarcm faptul c fiierele obiect rezultate n urma compilrii pot fi link-editate mpreun, chiar dac provin din limbaje de programare diferite. Astfel, un program rezultat (un fiier .exe sau .com) poate fi compus din module obiect care provin din surse diferite (fiiere Pascal, C, asamblare, etc.).

11

2. Introducere n limbajul C noiuni de baz


Toate elementele care compun un limbaj de programare, aa cum este i limbajul C nu sunt de sine stttoare, ci n conjuncie unul cu cellalt. Din acest motiv este important s fie nelese aspectele fundamentale ale limbajului, nainte ca ele s fie detaliate. Toate aplicaiile realizate prin intermediul limbajului C au anumite trsturi comune. Programele conin cel puin o funcie, fiecare incluznd una sau mai multe instruciuni. O funcie poate fi definit ca fiind o subrutin care poate fi apelat de diferite pri ale unei aplicaii. O instruciune se refer la o aciune care trebuie s fie executat de un program. Toate instruciunile scrise ntr-un program trebuie s aparin unei funcii i se termin obligatoriu cu caracterul ;. Cea mai simpl form a unei funcii este urmtoarea:
nume_funcie() { instruciuni; }

unde nume_funcie reprezint numele unei funcii, iar instruciuni reprezint secvena de instruciuni (una sau mai multe) care aparine funciei declarate. n ceea ce privete numele funciei (i nu numai) trebuie spus c limbajul C este de tip CASE-SENSITIVE, ceea ce presupune ca programatorul s aib n vedere faptul c, compilatorul C face deosebire ntre litere mari i litere mici. Orice program n limbajul C conine cel puin o funcie: funcia main(). Execuia fiecrui program ncepe cu funcia main(), ceea ce nseamn c dac programatorul omite scrierea acestei funcii, compilatorul va fi n imposibilitatea compilrii programului editat. Astfel, atunci cnd se execut un program, primele instruciuni executate sunt cele care fac parte din funcia main. Dac programul conine mai multe funcii, numele acestora va fi definit de utilizator. Toate programele scrise n limbajul C sunt memorate ntr-un fiier sau mai multe, acestea avnd extensia .c. Un fiier care conine un program scris n C sau care conine numai o parte a acestuia se numete fiier surs. Prin compilarea fiierului surs rezult un fiier obiect, care are extensia .obj. Fiierele surs care intr n compunerea unui program pot fi compilate mpreun sau separat. n urma unei compilri rezult un fiier obiect; fiierele obiect aferente aplicaiei pot fi reunite ntr-un program executabil prin link-editare. n urma link-editrii rezult un fiier executabil, cu extensia .exe. 12

Un nume este o succesiune de litere i eventual cifre, primul caracter fiind liter. Pot fi utilizate litere mici i litere mari precum i caracterul subliniere (_). Exist un numr de cuvinte mprumutate din limba englez care au o semnificaie predefinit. Utilizatorul nu poate da o alt utilizare acestora i de aceea se mai numesc cuvinte cheie. Acestea se scriu mereu cu litere mici i reprezint nume cu destinaii speciale (for, if, while, break, exit, etc.). Atomi lexicali Ca i alte limbaje, C are un alfabet i reguli pentru scrierea programelor corecte folosind semne de punctuaie. Aceste reguli formeaz sintaxa limbajului C. Compilatorul C are rolul de a testa dac un program editat este corect. Dac sunt erori, atunci va afia o list de mesaje de eroare i se va opri. Iniial, compilatorul mparte mulimea caracterelor (programul surs) n atomi lexicali, care reprezint vocabularul de baz al limbajului. n ANSI C (ANSI = American National Standards Institute) sunt ase tipuri de atomi lexicali (care se mai numesc i elemente lexicale sau uniti lexicale): cuvinte rezervate; identificatori; constante; iruri constante; operatori; semne de punctuaie. Caractere i atomi lexicali Un program C este o secven de caractere; caracterele permise n programele C sunt: literele mici i literele mari ale alfabetului: a b ... z, A, B, , Z cifre : 0 1 ... 9 alte caractere: * / = ( ) { } [ ] < > ' " ! @ # $ % & _ | ^ ~ \ . , ; : ? spaii: blank, newline i tab Identificatori Un identificator este un atom lexical compus dintr-o secven de litere, cifre sau caracterul underscore ("_") cu restricia c primul caracter este o liter sau underscore. n multe implementri C, se face distincie ntre litere mici i mari. n general, se obinuiete ca identificatorii s fie scrii cu nume sugestive care s uureze citirea i documentarea programului.

13

Exemple: 1. k, _id, contor, un_identificator sunt identificatori; 2. gresit#unu, 100_gresit_doi, -plus nu sunt identificatori. Comentarii n C Comentariile sunt iruri de caractere cuprinse ntre /* i */. Comentariile nu reprezint atomi lexicali. Practic, compilatorul va traduce comentariile ntr-un singur caracter spatiu, de aceea comentariile nu fac parte din codul executabil. Avantajele folosirii comentariilor: uurarea documentrii ulterioare; scopul documentrii este explicarea clar a folosirii programelor; un comentariu poate conine informaii care argumenteaz demonstraia corectitudinii unui algoritm; comentariile sunt foarte bine venite a fi scrise odat cu textul programului. Tipuri de date de baz Mulimea tipurilor de date predefinite constituie o caracteristic important i un argument n alegerea unui limbaj sau altul pentru rezolvarea unei probleme. n limbajul C exist o serie de tipuri simple de date predefinite (tabelul 2.1), a cror lungime poate s difere de la un calculator la altul i de la o implementare la alta. Pentru fiecare tip predefinit din limbajul C exist cuvinte rezervate, care reprezint modificatori de tip: unsigned, signed, long i short pentru tipul int; signed i unsigned pentru tipul char; long pentru tipul double. Cuvintele rezervate dintre parantezele ptrate sunt opionale i diferitele combinaii definesc acelai tip de dat. Tipurile de date specifice limbajului C sunt prezentate n tabelul 2.1. De asemenea, o component important a unui program C o reprezint biblioteca funciilor C (a funciilor executate de un program) care pot realiza: operaii de intrare/ieire, operaii matematice, operaii cu iruri, etc. Printre cele mai des folosite funcii din C se numr funcia printf, care este principala funcie de ieire aferent limbajului C, i este redat sub urmtoarea form:
printf(sir de caractere);

14

Pentru exemplul prezentat, pe ecran se va afia irul de caractere inclus ntre ghilimele.
Tabelul 2.1 Principalele tipuri de date n limbajul C Tip int short int long int Nr.octei 4 2 8 Interval de valori [-2147483648 2147483647] (-231 231-1) [-32768 32767] (-215 215-1) (-231 231-1) / (-215 215-1) Semnificaie conine numere ntregi cu semn, reprezentate binar pe 4 octei conine numere ntregi cu semn, reprezentate binar pe 2octei conine numere ntregi cu semn, reprezentate binar pe 8 octei se folosete pentru a crea ntregi fr semn. Diferena dintre ntreg cu semn i fr semn const n modul n care compilatorul interpreteaz bitul de semn. Dac bitul de semn este 0, numrul este pozitiv, i dac bitul de semn este 1, numrul este negativ. De cele mai multe ori, numerele negative se reprezint n complement fa de doi. Dac un ntreg este declarat cu unsigned, cnd bitul de semn este 1, numrul devine 231-1 sunt caractere, adic simboluri tipografice elementare: litere, cifre, semne de punctuaie, simboluri matematice, etc; caracter fr semn
38

unsigned int

[0 2147483647] (0-231 1)

[signed] char unsigned char float

1 1 4

[-128 127] (-27 27-1) [0 255] (28-1) [3,4*10


-38

3,4*10 ]

double long double

8 12

[1,7*10-308 1,7*10308] [3,4*10-4932 1,1*104932]

zecimal n virgul flotant simpl precizie conin numere reprezentate n virgul flotant (pot avea partea fracionar) zecimal n virgul flotant dubl precizie conin numere reprezentate n virgul flotant (pot avea partea fracionar) Reprezentare flotant n dubl precizie

O alt component important care caracterizeaz majoritatea programelor C sunt fiierele header. Toate informaiile aferente funciilor din biblioteca standard se afl n fiiere care sunt transmise mpreun cu compilatorul, aceste fiiere recunoscndu-se prin intermediul extensiei: .h. Informaiile din aceste fiiere sunt folosite de compilator pentru lucrul cu funciile din bibliotec. Toate fiierele necesare se adaug prin intermediul directivei preprocesor #include, care are rolul de a determina compilatorul s citeasc un alt fiier i s-l includ n program. Un fiier cu text surs poate fi inclus cu ajutorul construciei #include. Aceast construcie are formatul: 15

#include specificator_fiier sau

#include<specificator_fiier>

unde specificator_fiier (care depinde de sistemul de operare) definete un fiier cu text surs pstrat pe disc. n faza de preprocesare, textul fiierului respectiv se substituie construciei #include. Astfel, textul fiierului respectiv ia parte la compilare mpreun cu textul n care a fost inclus. n cazul sistemului de operare DOS, specificatorul de fiier trebuie s fie un nume de fiier mpreun cu extensia lui (.C pentru compilatorul C, .H pentru fiiere de tip header fiiere cu prototipuri, etc.). De asemenea, n afar de numele i extensia fiierului, specificatorul de fiier poate conine i o cale dac este necesar, pentru localizarea fiierului. Diferena dintre cele dou formate const n modul de cutare al fiierului de inclus: formatul cu paranteze unghiulare <> se folosete la includerea fiierelor standard, cum sunt cele care conin prototipuri pentru funcii din bibliotec; n cazul n care se folosete formatul cu ghilimele, fiierul este cutat n directorul curent sau conform cii dac aceasta este prezent. Un fiier standard care se include frecvent este stdio.h; acesta conine prototipurile pentru o serie de funcii ce realizeaz operaii de intrare/ieire. Construciile #include se scriu de obicei la nceputul fiierelor surs, pentru ca textele inserate s fie valabile n tot fiierul surs care se compileaz. Pentru a putea folosi ntr-un program funcia printf(), este necesar includerea directivei #include<stdio.h> deoarece, aa cum spuneam anterior, aceast directiv conine informaii cu privire la aceast funcie de ieire. Directivele incluse n program nu se finalizeaz cu caracterul ;, deoarece nu reprezint un cuvnt cheie al programului, ci o instruciune ctre compilatorul C. Cel mai simplu program scris n limbajul C este urmtorul: Exemplu:
#include <stdio.h> main() { printf("Primul program in limbajul C !!!"); getch(); }

care va afia pe suprafaa ecranului mesajul: Primul program in limbajul C !!!. Observaie: getch() este o funcie de ieire fr ecou (caracterul tiprit nu este afiat pe ecran) asupra creia se va reveni n cursul aferent funciilor de intrare/ieire; este folosit de mediul DevC++ pentru a pstra pe ecran (pn la apsarea unei taste) rezultatul programului compilat.

16

Practic, n acest program banal sunt incluse toate aspectele comune i obligatorii aferente unui program scris n C. Prima linie permite includerea n program a fiierului stdio.h. pentru a fi citit de compilator. nceputul funciei principale este desemnat de a doua linie, main(). Corpul tuturor funciilor n C (deci, i a funciei main) este inclus ntre dou paranteze acolad; tot ce este scris n interiorul acestor paranteze acolade constituie corpul funciei. n cazul programului prezentat anterior, corpul funciei main() este format dintr-o singur instruciune, care apeleaz funcia printf() din biblioteca standard pentru a permite afiarea pe ecran a irului dorit. Atunci cnd corpul unei funcii este format din mai multe instruciuni, parcurgerea acestora se va face secvenial. Constante i variabile n limbajul C O constant n limbajul C are un tip i o valoarea. Att tipul ct i valoarea sunt determinate de caracterele care intr n compunerea constantei. Valoarea unei constante nu poate fi schimbat n timpul execuiei programului n care a fost utilizat. Exist mai multe tipuri de constante: ntregi sunt constante care pot fi scrise n sistemul de numeraie n baza 8, 10 sau 16. O constant zecimal ntreag este un ir de cifre zecimale care are prima cifr diferit de zero. Constantele octale reprezint succesiuni de cifre octale (0-7) precedate de un zero nesemnificativ. O constant hexazecimal este o succesiune de cifre hexazecimale precedate de 0x sau 0X. Ex: 15150 care se reprezint binar prin: 00000000 00000000 00111011 00101110 flotante reprezint un numr raional i se compune din: o parte ntreag care poate fi i vid este o constant zecimal; o parte fracionar care poate fi i vid se compune din caracterul punct dup care urmeaz o succesiune de cifre zecimale; un exponent care poate fi i vid ncepe cu litera e sau E, dup care urmeaz un semn opional (plus sau minus) i un ir de cifre zecimale. Exponentul definete un factor care exprim o putere a lui 10. Ex:

100. 100 100.48 100,48 .48 0,48 12e5 12*105 .5E-5 0,5*10-5 15.255e2 15,255*102 17

1500.123e-3 1500,123*10-3

caracter o constant caracter are ca valoare codul ASCII al caracterului pe care-l reprezint i are tipul int. O constant caracter corespunztoare unui caracter imprimabil se reprezint prin caracterul respectiv inclus ntre caractere apostrof: A valoarea 65 a valoarea 97 * valoarea 42

ntr-un program utilizm alturi de date constante i date variabile care i schimb valorile n timpul execuiei programului. Dac la o dat constant ne putem referi folosind caracterele din compunerea ei, la o variabil trebuie s ne referim altfel. Cel mai simplu mod este acela de a denumi data respectiv. Numele datei ne permite accesul la valoarea ei, precum i schimbarea valorii dac este nevoie. n cazul n care o dat nu are legturi cu alte dat (spre exemplu, de ordine) se spune c ea este o dat izolat. Numele unei date izolate se spune c reprezint o variabil simpl. Unei date izolate i corespunde un tip. n cursul execuiei programului se pot schimba valorile unei date variabile dar nu i tipul ei. Corespondena dintre numele unei date variabile i tipul acesteia se definete printr-o declaraie. O variabil reprezint o locaie de memorie creia i s-a atribuit un nume i care conine diferite valori. Orice variabil n C trebuie mai nti declarat si apoi va fi folosit n program. Cnd se declar o variabil, se folosete urmtoarea form:
tip nume_variabil,

unde tip este tipul de dat dorit pentru variabila declarat, iar nume_variabil este numele variabilei (Ex: int varsta, n care se declar variabila varsta ca fiind de tip ntreg). Dac exist ntr-un program mai multe variabile de acelai tip, atunci ele pot fi declarate pentru acelai tip, separndu-le prin virgul (exemplu: int a,b,c;). Pentru a atribui o valoare unei variabile se folosete urmtoarea form general:
nume_variabil=valoare,

unde nume_variabil este numele variabilei (dac nu este nsoit de un tip, se consider c a fost declarat anterior), iar valoare reprezint valoarea atribuit variabilei nume_variabil (ex: i=10, n care s-a atribuit variabilei i (considerat a fi de tip int) valoare 10). O variabil care este declarat i se poate atribui o valoare iniial iniializarea variabilei. Iniializarea variabilei are urmtoarea form:
tip_dat nume_variabil = valoare;

18

Ex: int variabila=10; Observaie: Atunci cnd se iniializeaz o variabil, este obligatoriu ca dup semnul egal s avem o constant n cazul n care n loc de constant avem o alt variabil (care nu a fost iniializat), compilatorul va genera o eroare la compilarea programului. De asemenea, trebuie spus c atunci cnd se declar variabile, o parte dintre acestea pot primi valori iniiale n timp ce altele nu (ex: int i=1,j=1,a). n exemplele de program prezentate anterior, s-a folosit funcia printf() prin intermediul creia s-au afiat pe ecran caracterele scrise ntre ghilimele. ns pe ecran pot fi afiate prin intermediul funciei printf() i alte valori, cum ar fi cele aferente variabilelor declarate. n aceast situaie, funcia printf() conine mai multe argumente. Spre exemplu, dac dorim s afim valoarea variabilei i declarat anterior, se va folosi un specificator de format (toi specificatorii de format trebuie s nceap cu caracterul %). Caracterele vor fi afiate pe ecran n aceeai ordine n care ele sunt ntlnite n ir. Rolul specificatorului de format este acela de a informa funcia printf() c o alt categorie de date va fi afiat. Spre exemplu, dac se folosete calificatorul de format %d, atunci se va tipri pe ecran un ntreg n format zecimal. n acest caz, funcia va arata astfel:
printf("Valoarea variabilei i este %d",i);

n care valoare care trebuie afiat se afl pe al doilea argument, dup virgul (n cazul nostru, este i) i va fi afiat pe ecran acolo unde este ntlnit specificatorul de format. Funcia de mai sus va produce ca rezultat: Valoarea variabilei i este 10. Atunci cnd dorim s afim un caracter, specificatorul de format este %c, iar pentru tipurile float i double se folosete specificatorul %f. Exemplu:
main() { int i=1; char c=A; float f=15.15; double d=156.675; printf(Valoarea variabilei intregi i este %d\n,i); printf(Valoarea variabilei caracter c este %c\n,c); printf(Valoarea variabilei float f este %f\n,f); printf(Valoarea variabilei double d este %f\n,d); }

n exemplu anterior, au fost declarate i afiate valoriri ale variabilelor aferente celor patru tipuri de date prezentate.

19

Preluarea datelor de la tastatur Pentru preluarea datelor de la tastatur, se folosete funcia scanf(), din biblioteca C standard. Pentru citirea de valori de la tastatur, se folosete urmtoarea form general:
scanf(%spec_format,&variabila);

unde variabila este numele variabilei care va prelua valoarea introdusa de la tastatura. n funcia scanf(), primul argument este un ir care determin cum va fi tratat al doilea argument. Astfel, specificatorul de format %spec_format arat c al doilea argument (variabila) va primi valoarea introdus de la tastatur. De fiecare dat cnd se folosete funcia scanf(), deci cnd se preiau date de la tastatur, este obligatoriu ca variabila care preia valoarea s fie precedat de caracterul &. Practic, caracterul & permite funciei scanf() s plaseze o valoare n unul din argumentele sale. Atunci cnd se preiau caractere de la tastatur, funcia scanf() ateapt apsarea tastei ENTER i apoi convertete irul n format binar. Exemplu: Se creeaz urmtorul program:
#include<stdio.h> main() { int i; printf(introduceti valoarea lui i:); scanf(%d,&i); printf(valoarea lui i este: %d,i); }

Dup lansarea n execuie, pe ecran apare o fereastr, n care se ateapt introducerea unui ntreg. Dup introducerea valorii dorite, se ateapt apsarea tastei ENTER, dup care pe ecran va fi afiat valoarea preluat de la tastatur. Dac se dorete preluarea unui caracter, atunci funcia va include specificatorul de format %c, iar dac se preia un numr n zecimal, se folosete specificatorul %f. Tem: S se scrie un program care s preia de la tastatur dou valori de tipul int, i s se afieze suma lor.

20

3. Operatori aritmetici n limbajul C exist cinci operatori aritmetici: + : adunare - : scdere * : nmulire / : mprire % : modulo are ca rezultat restul mpririi a dou numere ntregi. Dintre operatorii prezentai anterior, primii patru pot fi folosii cu orice tip de dat, n timp ce operatorul modulo se folosete doar cu tipul int. Operatorii apar n expresii, care nu reprezint altceva dect combinaii de operatori i operanzi. Ex. 3.1.
#include <stdio.h> main() { int nr1,nr2, total, rest; printf("Introduceti cele doua numere:\n"); printf("nr1 este: "); scanf("%d",&nr1); printf("nr2 este: "); scanf("%d",&nr2); total=nr1+nr2; rest=nr1%nr2; printf("\n\n%d + %d = %d \n\n",nr1,nr2,total); printf("n1/n2=%d\n\n",(nr1/nr2)); printf("Restul este:%d.",rest); getch(); }

n exemplu de mai sus s-au declarat patru variabile de tip ntreg; valorile aferente primelor dou sunt preluate de la tastatura, calculndu-se apoi suma lor prin intermediul expresiei:
total=nr1+nr2;

Se observ c, operatorii pot fi inclusiv variabile. De asemenea, variabila rest primete ca valoare restul mpririi celor dou valori preluate. Operatori de incrementare i decrementare Majoritatea programelor realizate n C nu conin linii de genul: i=i+1, deoarece limbajul C pune la dispoziie un operator special care incrementeaz o variabil prin 1. Operatorul increment este ++ (de dou ori plus, fr spaiu). n acest fel, n loc de i=i+1 putem folosi i++. n mod similar, 21

pentru a decrementa variabila i cu unu, se folosete (de dou ori caracterul minus) (ex.: i--). Motivul pentru care se folosete aceast modalitate este acela c pentru multe compilatoare, aceti operatori sunt mult mai rapizi dect instruciunile de asignare corespunztoare. Aceast diferen apare deoarece n versiunea executabil a programului se evit ncrcarea i depozitarea unor instruciuni n limbaj main, prin substituirea cu o singur instruciune de incrementare sau decrementare. Atunci cnd operatorul de incrementare/decrementare apare n dreapta unei atribuiri, vom avea de-a face cu dou operaii: iniial se atribuie variabilei din stnga valoarea variabilei din dreapta, iar apoi se incrementeaz variabila din partea dreapt. Spre exemplu, dac avem urmtoarea asignare:
a=b++

n care se presupune c iniial variabila b a fost iniializat cu 100, dac dorim s afim a vom constata c are valoarea 100 i nu 101, asta pentru c iniial a a primit valoarea lui b i apoi b a fost incrementat cu 1. Un aspect important care trebuie menionat este acela c aceti operatori pot apare att dup numele variabilei, ct i naintea acesteia, ns cu semnificaii i implicaii total diferite. Atunci cnd operatorul de incrementare/decrementare apare naintea variabilei, presupunnd c avem de realizat tot o atribuire de valoare, iniial se realizeaz incrementarea variabilei din dreapta i apoi valoarea modificat se atribui variabilei din stnga. Dac se consider c variabila b are valoarea 100, atribuirea:
a=++b

va avea ca efect atribuirea valori 101 variabilei a. Ex. 3.2 Exemplu de utilizare a operatorilor de incrementare
# include <stdio.h> main() { int a=100, b=200,x,y; printf ("a++ = %d\n",a++); printf ("b-- = %d\n",b--); printf("a=%d, b=%d\n",a,b); printf ("--a = %d\n",--a); printf ("++b = %d\n",++b); printf("a=%d, b=%d\n",a,b); x=a++; y=b--; printf("x=%d, y=%d\n",x,y); printf("a=%d, b=%d\n",a,b); x=--a;

// afiseaza a=100 // afiseaza b=200 // afiseaza a=101 b=199 // afiseaza a=100 // afiseaza b=200 // afiseaza a=100 b=200 // afiseaza x=100 y=200 // afiseaza a=101 b=199

22

y=++b; printf("x=%d, y=%d\n",x,y); // afiseaza x=100 y=200 printf("a=%d, b=%d\n",a,b); // afiseaza a=100 b=200 }

Exemplul de mai sus pune n eviden toate aspectele menionate anterior. Operatori relaionali i operatorii logici Operatorii relaionali compar dou valori i returneaz true sau false. Operatorii logici conecteaz mpreun rezultatele true i false. Tabelul operatorilor logici este:
Tabelul 4.1 Operatorii logici n limbajul C

a&&b false false true false

a||b

!a

XOR false true true false

false false false true true true true false

false true true true true true false false

Tabelul 3.1 Operatorii relaionali i semnificaia lor

Operator Semnificaie > >= < <= == != Conversii n C n cadrul unei instruciuni de atribuire (asignare) n care tipul variabilei din stnga semnului egal difer de tipul din dreapta, atunci tipul din dreapta este convertit la tipul variabilei din stnga. n cazul n care tipul din stnga este mai puin cuprinztor dect tipul din dreapta, atunci se va realiza conversia cu pierdere de informaie; mai mare mai mare sau egal mai mic mai mic sau egal Egal diferit

23

dac tipul din stnga este mai cuprinztor dect tipul din dreapta, conversia se va realiza fr pierdere de informaie. Ex. 3.3. Exemplu conversii:
#include <stdio.h> main() { int i=100; float f=100.11; i=f; printf("i=%d\n\n",i); }

n acest exemplu, s-au declarat dou variabile: o variabil de tip int care este iniializat cu valoarea 100 i o variabil de tip float care este la rndu-i iniializat cu valoarea 100,11. Cnd se realizeaz atribuirea i=f, deoarece tipul lui i este mai puin cuprinztor dect tipul lui f, conversia se va realiza prin transformarea valorii lui f n valoare de tipul lui i. n cazul nostru, pe ecran va apare i=100. Dac f ar fi avut o valoare mai mare de 232-1, atunci conversia s-ar fi fcut cu pierdere de informaie, deoarece valoarea maxim pe care o poate lua i este 232-1. Dac se realiza atribuirea f=i, rezultatul ar fi fost: f=100.000000. n acest caz, practic se realizeaz doar o modificare a formei de reprezentare a numerelor, fr s se realizeze pierdere de informaie. Acelai lucru se ntmpl i dac se realizeaz conversia din tipul float n tipul double. Ex. 4.5. Exemplu conversie cu pierdere de informaie
#include <stdio.h> main() { int i=128; char ch; ch=i; printf("ch=%d\n\n",ch); }

n acest caz, conversia de la tipul int la tipul char se realizeaz cu pierdere de informaie, deoarece tipul char are domeniul de valori cuprins n intervalul [-128,127] (tipul char se reprezint pe un octet 8 bii). Astfel, dup compilarea acestui program, pe ecran va apare ch=-127. Pierderea de informaie n acest caz se produce deoarece doar cei opt bii inferiori ai lui i sunt copiai n ch. 24

De ce se obine ch= - 127? Aminteam anterior c tipul int este reprezentat intern pe 4 octei. Aceasta nseamn c numrul 128 va fi reprezentat binar astfel: 00000000 00000000 00000000 10000000 n ceea ce privete tipul char, acesta se reprezint n memoria intern pe un singur octet. n momentul n care se atribui lui ch valoarea variabilei de tip int, i, se realizeaz conversia de la tipul int la tipul char, lundu-se n calcul doar cei mai nesemnificativi 8 bii n cazul nostru, ch primete valoarea 10000000, n care primul bit reprezint bitul de semn, iar ceilali bii reprezint valoarea reprezentat. n cazul nostru, bitul de semn este 1, ceea ce nseamn c valoarea obinut este una negativ, iar restul de 7 bii semnific faptul c numrul este 127. Deci, ch=-127. Operatorul cast Operatorul cast se folosete atunci cnd se dorete modificarea tipului unei variabile. Acest operator are forma general: (tip_nou) variabila unde tip_nou este tipul de date ctre care se realizeaz trecerea variabilei variabila. Ex. 3.4. Exemplu de folosire a operatorului cast
#include <stdio.h> main() { float f=12.12; printf("Aplicarea operatorului cast pentru f produce rezultatul: %d\n\n",(int)f); }

n acest caz, pe ecran va apare: Aplicarea operatorului cast pentru f produce rezultatul:12

25

4. Instruciuni de control ale programului


Declaraiile permit utilizatorului s defineasc date de diferite tipuri (predefinite sau definite de utilizator). De asemenea, datele pot fi iniializate cu ajutorul declaraiilor. Prelucrarea tuturor datelor se realizeaz prin intermediul instruciunilor. Ordinea de execuie a instruciunilor unui program definete aa numita structur de control a programului. Cea mai simpl structur de control este structura secvenial. O astfel de structur de control se compune dintr-o succesiune de instruciuni care se execut una dup alta, n ordinea n care sunt scrise n program. ns, de obicei, la descrierea unui proces de calcul este nevoie s se utilizeze i alte tipuri de structuri. Astfel, marea majoritatea a programatorilor consider c pentru exprimarea proceselor de calcul sunt suficiente trei structuri de control fundamentale i anume: structura secvenial; structura alternativ; structura repetitiv (ciclic). Introducerea i utilizarea acestor structuri de control asigur o flexibilitate sporit n activitatea de programare. Astfel, programarea structurat reprezint un stil n programare care contribuie la realizarea de programe care au o structur clar i care pot fi uor depanate i ntreinute. Limbajul C a fost prevzut cu instruciuni menite s permit realizarea simpl a structurilor proprii programrii structurate. Structura secvenial se realizeaz cu ajutorul instruciunii compuse, structura alternativ cu ajutorul instruciunii if, structura repetitiv condiionat anterior prin intermediul instruciunilor while i for, structura selectiv se realizeaz cu ajutorul instruciunii switch, iar structura repetitiv condiionat posterior cu ajutorul instruciunilor do-while. Limbajul C are i alte instruciuni care reprezint elemente de baz n construirea structurilor amintite anterior. Astfel de exemple sunt instruciunea expresie i instruciunea vid. Alte instruciuni prezente n limbaj contribuie la creterea i mbuntirea flexibilitii n programare: return, break, continue i goto. Instruciunea vid i instruciunea expresie Instruciunea vid se reduce la caracterul ; i nu are nici un efect. Ea se folosete doar n anumite construcii n care este necesar prezena unei instruciuni, dar nu trebuie s se execute

26

nimic la momentul respectiv. Situaii de acest gen apar mai ales n cadrul structurii alternative i repetitive. Instruciunea expresie se obine atunci cnd se adaug punct i virgul (;) la sfritul unei expresii. Forma general a unei astfel de instruciuni este: expresie; Atunci cnd se atribuie o valoare unei variabile, spunem c avem o instruciune de atribuire. De asemenea, acest tip de instruciune mai apare atunci cnd expresia este un operand ce reprezint apelul unei funcii. n aceast situaie spunem c avem o instruciune de apel a funciei respective. Ex. 2.1. Exemple de instruciuni x=10; x++; instruciune de atribuire prin care variabilei x (se consider c este declarat anterior) i instruciune expresie, care mrete cu unu valoarea lui x. instruciune compus. se atribuie valoarea 10. {i=10; a=10}

Instruciunea if Instruciunea if este o instruciune condiional (de selecie) i are urmtoarea form general: a. if (expresie) instruciune b. if (expresie) instruciune 1 else instruciune 2 unde expresie poate fi orice expresie C valid. a. Pentru primul format, dac expresia este evaluat adevrat (true), instruciunea va fi executat. Dac expresia este evaluat ca fiind neadevrat (false) atunci se va sri peste executarea ei, executndu-se instruciunea care urmeaz dup if. De obicei, expresia din interiorul lui if compar o valoare cu alta folosind un operator de relaie.

27

Exemplu: Folosirea instruciunii if i a operatorului relaional >


#include <stdio.h> main() { int x=1, y=0; if (x>y) printf ("Adevarat !"); }

Dup executarea programului de mai sus, se evalueaz expresia ca fiind adevrat i pe ecran se va tipri Adevarat ! . Dac s-ar fi folosit operatorul de relaie <, pe ecran nu s-ar mai fi tiprit nici un mesaj. Observaie: Dac se dorete ca prin intermediul instruciunii if s se testeze egalitatea se va folosi operatorul ==. Exemplu: Aplicaia afieaz dou numere si ntreab utilizatorul care este suma lor. n funcie de rspuns se afieaz un mesaj.
#include <stdio.h> main() { int raspuns; printf ("Cat este suma numerelor 18 si 12? "); scanf("%d",&raspuns); printf("\n"); if (raspuns==30) printf("Raspunsul este corect\n\n"); if (raspuns!=30) printf("Raspunsul este gresit\n\n"); }

b. De cele mai multe ori, instruciunea if este nsoit de alternativa else. Semnificaia este urmtoarea: dac expresia aferent lui if este adevrat, se va executa blocul de instruciuni aferent lui if, caz n care corpul aferent lui else nu se mai execut. Dac expresia aferent lui if este fals, se va executa doar corpul lui else, n acest caz corpul lui if fiind cel care nu se mai execut. Atunci cnd este folosit un astfel de format, niciodat nu se va executa i corpul lui if i corpul lui else. 28

Ca prim exemplu cu else, s rescriem programul anterior, astfel nct s folosim cel de-al doilea format (cel cu else). Exemplu: Aplicaia afieaz dou numere i ntreab utilizatorul care este suma lor. n funcie de rspuns se afieaz un mesaj varianta cu else.
#include <stdio.h> main() { int raspuns; printf ("Cat este suma numerelor 18 si 12? "); scanf("%d",&raspuns); if (raspuns==30) printf("Raspunsul este corect\n\n"); else printf("Raspunsul este gresit\n\n"); }

Aceast variant a programului conduce exact la acelai rezultat ca la programul anterior. n acest caz, dac variabila rspuns ia valoarea 30 (variabila rspuns ia ca valoare ntregul introdus de utilizator), atunci se execut instruciunea din corpul lui if, fiind omis instruciunea din corpul lui else. n cazul n care valoarea introdus de utilizator este alta dect 30, nu se mai execut corpul de instruciuni aferent lui if, ci doar secvena de instruciuni din corpul lui else. Exemplu: Fie urmtorul program:
#include <stdio.h> main() { int a=3; if(++a<4) if(++a<4) printf(%d,a); else printf(%d,a); }

29

Acest program nu va afia nimic la execuie deoarece ramura else este asociat ultimului if. n schimb, dac adugm pentru delimitarea blocului primei instruciuni if dou acolade, atunci obinem codul: Exemplu:
#include <stdio.h> main() { int a=3; if(++a<4) { if(++a<4) printf(%d,a); } else printf(%d,a); }

n acest caz, la execuia programului, pe ecran va apare valoarea 4. Atunci cnd corpul unei instruciuni if sau else conine mai mult de o instruciune (secven de instruciuni), atunci, pentru a fi executate toate aceste instruciuni, trebuie s fie introduse ntre acolade. Pentru programul scris anterior, dac dorim ca instruciunea printf care realizeaz saltul la linie nou s fie scris pentru fiecare instruciune if sau else, secvena respectiv va arta astfel: Exemplu:
if (x>0) { printf(" x este mai mare decat 0 !!!"); printf("\n\n"); } else { printf(" x este mai mic sau egal cu 0 !!!"); printf("\n\n"); }

30

Dup cum se poate observa, corpul lui if i al lui else este format din dou instruciuni i atunci s-au folosit cele dou acolade. Dac s-ar fi omis introducerea acolodalor, atunci s-ar fi executat doar primele instruciuni aferente corpului instruciunilor respective. Instruciunea switch Instruciunea switch permite realizarea unor selecii multiple. Este folosit pentru a selecta una din mai multe alternative i lucreaz astfel: o variabil este testat succesiv cu o list de ntregi sau constante de tip char. Cnd apare egalitate, sunt executate instruciunile asociate acelei valori din list. Forma general a instruciunii este:
switch (nume_variabil) { case constant 1: instruciuni; break; case constant 2: instruciuni; break; case constant 3: instruciuni; break; ... default: instruciuni }

Dac s-a parcurs toat lista i variabila nu a fost egal cu nici una dintre constantele specificate, atunci se execut instruciunile aferente lui default (default este opional). Dac variabila este egal cu o constant a unui case, atunci se execut instruciunile aferente acelui case, pn la apariia lui break. Instruciunile switch i if difer prin aceea c switch testeaz numai egalitatea, n timp ce la if expresia de condiie poate fi de orice tip. De asemenea, switch lucreaz doar cu tipurile int i char. Exemplu: Exemplu de utilizare a instruciunii switch:

31

#include<stdio.h> main() { int i; printf("Introduceti un numar de la 1-4:"); scanf("%d",&i); switch(i) { case 1: printf("Ati apasat tasta Unu"); break; case 2: printf("Ati apasat tasta Doi"); break; case 3: printf("Ati apasat tasta Trei"); break; case 4: printf("Ati apasat tasta Patru"); break; default: printf("Numar necunoscut"); } printf("\n\n"); }

Tem: S se scrie un program care cere introducerea unei cifre de la tastatur i afieaz ziua corespunztoare acesteia, folosindu-se instruciunea switch.

32

5. Instruciuni repetitive Instruciunea for Una dintre cele trei instruciuni de ciclare ale limbajului C este for. Aceasta permite ca una sau mai multe instruciuni sau secvene de instruciuni s se repete de un numr specificat de ori. Instruciunea for este o instruciune care se utilizeaz pentru a realiza o structur repetitiv condiionat anterior i este util atunci cnd se cunoate numrul (maxim) de iteraii care vor trebui efectuate. Formatul acestei instruciuni este: for(<list_expresie_iniializare>;<expresie>;<list_expresie_actualizare>) instruciune unde: list_expresie_iniializare, expresie, list_expresie_actualizare sunt expresii iar instruciune este corpul ei i se execut repetat. n ceea ce privete cele trei expresii: list_expresie_iniializare partea de iniializare a instruciunii for; este folosit pentru a atribui o valoare iniial variabilei care controleaz ciclul. Aceast variabil este de obicei numit variabil de control a ciclului. Iniializarea se realizeaz o singur dat, nainte ca ciclul s nceap. Dac aceast list nu este prezent, atunci nu se execut nimic. expresie reprezint condiia de continuitate (testul de condiie a ciclului) i testeaz variabila de control cu valoarea scop ori de cte ori ciclul se repet. Dac, condiia este evaluat ca fiind neadevrat, ciclul nceteaz, iar execuia programului continu cu linia care urmeaz dup instruciunea for, iar dac expresie se evalueaz la true atunci se execut instruciune. Testul de condiie se face la nceputul ciclului, de fiecare dat cnd ciclul se repet. list_expresie_actualizare partea de reiniializare a instruciunii; se execut la sfritul fiecrei iteraii, adic dup ce s-a executat instruciunea sau secvena de instruciuni, dar nainte de testul de condiie. Scopul acestei expresii este acela de a incrementa/decrementa variabila de control a ciclului. Exemplu: S se realizeze un program care afieaz primele 10 numere naturale (de la 1 la 10):
#include <stdio.h> main() { int i; for (i=1;i<11;i++) printf(%d,i); }

Dup execuie, pe ecan va apare: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

33

Programul de mai sus lucreaz astfel: iniial, variabila contor i este iniializat cu 1. Apoi este evaluat expresia condiional (i<11). Deoarece aceasta este adevrat, ciclul for ncepe execuia. Dup afiarea numrului pe ecran, variabila i este incrementat cu 1 (i++), evalundu-se din nou expresia condiional. Toi aceti pai se repet pn n momentul n care condiia devine fals (n cazul nostru, pn cnd i devine 11). n ceea ce privete testul de condiie, acesta este realizat la nceputul fiecrei iteraii, ceea ce nseamn c dac la prima iteraie testul este fals, isntruciunea/secvena de instruciuni nu se mai execut. Spre exemplu, dac instruciunea for arta astfel: for (i=11; i<11; i++), testul de condiie ar fi fost evaluat la neadevrat chiar de la prima iteraie, ceea ce nsemna ieirea din ciclu fr a se executa nici mcar o singur dat instruciunea aferent lui for. Dac se dorete s se afieze de la 10 la 1, atunci valoarea iniial a lui i este 10, iar i va fi decrementat cu 1. Exemplu: S se realizeze un program care afieaz primele 10 numere naturale n ordine descresctoare (de la 10 la 1):
#include <stdio.h> main() { int i; for (i=10;i>0;i--) printf(%d,i); }

Dup execuia programului, pe ecan va apare: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 Aa cum se poate observa n forma general a acestei instruciuni repetitive, toate cele trei expresii sunt opionale. Astfel, exist instruciuni for care au doar dou expresii, o singur expresie, sau chiar nici o expresie. n cazul n care lipsete cel puin o expresie, caracterul punct i virgul trebuie s fie inclus. Exemplu: Exemplu de utilizare a instruciunii for n care lipsete expresia de iniializare
#include <stdio.h> main() { int i=1, suma=0,n=10; for(;i<n;i++) { suma+=i; printf("%d ",suma); } }

Exemplu: Exemplu de utilizare a instruciunii for n care lipsete expresia de actualizare


#include <stdio.h>

34

main() { int i=1, suma=0,n=10; for(;i<n;) { suma+=i++; printf("%d ",suma); } }

Exemplu: Exemplu de utilizare a insturciunii for n care lipsesc toate expresiile


#include <stdio.h> main() { int i=1, suma=0,n=10; for(;;) { if (i>=n) break; suma+=i++; printf("%d ",suma); } }

Pentru ultimul exemplu, trebuie fcut observaia c dac lipsete i expresie din sintaxa funciei for, atunci trebuie s utilizm instruciunea break, n caz contrar obinndu-se un ciclu infinit. Exemplu: S se declare o variabil local n a crei valoare s o primeasc de la tastatur i apoi s se afieze numerele n, n-1, n-2, ..., 2, 1.
# include <stdio.h> main() { int val,i; printf("Introduceti valoarea variabilei val: "); scanf ("%d",&val); for (i=0;i<val;i++) printf("%d ",val-i); printf("\n\n"); }

Exemplu: S se realizeze un program care s calculeze suma numerelor dintre 1 i n, adic cere suma dintre 1+1, 2+2, ..., n+n, n care n este un ntreg preluat de la tastatur, calculndu-se n acelai timp i numrul de raspunsuri corecte, respectiv greite. 35

# include <stdio.h> main() { int i, val, raspuns, corect=0, gresit=0; printf("Introduceti numarul pana la care doriti sa calculati suma: val="); scanf("%d",&val); for (i=1;i<=val;i++) { printf("Suma dintre %d si %d este: ",i,i); scanf("%d",&raspuns); if (raspuns==i+i) { printf("Raspunsul este corect"); corect=corect+1; } else { printf("Raspunsul este gresit: suma este %d",i+i); gresit=gresit+1; } printf("\n"); } printf("Suma numerelor pana la %d s-a terminat.\n",val); printf("Ati raspuns corect la %d si ati gresit la %d.",corect,gresit); printf("\n\n"); }

Observaie: n programul anterior, ciclul for include o secven de instruciuni, fapt care determin introducerea acoloadelor care s ncadreze corpul instruciunii for.

36

6. Instruciuni repetitive Instruciunea while Forma general a instruciunii while este: while(expresie) instruciune; n cazul n care instruciune reprezint o secven de instruciuni, atunci acestea trebuie s fie incluse ntre acolade. Semnificaia lui while este aceea c se execut corpul lui atta timp ct expresia este adevrat. Atunci cnd valoarea de adevr a expresiei devine fals, ciclul while i ncheie execuia. Aa cum reiese din forma general, instruciunea while este o instruciune repetitiv cu test iniial, ceea ce nseamn c este posibil ca instruciunile din corp s nu se execute niciodat: este cazul n care expresie are valoare fals nc de la prima parcurgere. La ntlnirea acestei instruciuni, se evalueaz iniial expresia din paranteze. Dac ea are valoarea adevrat atunci se execut instruciune. Apoi se revine la punctul n care se evalueaz din nou valoarea expresiei din paranteze, n felul acesta, corpul ciclului se execut att timp ct expresia din antetul ei este diferit de zero. n momentul n care expresie are valoarea zero (fals), se trece la instruciunea urmtoare instruciunii while. Corpul instruciunii while poate s nu se execute niciodat; dac expresie are valoarea zero de la nceput, atunci se trece la instruciunea urmtoare instruciunii while fr a se executa niciodat corpul instruciunii respective. Corpul instruciunii while este o singur instruciune, care poate fi compus. n felul acesta, avem posibilitatea s executm repetat mai multe instruciuni grupate ntr-o instruciune compus. De asemenea, corpul instruciunii while poate fi o alt instruciune while sau s fie o instruciune compus care s conin instruciuni while. n acest caz se spune c instruciunile while respective sunt imbricate. O situaie n care se folosete des aceast instruciune repetitiv este aceea n care utilizatorul dorete executarea corpului de instruciuni att timp ct caracterul instrodus de la tastatur difer de cel care permite ieirea din program. Diferena major dintre instruciunile for i while este aceea c instruciunile care formeaz corpul lui for se execut de un numr cunoscut de ori, n timp ce instruciunile care formeaz corpul lui while se execut de un numr de ori necunoscut: se execut att timp ct expresia are valoarea true.

37

Exemplu: S se realizeze un program care permite citirea i afiarea unui ntreg par (ntregul este introdus de utilizator). Dac utilizatorul introduce un numr impar, se va iei din program.
#include<stdio.h> main() { int i; printf("i="); scanf("%d",&i); while(i%2==0) { printf("i introdus este: %d",i); printf("\ni="); scanf("%d",&i); } printf("\n\nAti introdus un intreg impar!!!"); getch(); }

Exemplu: S se scrie un program care citete un ir de ntregi separai prin caractere albe i afieaz suma lor. Dup ultimul numr, se va tasta un caracter alb urmat de un caracter nenumeric, dup care se va aciona tasta Enter.
#include <stdio.h> main() { int nr,suma=0; while(scanf("%d",&nr)==1) suma=suma+nr; printf("suma=%d\n",suma); getch(); }

Tem: S se scrie un program care calculeaz i afieaz valoarea polinomului: p(x)=3*x*x-7*x-10 pentru x=1,2,...,10. Dac utilizatorul introduce un x>10, se iese i nu se mai calculeaz! 38

Instruciunea do-while Aceast instruciune are urmtoarea form general:


do{ instruciuni; }while (expresie);

do-while este o instruciune repetitiv cu test final, ceea ce nseamn c secvena de instruciuni se va executa cel puin odat. De asemenea, instruciunile care formeaz corpul lui dowhile se vor se vor repeta pn cnd expresia devine fals. Exemplu: S se realizeze un program care permite citirea i afiarea de ntregi de la tastatur pn cnd utilizatorul tasteaz caracterul N sau n pentru ieirea din program.
#include <stdio.h> main() { char c; int i; do { printf("i="); scanf("%d",&i); printf("i=%d",i); printf("\n\nContinuati? (D/N)?"); c=getche(); printf("\n\n"); } } while (c!='N' && c!='n');

Instruciunea break i instruciunea continue Instruciunea break permite ieirea dintr-un ciclu, din oricare punct din interiorul su. Atunci cnd se folosete break, se abandoneaz ciclul respectiv, controlul fiind transferat instruciunii imediat urmtoare celei repetitive. Exemplu: S se realizeze un program care s afieze doar numerele de la 0 la 20.
#include <stdio.h> main() {

39

int i; for(i=0;i<50;i++) { printf("%d ",i); if (i==20) break; } }

Dup executarea acestui program, vom observa c au fost afiate doar cifrele de la 0 la 20, dei n instruciunea for, i mergea pn la 50. Instruciunea break poate fi folosit cu fiecare din cele trei instruciuni repetitive ale limbajului. ntr-un ciclu pot exista oricnd instruciuni break. n general se recomand ca instruciunea break s fie utilizat pentru scopuri speciale i nu pentru o ieire normal din ciclu. Ex: S se realizeze un program care s afieze multiplii de 6 pn la 1000. Atunci cnd utilizatorul dorete s se opreasc, apas tasta N. Instruciunea continue este opus instruciunii break. Ea foreaz ca urmtoarea iteraie a ciclului s aib loc trecnd peste instruciunile dintre ea i testul de condiie. Exemplu: Exemplu cu instruciunea continue
#include <stdio.h> main() { int i; for (i=0;i<100;i++) { continue; printf("%d",i); //nu se afiseaza i } }

n exemplul de mai sus nu se va afia valoarea lui i.

40

7. Funcii de intrare/ieire standard


Intrrile/ieirile reprezint un set de operaii care permit schimbul de date ntre un program i un periferic. n general, operaia de introducere a datelor de la periferic se numete citire, iar cea de ieire pe un periferic se numete scriere. Limbajul C nu dispune de instruciuni specifice pentru aceste operaii de intrare/ieire. Ele se pot realiza prin intermediul unor funcii construite special pentru acest scop, funcii care exist n biblioteca limbajului C. Toate aceste operaii de intrare/ieire care se realizeaz prin intermediul funciilor de bibliotec se numesc operaii standard. Cele mai utilizate funcii de bibliotec folosite pentru realizarea operaiilor de intrare/ieire folosind terminalul standard sunt: getch(), getche(), gets() i scanf() i macroul getchar() pentru intrare; putch(), puts() i printf() i macroul putchar() pentru ieire. Cele dou macrouri sunt definite n fiierul stdio.h i din aceast cauz utilizarea lor implic includerea acestui fiier. Funciile getch() i getche() Cele dou funcii permit citirea unui caracter de la tastatur. Funcia getch() realizeaz citirea fr ecou de la tastatur (caracterul tiprit nu este afiat). La citirea unui caracter, funcia returneaz codul ASCII al acestuia. Spre deosebire de getch(), funcia getche() realizeaz citirea cu ecou a unui caracter atunci cnd se citete caracterul, el este automat afiat pe ecran. Ambele funcii nu au parametri si pot apare i ca operanzi n anumite expresii. La apelarea acestor funcii, se vizualizeaz fereastra utilizator i se ateapt tastarea unui caracter. De asemenea, trebuie specificat faptul c cele dou funcii au prototipurile n fiierul conio.h, ceea ce presupune includerea acestuia atunci cnd este utilizat aceast funcie. Tema: S se scrie un program care citete dou caractere: primul caracter este citit fr ocou, iar al doilea este citit cu ecou; ambele caractere vor fi afiate la consol folosind funcia printf(). Funcia printf() Funcia printf() este folosit n principal pentru afiarea caracterelor numerice sau a irurilor de caractere i va returna programului apelant numrul de caractere specificat. Dac se produce o eroare, funcia returneaz o valoare negativ. Forma general a funciei este: printf(control, par1,par2, ...,parn)

41

unde control este un ir de caractere care definete diferite texte i formatele datelor care se descriu, iar par1,par2, ...,parn sunt expresii i valorile lor se scriu conform specificatorilor de format prezeni n parametrii de control. Datele care sunt gestionate de funcia printf() sunt supuse unor transformri deoarece ele au un format extern i unul intern. Parametrul de control al funciei conine aa numiii specificatori de format care definesc conversiile datelor din format intern n format extern. n afara acestor specificatori de format, parametrul control poate conine succesiuni de caractere care se afieaz ca atare n poziiile corespunztoare. n mod normal, parametrul control conine att specificatorii de format, ct i alte caractere, ns pot apare situaii n care s nu le conin pe amndou. Spre exemplu, pentru afiarea simpl a unui text, se poate scrie urmtoarea linie:
printf(Text afisat cu funcia printf.../n);

Specificatorii de format, dac exist, corespund parametrilor par1, par2, ..., parn. Practic, specificatorul de format cu numrul k controleaz afiarea valorii parametrului park. Formatul extern al unei date const dintr-o succesiune de caractere imprimabile care se afieaz ntr-o zon numit cmp. Specificatorul de format ncepe mereu cu caracterul % i se termin cu o liter. Dac dup caracterul procent urmeaz un caracter diferit de cele folosite la definirea specificatorilor de format, caracterul procent respectiv se ignor i se afieaz caracterul care urmeaz dup el. Aceast regul permite afiarea nsui a caracterului procent. ntre caracterul procent i litera specificat, mai pot apare urmtoarele caractere: caracterul minus datele se ncadreaz n dreapta cmpului n care ele se scriu. Dac acest caracter este prezent, atunci data corespunztoare lui este ncadrat n stnga. un ir de cifre zecimale definesc dimensiunea minim a cmpului n care se afieaz caracterele care intr n compunerea formatului extern al datei. n cazul n care data necesit un cmp de dimensiune mai mare dect cel precizat, ea se va scrie pe attea caractere cte i sunt necesare. Dac data necesit un cmp mai mic, aceasta se va scrie n cmpul respectiv n dreapta sau n stnga, dac este prezent caracterul minus. un punct urmat de un ir de cifre zecimale irul de cifre de dup punct definete precizia datei care se scrie sub controlul specificatorului respectiv. Dac punctul este prezent, atunci i precizia este prezent. Dac data care se scrie este flotant, precizia definete numrul de zecimale care se scriu. Dac data este un ir de caractere, precizia indic numrul caracterelor care se tipresc. 42

una sau dou litere care definesc tipul conversiei aplicat datei care se tiprete. Cei mai importani specificatori de format sunt prezentai n continuare. 1. litera c permite afiarea unui caracter; 2. litrera s permite afiarea unui ir de caractere; Exemplu folosirea specificatorului de format %s.
char caracter[255]; gets(caracter); printf("Caracterele tastate sunt: %s.",caracter); printf("Text in printf:%10s!!!",caracter);

Dup executare, pe ecran va apare irul de caractere introdus de utilizator (pentru primul printf()). De asemenea, a doua linie care conine printf() va produce rezultatul: Text in printf: !!! 3. Litera d este folosit pentru afiarea unui ntreg. Datele de tip int pot fi convertite i afiate n zecimal folosind specificatorul de format terminat cu litera d. Valorile negative se afieaz precedate de semnul minus. Exemplu Folosirea specificatorului de format %d
printf("%d\n",100); printf("100%10d100\n",100); printf("100%-10d100\n",100);

Dup rulare, pe ecran va apare:


100 100 100100 100100 100

4. litera o realizeaz conversia datelor de tip int sau unsigned n octal. 5. litera x sau X realizeaz conversia datelor de tip int sau unsigned n hexazecimal. 6. litera u realizeaz conversia datelor binare de tip unsigned n zecimal. 7. litera l litera l poate precede una din literele d, o, x, X, u i realizeaz conversii din tipul long sau long unsigned. ld realizeaz conversia din long n zecimal, lu realizeaz conversia din long unsigned n zecimal, lo face conversia din long sau long unsigned n octal iar lx sau lX din long sau long unsigned n hexazecimal. 8. litera f permite conversia datelor de tip float sau double spre formatele parte ntreag sau parte_ntreag.parte_fracionar. Partea ntreag este un ir de cifre zecimale care este precedat de semnul minus dac numrul este negativ. Numrul zecimalelor este dat de precizia indicat n specificatorul de format. Dac este absent, atunci se afieaz 6 zecimale. Ultima cifr afiat este 43

rotunjit. Rotunjirea se face prin adaos dac prima cifr neglijat este cel puin egal cu 5 i prin lips n caz contrar. Exemplu Folosirea specificatorului de format %f
printf("%f\n",13.12345678); printf("%7f\n",13.12);

Dup executarea unui program care conine cele 2 linii, pe ecran va apare:
13.123457 13.120000

Funcia printf() poate ncorpora i alte faciliti; acestea pot fi obinute prin folosirea caracterului back-slash (\). Limbajul C definete 13 coduri speciale care nu pot fi introduse de la tastatur (cele mai importante sunt prezentate n tabelul de mai jos). Toate aceste coduri sunt folosite oriunde sunt folosite i caracterele normale. Cod Semnificaie \b \n \r \t \a \ \ \\ terge un caracter Salt la linie nou Nu returneaz caracterele din faa codului Tab orizontal Emite un sunet Insereaz ghilimele Insereaz un apostrof Insereaz caracterul backslash

Cel mai utilizat cod backslash este \n, numit newline character. Exemplu: S se scrie un program care compar dou litere.
main() { char c1='g',c2; c2=getche(); printf(c1>c2 ? "\nCaracterul %c este inaintea lui %c":"caracterul %c este dupa %c",c2,c1,c2,c1); }

Dac se ruleaz programul de mai sus i se introduce o tast care precede tasta g pe ecran va apare mesajul: Caracterul ... este naintea lui g, iar dac se afl dup g, va apare mesajul Caracterul ... este dup g. 44

Exemplu: S se scrie un program care afieaz codul ASCII al unui caracter introdus de la tastatur.
main() { char c1; printf("Introduceti caracterul:"); c1=getche(); printf("\nCaracterul %c are codul ASCII: %d\n",c1,c1); }

Exemplu: O utilizare frecvent a funciei care permite citirea caracterelor de la tastatur este aceea de a obine rspunsul da/nu al utilizatorului. Spre exemplu:
main() { char raspuns; printf("Continuati ? (D/N)"); raspuns=getche(); printf(raspuns=='D'?"\nSe continua":"\nNu se continua"); printf("\n\n"); }

Funcia scanf() Funcia scanf() poate fi folosit pentru a introduce de la tastatur date, sub controlul unor formate. Formatul general al funciei este:
scanf(control, par1, par2, , parn)

unde control este un ir de caractere care definete formatele datelor i a eventualelor texte aflate la intrarea de la tastatur, iar par1, par2, ,parn sunt adresele zonelor n care se pstreaz datele citite dup ce au fost convertite din formatele lor externe n formate interne corespunztoare. Spre exemplu, dac se citete o dat care se dorete a fi atribuit variabile var, respectiva variabil poate fi precedat de textul: var=. n acest caz parametrul trebuie s conin n poziia corespunztoare:
var=specificator format

Specificatorii de format controleaz introducerea datelor care au diferite formate externe reprezentate prin anumite succesiuni de caractere; ei definesc conversiile din formate externe n cele interne. Specificatorii de format ai funciei scanf() sunt asemntori cu cei care au fost descrii la funcia printf(). n ceea ce privete parametrii par1, par2, ,parn, acetia reprezint adresele zonelor de memorie n care se pstreaz datele citite de la tastatur n urma conversiilor n format intern.

45

Adresa unei zone de memorie se exprim prin intermediul operatorului unar &. Spre exemplu, dac dorim s citim un ntreg i apoi s-l afim: Exemplu:
#include <stdio.h> main() { int var; printf("var="); scanf("%d",&var); printf("var=%d",var); }

n programul de mai sus, s-a declarat variabila local de tip int var, apoi s-a folosit funcia printf() pentru a afia pe ecran var= , dup care, utilizatorul va introduce ntregul dorit, ntreg care va fi memorat n adresa variabilei var. Apoi, ultima linie va avea ca efect afiarea pe ecran a valorii variabilei var. Se observ c specificatorul de format este acelai (%d) att pentru funcia scanf ct i pentru funcia printf, deoarece se citete i se afizeaz o valoarea ntreag. Exemplu: Fie urmtorul program:
int main(void) { int intreg; float real; long nr_long; printf("Introduceti trei numere\n"); printf("Introduceti un intreg:"); scanf("%d", &intreg); printf("Introduceti un real si %s", "un long:"); scanf("%f%ld", &real, &nr_long ); printf("Ati introdus:\nun intreg=%d,\nun real=%f,\nun long=%ld.\n\n",intreg,real,nr_long); }

Aa cum se poate observa, pe o singur linie pot fi citite i mai multe valori de la tastatur, aa cum se ntmpl cu citirea varibilelor de tip float i long. Tema: S se modifice programul anterior astfel nct s se calculeze i s se afieze media aritmetic a celor 3 numere.

46

8. Funcii n C
n C, subprogramele se numesc funcii i reprezint unul dintre elementele fundamentale ale limbajului. Aa cum se tie deja, orice program C este alctuit dintr-o succesiune de funcii, dintre care una este funcia principal, denumit main(); la lansarea n execuie a unui program C este apelat funcia main(). ntr-un program C toate prelucrrile sunt organizate sub forma unei ierarhii de apeluri de funcii, baza ierarhiei fiind funcia principal main(). Pentru a dezvolta i utiliza funcii proprii este necesar s se tie cum se definesc, cum se declar i cum se apeleaz funciile. Definirea unei funcii n definiia unei funcii ntlnim:

un antet care conine tipul rezultatului returnat de, numele funciei i lista parametrilor; un bloc de instruciuni, care descrie prelucrrile efectuate de funcie.

Forma general a definiiei unei funcii este urmtoarea:


tip nume (lista parametrilor formali) // antetul funciei { declaraii variabile locale instruciuni }

unde: tip este tipul rezultatului returnat de funcie; nume este numele funciei; lista parametrilor formali este alctuit din una sau mai multe declaraii de parametri cu care funcia opereaz, separate prin virgul (lista poate fi i vid). Parametrii formali se numesc astfel deoarece cu ajutorul lor se descriu n mod formal operaiile care se vor efectua cu datele transmise de programul apelant. Observaie: Corpul unei funcii poate conine numai instruciuni sau numai declaraii, sau se poate reduce la cele dou acolade n cazul anumitor compilatoare sau medii care permit editarea i compilarea codului surs C, acoladele trebuie s fie mereu prezente. Corpul unei funcii conine, ntre acolode, o succesiune de declaraii urmate de o succesiune de instruciuni.

47

Exemplu:
void functie_1 (int x, int y) { printf(Suma numerelor este %d,x+y); }

Observaii: funcia functie_1 are tipul void, adic nu returneaz nici un rezultat; parametrii formali sunt de tip int. funcia afieaz suma celor dou numere transmise ca parametri de programul apelant.
int functie_2 (int x, int y) { int s; s=x+y; return s; }

Observaii: funcia functie_2 are tipul ntreg i calculeaz suma a dou numere ntregi, transmise ca parametri de programul apelant; variabila s este declarat n blocul funciei variabil local; n corpul funciei intervine instruciunea return, care va returna programului apelant valoarea variabilei s. Prototipul funciilor Compilatorul C va fi informat de existena funciei i a formatului acesteia prin intermediul prototipului funciei. Forma general a unui prototip este:
tip nume (list parametri formali);

Declaraia unei funcii este alctuit din antetul funciei urmat de caracterul ;, nu de blocul de instruciuni al acesteia. De aceea, declaraia funciei se numete i prototip. Este necesar ca naintea oricrui apel de funcie s apar fie definiia funciei, fie declaraia ei. Prototipul extinde metoda prezentat anterior prin declararea numrului i tipului argumentelor funciei. Prototipul permite limbajului C s gseasc orice nepotrivire ntre tipul argumentelor folosite pentru apelarea unei funcii i tipul parametrilor declarai ai acesteia. De asemenea, prototipul permite compilatorului s verifice dac numrul argumentelor cu care a fost apelat o funcie este acelai cu numrul parametrilor declarai ai funciei. 48

double sqrt(double);

Observaii: funcia sqrt() are tipul double i returneaz radicalul valorii primite ca parametru; prototipul acestei funcii este n heder-ul MATH.H.
float calcul(int x, float v);

Observaii: funcia calcul() are tipul real, are doi parametri: unul de tip int i unul de tipul float; n declaraia funciei calcul() este specificat numele parametrilor, nu doar tipul acestora. Apelul funciilor Apelul unei funcii se poate face n dou moduri: printr-o instruciune de apel: nume(lista parametrilor actuali); ca operand ntr-o expresie: v= nume(list parametri actuali); unde: - nume este numele funciei; - list parametri actuali este format dintr-o succesiune de expresii, separate prin virgul. La apelul unei funcii, valorile parametrilor actuali sunt atribuite, n ordine, parametrilor formali corespunztori. Parametrii actuali trebuie s corespund cu parametrii formali ca numr, ordine i tip. Ce se ntmpl cu parametrii actuali i unde sunt memorate valorile lor? Funciile folosesc o zon de memorie numit stiv (snack) care funcioneaz pe principiul LIFO ultimul intrat, primul ieit (last input, first output). La apelarea funciei, se aloc spaiul de memorie pe stiv pentru valorile parametrilor actuali. Se realizeaz astfel transferul prin valoare al parametrilor. La terminarea execuiei funciei, zona de memorie alocat pe stiv se elibereaz. Deoarece zona de memorie alocat pentru parametri se elibereaz, valorile modificate n funcie se pierd. Exemplu: S se realizeze un program care s calculeze volumul, folosind un prototip de funcie.
#include <stdio.h> double volum (double s1, double s2,double s3); main() { double vol; vol=volum(10.1,5.2,7.4); printf("Volumul este %lf",vol);

49

} double volum(double s1, double s2, double s3) { return s1*s2*s3; }

Variabile locale i variabile globale Variabilele ntr-un program se pot declara fie n interiorul unei funcii, fie n exteriorul tuturor funciilor declarate; n funcie de locul lor de declarare, exist variabile locale i variabile globale. Atunci cnd ntr-un program se declar variabile locale (ele sunt n interiorul unei funcii), acestea pot fi folosite doar de instruciunile funciei respective, i nu pot fi accesate de instruciuni aferente altor funcii. Trebuie specificat faptul c, compilatorul aloc memorie pentru o variabil local numai atunci cnd acesta a ajuns n funcia respectiv i este distrus din memorie atunci cnd se iese din funcia care a creat variabila. Tocmai din aceast cauz, funcii diferite pot conine declaraii pentru acelai nume de variabil local. Exemplu:
#include <stdio.h> main() { int i=100; printf("Suma variabilei i din main si a variabilei i din f1 este:%d\n\n",i+f1()); } f1() { int i=50; return i; }

Dup rulare, pe ecran va apare:


Suma variabilei i din main si a variabilei i din f1 este:150

Este important de menionat aici c i parametrii formali ai unei funcii fac parte tot din categoria variabilelor locale, chiar dac valorile acestor parametri provin prin apelul funciei din alt funcie. n ceea ce privete variabilele globale, acestea sunt vizibile pentru toate funciile unui program, iar declararea lor trebuie s se fac n afara oricrei funcii.

50

Exemplu: Utilizarea variabilelor globale


#include <stdio.h> int var_globala=100; //aceasta variabila este vizibila in tot programul main() { int var_locala=50,total; //aceste variabile sunt vizibila doar in functia principala main() total=var_globala+f1(150); // se apeleaza functia f1 si se transmite ca parametru valoarea 150 printf("Suma totala este: %d.\n",total); } int f1( int var_locala_0) // functia are parametru formal care este tot o variabila locala a lui f1 { int var_locala_1=50; //aceasta variabila locala este utilizabila doar in functia f1 //iar compilatorul nu va face confuzie intre cele doua variabile locale int suma; suma=var_locala_0+var_locala_1+var_globala; return suma; // variabila locala suma va primi valoarea 300, iar functia va intoarce aceasta valoare }

n programul de mai sus, s-a declarat variabila global var_global creia i s-a atribuit valoarea 100. Dup cum se observ, fiind declarat n afara oricrei funcii, ea poate fi folosit att n funcia main() ct i n funcia f1. n funcia principal se declar dou variabile locale total i var_locala, ultimei atribuindui-se valoarea 50. Variabila total primete valoarea variabilei globale nsumat cu valoarea ntoars de funcia f1, care transmite ca argument valoarea 150. n acest moment, compilatorul transfer controlul funciei f1, care la rndul ei are declarate variabilele locale var_locala_1 i suma (plus parametru formal care este tot o variabil local). Apoi suma primete ca valoare suma dintre valoarea variabilei globale (care este vizibil i n aceast funcie), valoarea variabilei locale var_local (care a primit la declarare valoarea 50) i valoarea transmis ca argument funciei f1 i care a fost atribuit parametrului formal var_locala_0. Astfel, variabila suma va prima valoarea 300, n final funcia returnnd valoarea aferent acestei variabile. Dup instruciunea return, compilatorul transfer din nou controlul ctre funcia principal, nlocuind practic operandul f1(150) cu valoarea ntoars de aceast funcie, respectiv 300. Instruciunea de afiare face ca la execuie s se afieze pe ecran:
Suma totala este:400.

Observaie: Dac ntr-un program se declar o variabil global i o variabil local cu acelai nume, copilatorul nu va genera eroare: dac numele variabilei respective este folosit n cadrul funciei, atunci compilatorul va ine cont de valoarea variabilei declarate local, i nu de valoarea variabilei declarate global. 51

Tema: S se scrie un program care s pun n eviden observaia anterioar. Este important s nu se abuzeze cu declararea variabilelor globale: atunci cnd este necesar, se declar, ns atunci cnd ele nu sunt folosite n mai multe funcii, se prefer varianta variabilelor locale, deoarece variabila global ocup spaiu de memorie pe toat durata programului, n timp ce variabila local ocup spaiu cnd se intr n funcia n care a fost declarat i se distruge din memorie la ieirea din funcia respectiv.

Recursivitatea n programare, recursivitatea se refer la proprietatea unei funcii de a se putea apela pe ea nsi. Cu alte cuvinte, spunem c o funcie este recursiv dac ea se autoapeleaz. Ea se poate autoapela fie direct, fie indirect prin apelul altor funcii. La apelurile recursive ale unei funcii aceasta este apelat nainte de a reveni din ea. La fiecare reapel al funciei, parametrii i variabilele automatice ale ei se aloc pe stiv ntr-o zon independent. De aceea, aceste date au valori distincte la fiecare reapelare. Nu acelai lucru se ntmpl cu variabilele statice. Ele ocup tot timpul aceeai zon de memorie i deci ele pstreaz valoarea la reapel. Orice apel al unei funcii conduce la o revenire din funcia respectiv la punctul urmtor celui din care s-a fcut apelul. Atunci cnd se revine dintr-o funcie se procedeaz la curirea stivei, adic stiva se reface la starea ei dinaintea apelului. De aceea, orice apel al unui apel recursiv va conduce i la curirea stivei, la o revenire din funcie, i deci parametrii i variabilele locale vor reveni la valorile lor dinaintea reapelului respectiv. Numai variabilele statice rmn neafectate la o astfel de revenire. Funciile recursive se utilizeaz la definirea proceselor recursive de calcul. Un proces de calcul se spune c este recursiv, dac el are o parte care se definete prin el nsui. Un exemplu simplu de astfel de funcie este funcia de calcul al factorialului, care se poate defini astfel:
factorial(n)=1, dac n=0, altfel factorial(n)=n* factorial(n-1)

Alternativa:
factorial(n)=n* factorial(n-1)

definete funcia factorial(n) prin ea nsi.

Un proces recursiv trebuie mereu s conin o parte care nu se definete prin el nsui. n exemplul de mai sus, alternativa: 52

factorial(n)=1, dac n=0

este partea care este definit direct. Definiia de mai sus se transmite imediat n limbajul C:
double factorial(int n) { if (n==0) return 1.0; else return n*factorial(n-1); }

S urmrim execuia acestei funcii pentru n=3. La apelul din funcia principal se construiete o zon pe stiv n care se pstreaz adresa de revenire n funcia principal, dup apel, precum i valoarea lui n, aa cum este prezentat n figura de mai jos:
rev n adr1 3 figura 1
adresa de revenire n funcia care a fcut apelul.

Deoarece n>0, se execut alternativa else. Aceasta conine expresia:


n*factorial(n-1);

care autoapeleaz (direct) funcia factorial. Reapelarea funciei se realizeaz cu valoarea n-1=3-1=2. prin aceasta se construiete o zon nou pe stiv, care conine revenirea la evaluarea expresiei de mai sus, precum i valoarea nou a parametrului n, adic valoarea 2. Se obine starea din figura de mai jos:
rev n rev n adr2 2 adr1 3 figura 2 adresa de revenire la evaluarea expresiei n*factorial(n-1);

La o nou reapelare a funciei factorial, n=2>0, deci din nou se execut alternativa else. Evaluarea expresiei conduce la o reapelare cu valoarea n-1=2-1=1. Se obine urmtoarea configuraie a stivei:

53

rev n rev n rev n

adr3 1 adr2 2 adr1 3 figura 3

adresa de revenire la evaluarea expresiei n*factorial(n-1);

Iari

n=1>0 i deci se face o nou reapelare i se obine configuraia:


rev n rev n rev n rev n adr2 0 adr2 1 adr2 2 adr1 3 figura 4 adresa de revenire la evaluarea expresiei n*factorial(n-1);

n acest moment n=0, deci se revine din funcie cu valoarea 1. Se cur stiva i se revine la configuraia din figura 3. Adresa de revenire permite continuarea evalurii expresiei n*factorial(n-1). n acest moment n are valoarea 1 i cum s-a revenit tot cu valoarea 1, se realizeaz produsul:
1*1=1

Dup evaluarea expresiei n*factorial(n-1) se realizeaz o nou revenire tot cu valoarea 1. Dup curirea stivei se ajunge la configuraia din figura 2. n acest moment n are valoarea 2. Evalund expresia n*factorial(n-1) se realizeaz produsul:
2*1=2

54

Apoi se revine din nou la funcia cu valoarea 2. Dup curirea stivei se ajunge la configuraia din prima figur. n acest moment n are valoare 3. Se evalueaz expresia n*factorial(n-1) i se obine:
3*2=6

Se revine din funcie cu valoarea 6 (3!) i conform adresei de revenire, se revine n funcia principal din care s-a apelat funcia factorial. Programul care calculeaz factorialul unui ntreg preluat de la tastatur este prezentat n continuare. Ex. S se realizeze un program care calculeaz factorialul unui ntreg preluat de la tastatur.
#include <stdio.h> main() { int n; printf("Introduceti intregul pentru care se va calcula factorialul:"); scanf ("%d",&n); printf("fact(%d)=%d",n,factorial(n)); } factorial(int n) { if(n==0) return 1; else return n*factorial(n-1); }

Referitor la funciile recursive, trebuie specificat faptul c recursivitatea constituie un nou tip de mecanism de control al programului. Din acest motiv, fiecare funcie recursiv va avea o instruciune if care controleaz dac funcia se va apela pe ea nsi din nou sau dac va reveni n programul apelant. Fr o astfel de instruciune, o funcie recursiv va rula necontrolat, folosind toat memoria alocat stivei, conducnd n final la o blocare a calculatorului. Recursivitatea poate fi util n simplificarea anumitor algoritmi. Spre exemplu, algoritmul de sortare Quicksort este foarte dificil de implementat dac nu se folosete recursivitatea. Ex. S se realizeze un program care afieaz descresctor (sau cresctor) numerele de la n la 0 (sau 0 la n) folosind o funcie recursiv, unde n este un ntreg introdus de utilizator.
#include <stdio.h> main() { int n;

55

printf("Introduceti n>"); scanf("%d",&n); afisare(n); } afisare(int n) { if (n>=0) { printf("%d ",n); afisare(n-1); } }

Pentru afiarea numerelor n ordine cresctoare, n corpul instruciunii if se va inversa ordinea celor dou instruciuni. Tema 1.1: S se scrie un program care s conin o funcie care returneaz un ir de caractere prin intermediul instruciunii return i s se afieze din funcia principal respectivul ir de caractere; Tema 1.2: S se scrie un program care conine o funcie care returneaz cu ajutorul lui return o valoarea ntreag care este adunat cu o alt valoare n funcia principal. Tema 1.3: S se scrie un program care folosete o funcie care are ca parametri formali dou variabile de tip char, iar funcia afieaz pe ecran argumentul transmis la apel. Tema 1.4: S se scrie un program care folosete o funcie care are ca parametri formali dou variabile de tip ntreg. Funcia returneaz n funcia principal suma celor doi parametri care este adunat cu un alt ntreg i apoi afiat la consol.

56

9. Structuri n limbajul C
O structur reprezint un conglomerat de tipuri de date. Spre deosebire de tablouri, fiecare element al unei structuri poate avea propriul su tip care poate diferi de tipul oricrui element. Structurile sunt definite n C folosind forma general:
struct tip_structur { tip element_1; tip element_2; tip elemen_n; }list_variabile;

unde struct este un cuvnd cheie i are rolul de a instrui compilatorul despre faptul c se definete o structur; tip_structur este tipul noii structurii; tip este oricare tip de dat admis de limbajul C i nu trebuie s fie acelai pentru toate elementele; list_variabile este o list de variabile de tipul tip_structur. Dintre elemetele prezentate anterior, tip_structur i list_variabile sunt opionale, dar una dintre ele trebuie s fie prezent. Elementele unei structuri sunt numite cmpuri sau membri, n funcie de sursa citat. n general, informaiile care sunt incluse ntr-o structur sunt nrudite din punct de vedere logic. De exemplu, se poate imagina o structur care s conin adresa unei persoane. n continuare este prezentat un exemplu de structur pentru gestionarea unui carte de cri.
struct carte { char nume [40]; // numele autorului char titlu [40]; char editur [40]; long data; // data publicrii unsigned char ed; // ediia }c1;

n exemplul de mai sus, carte este tipul structurii. El nu este numele unei variabile. Singura variabil definit de aceast structur este c1. Pentru a accesa un cmp al unei structuri, trebuie specificat att numele structurii ct i numele cmpului, separate printr-un punct. De exemplu, folosind variabila c1 de tip carte, urmtoarea instruciune asigneaz cmpului data valoarea 2009.
c1.data = 2009;

Pentru a afia data publicrii, se poate folosi urmtoarea instruciune:


printf(Data publicrii este: %u, c1.data);

Pentru a introduce data, utilizai instruciunea scanf(), dup cum urmeaz: 57

scanf(%ld, &c1.data);

Este important de reinut c operatorul & trebuie plasat naintea numelui structurii i nu naintea numelui cmpului. n aceeai manier, instruciunile urmtoare citesc numele autorului i afieaz titlul:
gets (c1.nume) ... printf(%s,c1.titlu);

Pentru a accesa un caracter din cmpul titlu, trebuie indexat titlu. De exemplu, instruciunea urmtoare afieaz a treia liter:
printf(%c, c1.titlu[2]);

Odat ce a fos definit un tip de structur, se pot crea mai multe variabile de acest tip, folosind forma general:
struct tip_structur list_variabile;

Presupunnd de exemplu c structura carte a fost definit aa cum am artat anterior n aceast seciune, declaraia urmtoare definete trei variabile de tipul carte:
struct carte var1, var2, var3;

Subliniem c nu este necesar s se declare toate variabilele de tipul respectiv atunci cnd este definit o structur. Conform declaraiei anterioare, variabilele se pot declara separat, pe msur ce apare necesitatea folosirii lor. Dac se cunoate c va fi necesar numai un numr fix de variabile, tip_structur poate s nu fie specificat. Spre exemplu, urmtorul cod surs creaz dou variabile de structur, fr ca structura s aib tip.
struct { int a; char ch; }var1, var2;

n practica uzual, se ntlnesc rareori tructuri care s nu aib tipul declarat. Structurile pot fi aranjate n tablouri n acelai fel ca i celelalte tipuri de date. Spre exemplu, urmtoarea declaraie creaz un tablou de structuri de tip carte care conine 100 de elemente:
struct carte cat[100];

Pentru a accesa o anumit structur din tablou, trebuie indexat numele tabloului. De exemplu, pentru a accesa prima structur a tabloului de structuri, trebuie scris cat[0]. Pentru a accesa un cmp al unei anumite structuri din tablou, indexul trebuie urmat de un punct i de numele cmpului dorit. Spre exemplu, urmtoarea instruciune ncarc cu valoarea 2 cmpul ed al structurii 34: 58

cat[33].ed=2;

Structurile pot fi transferate ca parametri funciilor, iar o funcie poate s returneze o structur. De asemenea, coninutul unei variabile de structur poate fi atribuit unei alte variabile de structur de acelai tip. De exemplu, acest fragment de program este valid:
struct tip_s { int a; float f; } var1, var2; var1.a=10; var1.f=100.23; var2=var1;

Dup rularea acestei secvene de cod, var1 i var2 vor conine acelai lucru. De asemenea, trebuie menionat c atunci cnd se transfer o structur unei funcii, est etransferat ntreaga structur dac folosim apelarea prin valori. Urmtorul exemplu prezint cteva moduri de accesare a elementelor unei structuri.
#include <stdio.h> #include <conio.h> struct tip_struct { int i; char ch; double d; char str[80]; }s; void main(void) { printf("Introduceti un intreg: "); scanf("%d",&s.i); printf("Introduceti un caracter: "); scanf(" %c",&s.ch); printf("Introduceti un float: "); scanf("%lf",&s.d); printf("Introduceti un sir: "); scanf("%s",&s.str); printf("\n\n%d\n%c\n%lf\n%s",s.i,s.ch,s.d,s.str); getch(); }

59

10. Preprocesare. Directive preprocesor


Un program surs C poate fi prelucrat nainte de a fi supus compilrii. O astfel de prelucrare poart numele de preprocesare. Ea se realizeaz automat naintea compilrii. Procesarea const n principiu n substituii i aigur: includeri de fiiere cu texte surs; definiii de apeluri i macrouri; compilare condiionat. Proprocesarea se realizeaz prin prelucrarea unor informaii specifice care au prim caracter caracterul diez (#). Directiva preprocesor #define Aa cum am amintit, o directiv preprocesor este o instruciune ctre procesor. Directiva #define este folosit cu scopul de a instrui preprocesorul s nlocuiasc un ir printr-un alt ir, n toate locurile n care primul ir este ntlnit n program. Acest proces poart numele de macrosubstituie (macrosubstitution). Cu alte cuvinte, aceast construcie se folosete pentru substituirea de succesiuni de caractere. Forma general a acestei directive este urmtoarea:
#define nume_macro ir

O observaie important este aceea c aceast linie nu se termin cu caracterul ; . De fiecare dat cnd n program este ntlnit nume_macro, n locul lui este introdus ir. Spre exemplu, fie urmtorul program: Ex.7.1.
#include <stdio.h> #define MAX 100 main() { int i; for (i=0;i<MAX;i++) printf(%d,i); }

Atunci cnd preprocesorul ntlnete identificatorul MAX, l substituie n mod automat cu irul 100. astfel c, pentru compilator, ciclul for ca apare sub forma:
for (i=0;i<100;i++)

60

printf(%d,i);

n momentul substituiei, 100 este un ir de caractere compus din 1 i dou zerouri. Preprocesorul nu convertete numrul n format intern. Aceast sarcin este lsat n seama compilatorului. Numele nume_macro care apare n forma general a acestei directive poate fi orice identificator C valid. n ceea ce privete ir, el poate conine orice tip de caracter i este terminat de sfritul liniei. Ex.7.2.
... #define A 100 // A se substitue prin 100 ncepnd din acest punct al programului ... #define B 20 #define C (A*B) ... char tab[C]; double matrice[A][B];

Dup preprocesare, se obin declaraiile:


char tab[(100*20)]; double matrice[100][20];

C s-a substituit prin (100*20) i nu prin valoarea 2000 a produsului deoarece preprocesorul nu face calcule ci numai substituii. Din aceast cauz se recomand ca expresiile utilizate n construcii #define s fie incluse n paranteze rotunde. Numele definit printr-o construcie #define sunstituindu-se prin secvena de caractere corespunztoare nu mai este prezent la compilare. Directivele preprocesor n general i directiva #define n particular nu sunt afectate de secvenele de instruciuni ale limbajului C. Dac o directiv definete un nume_macro n exteriorul tuturor funciilor sau n corpul unei funcii, toate instruciunile care urmeaz dup aceast directiv au acces la macro. Ex.7.3. Urmtorul program afieaz pe ecran 1000
# include <stdio.h> void f(void); void main(void)

61

{ #define MACRO 1000 # define MACRO1 "Alta macrosubstitutie" f(); } void f(void) { printf("%d",MACRO); printf("\n\n"); printf(MACRO1); printf("\n\n"); }

#define este folosit de obicei pentru definirea constantelor. De aceea, uneori un nume definit printr-o construcie #define se spune c este o constant simbolic. O construcie #define autorizeaz substituia pe care o definete din punctul n care ea este scris i pn la sfritul fiierului n care ea este scris sau pn la ntlnirea unei construcii #undef care o anuleaz. La ntlnirea ei (#undef nume_macro) se dezactiveaz substituirea lui nume_macro cu succesiunea de caractere care i-a fost ataat n prealabil printr-o construcie #define. Un alt motiv pentru care macro-substituia este important este acela c ajut la ntreinerea programelor. Spre exemplu, dac se tie c o anumit valoare, cum ar fi dimensiunea unui tablou, va fi folosit n mai multe locuri ntr-un program, este bine s se creeze un macro pentru aceast valoare. Dac ulterior trebuie schimbat valoarea, este suficient s se modifice numai linia unde a fost defint macro-ul respectiv. Toate referinele la ea vor fi automat modificate cnd programul va fi recompilat.

62

11. Fiiere n C
Biblioteca C standard conine un bogat set de funcii de intrare/ieire. Aa cum vom vedea n continuare, aceste funcii sunt eficiente, puternice i flexibile. Multe compilatoare C furnizeaz dou seturi complete de funcii de intrare/ieire pentru disc. Primul set este numit ANSI file system (uneori numit i buffered file system) i este definit de standardul ANSI C. Al doilea set este bazat pe mediul de operare UNIX i este numit UNIX-like file system (uneori numit i unbuffered file system). Standardul ANSI C definete numai unul din aceste dou sisteme, ele fiind redundante. Mai mult chiar, nu orice mediu poate suporta UNIX-like file system. Din acest motiv vom prezenta n continuare numai ANSI file system.

STREAM-ul nainte de a ncepe prezentarea funciilor de intrare/ieire pentru fiierele disc, trebuie s nelegem un concept foarte important al limbajului C, stream-ul. n C, un stream este o inerfa logic ntre computer i unul dintre diferitele periferice ale sale. n forma sa cea mai comun, un stream este o interfa logic pentru un fiier. Dupa cum definete limbajul C termenul de fiier, el se poate referi la un fiier disc, ecran, tastatur, port i aa mai departe. Dei fiierele difer ca forme i proprieti, stream-urile sunt aceleai. Avantajul acestei aproximri este c, pentru programator, toate perifericele apar la fel. Un stream este asociat unui fiier prin executarea unei operaii open i este eliberat de un fiier prin executarea unei operaii close. Exist dou tipuri de stream-uri: text i binar. Un stream text este folosit pentru a manipula caractere ASCII. Cnd folosim un stream text, poate aprea translatarea anumitor caractere. De exemplu, atunci cnd este ntlnit caracterul newline, el este convertit ntr-o secven carriagereturn/line feed (ntoarce/deplasare rnd). Din acest motiv, este posibil s nu existe o coresponden total ntre ceea ce este trimis ctre stream i ceea ce conine fiierul. Un stream binar poate fi utilizat cu orice tip de dat; el nu produce translatri de caractere, iar corespondena ntre ceea ce este transmis ctre stream i ceea ce conine fiierul este total. Un alt concept care trebuie neles corect este locaia curent. Locaia curent (poziia curent) este locaia din fiier unde se va produce urmtoarea operaie de acces. Spre exemplu, dac un fiier are lungimea de 100 bytes i jumtate de fiier a fost citit, urmtoarea operaie de citire se va produce la byte-ul 50 care reprezint locaia curent. 63

Bazele fiierelor Pentru a deschide un fiier i a-l asocia unui stream, vom folosi funcia fopen(), al crei prototip este: FILE *fopen(char *fname, char *mode), unde: funcia fopen() folosete fiierul header stdio.h; fname este un pointer ctre numele fiierului ce urmeaz a fi deschis. El trebuie s fie un nume valid n sensul n care este definit de sistemul de operare; mode este un pointer ctre un ir care determin modul n care fiierul poate fi accesat. Valorile aferente lui mode sunt prezentate n tabelul urmtor: Mode Semnificaie r w a rb wb ab r+ w+ a+ r+b w+b a+b Deschiderea unui fiier text pentru citire Crearea unui fiier text pentru scriere Append la un fiier text Deschiderea unui fiier binar pentru citire Crearea unui fiier binar pentru scriere Append la un fiier binar Deschiderea unui fiier text pentru citire/scriere Crearea unui fiier text pentru citire/scriere Append sau crearea unui fiier text pentru citire/scriere Deschiderea unui fiier binar pentru citire/scriere Crearea unui fiier binar pentru citire/scriere Append la un fiier text pentru citire/scriere

Dac operaia de deschidere a fost efectuat cu succes, funcia fopen() returneaz un pointer ctre fiier. Tipul FILE este definit n stdio.h i este de fapt o structur care conine diferite informaii despre fiier, cum ar fi: dimensiunea fiierului, locaia curent, modul de acces. n general, aceast structur identific fiierul (o structur este un grup de variabile accesate sub acelai nume). Funcia fopen() returneaz de fapt un pointer ctre structura asociat cu fiierul prin procesul de deschidere. Vom folosi acest pointer cu toate funciile care opereaz asupra fiierelor. Dac operaia de deschidere eueaz. Funcia fopen() returneaz un pointer null. Este foarte 64

important s ne asigurm c funcia fopen() a returnat un pointer valid, deci trebuie n general verificat valoarea returnat de fopen() pentru a fi siguri c nu este NULL. Modul n care trebuie deschis un fiier text numit myfile pentru intrare, este prezentat n continuare:
FILE *fp; if ((fp=fopen(myfile,r))==NULL) { printf (Eroare de deschidere\n); exit (1); }

Vor apare urmtoarele situaii: dac atunci cnd ncercm s deschidem un fiier pentru citire, fiierul nu exist, fopen() eueaz; dac atunci cnd ncercm s deschidem un fiier pentru append, fiierul nu exist, el va fi creat. Mai mult chiar, toate datele noi scrise ntr-un fiier vor fi scrise la sfritul fiierului; dac atunci cnd ncercm s deschidem un fiier pentru scriere, fiierul nu exist, coninutul original va fi distrus i va fi creat un nou fiier. Diferena dintre r+ i w+ este aceea c r+ nu va crea un fiier dac fiierul cerut nu exist, n timp ce w+ va crea un fiier. Mai mult, dac fiierul exist, deschiderea lui cu w+ i va distruge coninutul, n timp ce deschiderea cu r+ nu l distruge. Pentru nchiderea unui fiier se folosete funcia fclose(), al crei prototip este: int fclose (FILE *fp); Funcia fclose() nchide fiierul asociat cu fp, care trebuie s fie un pointer valid obinut anterior printro operaie fopen() i dezafecteaz stream-ul de la fiier. Pentru mbuntirea eficienei, n multe implementri, datele sunt scrise pe disc astfel: ele sunt depuse ntr-un buffer pn cnd cantitatea de informaie este echivalent cu cantitatea de informaie ce poate fi coninut ntr-un sector de disc i apoi buffer-ul este scris fizic pe disc. Atunci cnd este apelat funcia fclose(), ea scrie automat pe disc informaia coninut n buffer-ul parial plin. Aceast operaie se numete flushing the buffer. Funcia fclose() returneaz 0 dac se ncheie cu succes i EOF n cazul n care apare o eroare. Odat ce un fiier a fost deschis, se pot citi sau scrie bytes din, sau n el, utiliznd urmtoarele dou funcii: int fgetc (FILE *fp); int fputc (int ch, FILE *fp); Utilizarea uneia sau a celeilalte dintre ele depinde de modul n care a fost deschis fiierul. Funcia fgetc() citete urmtorul byte din fiierul deschis de fp ca un char fr semn i l returneaz ca un ntreg. Motivul pentru care bitul citit este returnat ca un ntreg este acela c, dac 65

apare o eroare, fgetc() returneaz EOF, care este o valoare ntreag. Funcia fgetc() returneaz de asemenea EOF i atunci cnd este atins sfritul de fiier. Rutinele care vor fi scrise pot asigna valoarea returnat de fgetc() unor variabile tip char, nu neaparat unor variabile tip int. Funcia fputc() scrie byte-ul coninut de variabila ch n fiierul asociat cu fp ca un char fr semn. Dei ch este definit ca int, funcia poate fi apelat cu o variabil tip char, aceasta fiind de fapt utilizarea cea mai comun. Funcia fputc() returneaz caracterul scris dac operaia s-a finalizat cu succes sau EOF n cazul apariiei unei erori. Numele tradiionale pentru funciile fgetc() i fputc() sunt getc() i putc(). Standardul ANSI definete i aceste nume; ele sunt interschimbabile cu fgetc() i fputc(). Motivul pentru care au fost create noile nume fgetc() i fputc() este acela c toate celelalte funcii sistem ncep cu f, aa c, pentru consecven, a fost adugat f funciilor getc() i putc(). n continuare se prezint un exemplu n care sunt utilizate cele patru funcii. Mai nti este deschis un fiier numit myfile pentru scriere (creare) i apoi scrie fiierul: Acesta este un sir pentru fiiere. n continuare, fiierul este nchis i apoi redeschis pentru citire. n final, este afiat pe ecran coninutul fiierului, dup care fiierul va fi nchis.
#include <stdio.h> #include <stdlib.h> char str[80]="Acesta este un sir pentru fisiere."; main() { FILE *fp; char *p; int i; //deschiderea fisierului if ((fp=fopen("myfile","w"))==NULL) { printf("Nu pot deschide fisierul..."); exit(1); } //scrierea fisierului p=str; while (*p) { if(fputc(*p,fp)==EOF) { printf("Eroare de scriere in fisier\n"); exit(1); } p++; } fclose(fp); //deschiderea fisierului pt citire

66

if((fp=fopen("myfile","r"))==NULL) { printf("Nu pot deschide fisierul.\n"); exit(1); } //citirea fisierului for (;;) { i=fgetc(fp); if (i==EOF) break; putchar(i); } getch(); fclose(fp); }

n aceast versiune, varianta returnat de fgetc() este asignat unui int. Valoarea acestui ntreg este apoi verificat pentru a stabili dac s-a ajuns la sfritul fiierului. Pentru multe compilatoare, se poate atribui valoarea returnat de funcia fgetc() unei variabile de tip char. Aceasta nu ne mpiedic s verificm dac valoarea returnat este EOF. Programul urmtor ilustreaz acest lucru.
#include <stdio.h> #include <conio.h> #include <stdlib.h> char str[80]="Acesta este un sir pt fisiere."; main() { FILE *fp; char ch, *p; //deschiderea fisierului if ((fp=fopen("myfile","w"))==NULL) { printf("Nu pot deschide fisierul..."); exit(1); } //scrierea fisierului p=str; while (*p) { if(fputc(*p,fp)==EOF) { printf("Eroare de scriere in fisier\n"); exit(1); } p++; } fclose(fp); if((fp=fopen("myfile","r"))==NULL) { printf("Nu pot deschide fisierul.\n"); exit(1); } for (;;)

67

{ ch=fgetc(fp); if (ch==EOF) break; putchar (ch); } getch(); }

S se scrie un program care primete dou argumente n linie comand. Primul este numele fiierului iar al doilea este un caracter. Programul va cuta acest caracter n fiierul specificat: dac se regsete n acesta, programul va afia un mesaj de confirmare. Totodat, este important s reinem modul n care se acceseaz fiierul i caracterul cutat.
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { FILE *fp1; char ch1; int contor=0; if (argc!=3) { printf ("Nr. argumente incorect. \n"); exit(1); } //deschiderea fisierului if ((fp1=fopen(argv[1],"r"))==NULL) //rb deschide un fisier binar pt citire { printf("Nu pot deschide fisierul sursa. \n"); exit(1); } while ((ch1=fgetc(fp1))!=EOF) if (ch1==*argv[2]) { printf("\n%c exista in fisier.",ch1); contor++; break; } if (contor==0) printf("\n%c nu exista in fisier.",*argv[2]); fclose (fp1); }

Programul prezentat anterior va fi rulat n mediul command. Pentru a lansa fereastra care ne permite s scriem aceste linii de cod, se vor parcurge urmtorii pai: 1. Start Run...; 2. Se tasteaz cmd i se apas butonul ok care va cauza deschiderea ferestrei: c:\\WINDOWS\system32\cmd.exe; 68

3. se alege calea care definete locaia (folderul) n care se gsete fiierul executabil; 4. totodat, n acelai director se va crea un fiier cu extensia .txt, n care se va scrie un text; 5. pentru rularea i testarea programului se va scrie urmtoarea linie: aplicatie.exe cale_fisier_text\nume_fisier_text.txt caracter_cautat, unde: a. aplicatie.exe reprezint numele fiierului n care este scrie codul surs (numele fiierului creat n Dev-C++); b. cale_fisier_text reprezint locaia n care este salvat fiierul text creat anterior; c. nume_fisier_text.txt reprezint numele fiierului text; d. caracter_cautat este caracterul pe care l vom cuta n text. Observaie: Numele programului cu extensia .exe, numele fiierului text i caracterul cutat se separ prin spaiu. Pentru exemplificare considerm c vom crea fiierul text cu numele fp11.txt salvat in folderul C care se gsete n C i n care am scris textul Alexandra. De asemenea, fiierul cod surs este numit fis_ex_4, iar caracterul cutat este a. Aa cum se observ, programul ne anun c a gsit caracterul a n fiierul text. n cazul n care am cuta caracterul u, atunci am fi informai c el nu se gsete n fiierul creat.

Funciile feof() i ferror() Dup cum am menionat deja, dac fgetc() returneaz EOF, nseamn c s-a produs o eroare sau s-a ajuns la sfritul fiierului. ntrebarea este: cum am putea ti care din aceste dou evenimente a avut loc? Mai mult, dac se opereaz asupra unui fiier binar, orice valoare este valid. Aceasta nseamn c este posibil ca un byte s aib aceeai valoare (cnd este evaluat la un int) ca i EOF. Deci, n ce mod 69

putem afla dac a fost returnat o dat valid sau s-a ajuns la sfritul fiierului? Soluia pentru aceste probleme o constituie funciile feof() i ferror(), al cror prototip este urmtorul: int feof(FILE *fp); int ferror(FILE *fp); Funcia feof() returneaz non-0 dac s-a ajuns la sfritul fiierului asociat cu fp; altfel ea returneaz 0. Aceast funcie lucreaz att pentru fiiere binare ct i pentru fiiere text. Funcia ferror() returneaz non-0 dac n fiierul asociat cu fp a aprut o eroare; n caz contrar va returna 0. Utiliznd funcia feof(), acest fragment de program arat cum poate fi fcut o citire de fiier pn la sfrit: FILE *fp; . . . while (!feof(fp)) fgetc(fp); Acest fragment lucreaz corect pentru ambele tipuri de fiiere i este mai bun dect verificarea pentru EOF. El nu prevede nici o verificare pentru apariia unor erori, aa c vom relua fragmentul: FILE *fp; . . . while (!feof(fp)) fgetc(fp); if (ferror(fp)) { printf(file error!\n); break; } Subliniem c ferror() se refer la erorile aprute relativ la ultima operaie de acces la fiier. Pentru a face o verificare total a erorilor, ea trebuie apelat dup fiecare operaie asupra fiierului. Cele mai multe erori apar la nivelul sistemului de operare, iar sistemul de operare interpreteaz el nsui erorile i afieaz propriile sale mesaje. De cele mai multe ori, singurele erori care sunt transmise napoi programului sunt unele minore care au aprut din cauza unor greeli n scrierea programului, cum ar fi accesarea unui fiier ntr-un mod necorespunztor sau apariia unei condiii n afara limitelor. Din acest motiv, apar foarte rar apelri ale funciei ferror() n programele scrise n C. 70

S se scrie un program care compar dou fiiere ale cror nume sunt specificate n linia de comand. El afieaz fiierele sunt identice sau numrul byte-ului care nu este acelai.
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { FILE *fp1, *fp2; char ch1, ch2, same; long q; if (argc!=3) { printf ("Nr. argumente incorect. \n"); exit(1); } //deschiderea primului fisier if ((fp1=fopen(argv[1],"rb"))==NULL) { printf("Nu pot deschide fisierul sursa. \n"); exit(1); } //deschiderea celui de-al doilea fisier if ((fp2=fopen(argv[2],"rb"))==NULL) { printf("Nu pot deschide fisierul destinatie. \n"); exit(1); } q=0; same=1; //compararea continutului celor doua fisiere while (!feof(fp1)) { ch1=fgetc(fp1); if (ferror(fp1)) { printf("Eroare de citire in fisier!\n"); exit (1); } ch2=fgetc(fp2);; if (ferror(fp2)) { printf("Eroare de citire in fisierul fp2!\n"); exit(1); } if (ch1!=ch2) { printf("\nFisierul difera la byte-ul numarul %ld",q); same=0; break; } q++; } if (same) printf ("\nFisierele sunt identice!\n"); fclose (fp1); fclose (fp2); }

71

12. Funcii text de nivel nalt


Pentru a lucra cu fiiere text, limbajul C ofer patru funcii care simplific mult operaiile cu fiierul. Primele dou funcii sunt numite fputs() i fgets(). Funcia fputs() scrie un ir ntr-un fiier, n timp ce funcia fgets() citete un ir dintr-un fiier. Prototipul lor este: int fputs(char *str, FILE *fp); char *fgets (char *str, int num, FILE *fp); Funcia fputs() scrie irul punctat de str n fiierul asociat cu fp. Ea returneaz EOF dac se produce eroare i non-negativ dac operaia se finalizeaz cu succes. Caracterul NULL care termin str nu este scris n fiier. Deci, ea lucreaz altfel dect funcia nrudit puts(), n sensul c nu adaug automat secvena carriage-return/line-feed. Funcia fgets() citete caractere din fiierul asociat cu fp ntr-un ir punctat de str pn cnd: au fost citite num 1 caractere; a fost ntlnit caracterul newline; s-a ajuns la sfritul fiierului. n toate cazurile, irul este terminat printr-un NULL. Ea lucreaz diferit fa de funcia nrudit gets(), n sensul c este reinut caracterul newline. Funcia returneaz pointerul str dac operaia s-a terminat cu succes sau un pointer NULL dac s-a produs o eroare. Limbajul C conine alte dou funcii foarte puternice, similare cu cele dou funcii deja prezentate. Ele sunt fprintf() i fscanf(). Aceste funcii opereaz exact ca printf() i scanf(), cu excepia faptului c lucreaz cu fiiere. Prototipul lor este: int fprintf(FILE *fp, char *control_string, ...); int fscanf(FILE *fp, char *control_string, ...); Aceste funcii nu opereaz cu consola, ci cu fiierul specificat cu fp. Avantajul folosirii funciilor fprintf() i fscanf() const n faptul c permit scrierea i citirea unei largi varieti de date ntr-un, sau dintr-un fiier, folosinf un specificator de format. Urmtorul program demonstreaz folosirea funciilor fputs() i fgets(). El citete liniile introduse de utilizator i le scrie ntr-un fiier specificat n linia comand. Cnd utilizatorul introduce o linie blanc, faza de introducere se finalizeaz, fiierul fiind apoi nchis. Apoi, fiierul este redeschis pentru citire, iar prin intermediul funciei fgets se afieaz coninutul su.
#include <stdio.h> #include <string.h>

72

#include <stdlib.h> void main(int argc, char *argv[]) { FILE *fp; char str[80]; if (argc!=2) { printf ("Specificati numele fisierului in comanda. \n"); exit(1); } if((fp=fopen(argv[1],"w"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } printf("\nIntroduceti o linie de blancuri pentru stop\n"); do { printf(":"); gets(str); strcat(str,"\n"); //adaugarea caracterului newline if(*str!='\n') fputs(str,fp); }while (*str!='\n'); fclose (fp); //deschiderea fisierului pt citire if((fp=fopen(argv[1],"r"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } //citirea fisierului do { fgets(str,79,fp); printf(str); }while(!feof(fp)); fclose(fp); }

Programul urmtor prezint folosirea funciilor fprintf() i fscanf(). Programul scrie o dat de tip double, una de tip int i un ir de caractere ntr-un fiier specificat n linia de comand, apoi citete aceste date i afieaz valorile lor pentru verificare. Dac vom analiza cu atenie fiierul creat, vom observa c el conine un text care se poate citi deoarece fprintf() scrie ntr-un fiier disc ceea ce printf() ar scrie pe ecran.
#include <stdio.h> #include <string.h> #include <stdlib.h> void main(int argc, char *argv[]) { FILE *fp; double ld; int d;

73

char str[80]; if (argc!=2) { printf ("Specificati numele fisierului in comanda. \n"); exit(1); } if((fp=fopen(argv[1],"w"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } fprintf(fp,"%lf\n%d\n%s",12345.342,2000,"Salut"); fclose(fp); //deschiderea fisierului pt citire if((fp=fopen(argv[1],"r"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } fscanf(fp,"%lf %d %s",&ld, &d, str); printf("%lf %d %s",ld, d, str);\ fclose(fp); }

Citirea i scrierea datelor binare Orict de utile ar fi funciile fprintf() i fscanf(), ele nu constituie cel mai eficient mod de a scrie i de a citi date n, sau dintr-un fiier, deoarece ambele se bazeaz pe conversii de date. De exemplu, cnd scriem un numr ntr-un fiier folosind funcia fprintf(), numrul este transformat din formatul intern n format ASCII. Cnd citim un numr dintr-un fiier folosind funcia fscanf(), el este convertit napoi n format intern. Pentru o mare parte din aplicaii, timpul ocupat de aceste conversii nu este semnificativ, dar pentru altele, durata conversiilor va constitui o limitare sever. Mai mult chiar, pentru unele tipuri de date, un fiier creat cu funcia fprintf() va fi mai mare dect acelai fiier n care datele sunt scrise n formatul intern. Din acest motiv, limbajul C include dou funcii foarte importante, numite fread() i fwrite(). Aceste dou funcii pot citi i scrie orice tip de dat, folosind orice fel de reprezentare, iar prototipul lor este: size_t fread (void *buffer, size_t size, size_num, FILE *fp); size_t fwrite (void *buffer, size_t size, size_t num, FILE *fp); Dup cum se poate observa, aceste dou prototipuri introduc unele elemente cu care nu ne-am familiarizat nc. nainte de prezentarea lor, este necesar descrierea celor dou funcii. Funcia fread() citete din fiierul asociat cu fp, num obiecte, fiecare obiect avnd lungimea de size bytes, ntr-un buffer punctat de buffer. Funcia returneaz numrul de obiecte citite. Dac aceast valoare este 0, nu a fost citit nici un obiect, adic a aprut o eroare sau a fost atins sfritul fiierului (se pot utiliza funciile feof() sau ferror() pentru a afla care din aceste dou evenimente s-a produs). 74

Funcia fwrite() este opus lui fread(). Ea scrie n fiierul asociat cu fp, num obiecte, fiecare obiect avnd lungimea de size bytes din bufferul punctat de buffer. Ea returneaz numrul de obiecte scrise. Aceast valoare poate fi mai mic dect num numai dac apare o eroare de ieire. nainte de a studia exemplele, s aruncm o privire asupra conceptelor noi introduse de prototipul acestor funcii. Primul concept este acela de pointer void. Un astfel de pointer este cel care poate puncta orice tip de dat, fr s fie nevoie de folosirea unui operator cast. n general, acesta este numit generic pointer. n C, pointerii void sunt folosii pentru dou scopuri primare: aa cum ilustreaz fread() i fwrite(), ei constituie modul prin care o funcie poate primi un pointer ctre orice tip de dat, fr ca aceasta s conduc la o eroare. Aa cum am mai artat, cele dou funcii pot fi folosite pentru a scrie sau pentru a citi orice tip de dat; astfel spus, cele dou funcii tr s fie capabile s primeasc orice tip de dat punctat de buffer; permit unei funcii s returneze un generic pointer. Al doilea concept nou este size_t. Acest tip este definit n fiierul header STDIO.H. O variabil de acest tip este definit de standardul ANSI C ca fiind capabil s conin o valoare egal cu dimensiunea celui mai mare obiect suportat de compilator. Pentru scopurile noastre, se poate presupune size_t ca fiind acelai cu unsigned sau unsigned lond. Motivul pentru care este folosit size_t n locul echivalenilor si este acela c permite compilatoarelor C care ruleaz n diferite medii s se acomodeze cu cerinele acestora. S se scrie un program care scrie o valoare ntreag ntr-un fiier numit fisier, folosind reprezentarea intern (binar). Programul presupune c ntregii se reprezint pe doi octei.
#include<stdio.h> #include<stdlib.h> int main (void) { FILE *fp; int i; if((fp=fopen("fisier","w"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } i=100; if(fwrite(&i, 2, 1, fp)!=1) { printf("Eroare de scriere.\n"); exit(1); } fclose(fp); if((fp=fopen("fisier","r"))==NULL) { printf("\nNu pot deschide fisierul.\n");

75

exit(1); } if(fread(&i,2,1,fp)!=1) { printf("Eroare de citire.\n"); exit(1); } printf("i este %d",i); fclose(fp); getch(); }

De remarcat modul comod n care se face verificarea apariiei unor erori prin simpla comparare a numrului de elemente citite sau scrise cu numrul de elemente cerute. n anumite sitiaii, se vor putea totui folosi funciile feof() i ferror() pentru a determina dac a fost atins sfritul de fiier sau a aprut o eroare. Acest program nu va putea fi executat pe calculatoare care reprezint ntregii pe patru octei. Mai general, dimensiunea diferitelor tipuri de date se schimb de la sistem de operare la sistem de operare i este destul de dificil de determinat manual. Din acest motiv, limbajul C include cuvntul cheie sizeof care este un operator de compilare care returneaz dimensiunea n bytes a tipului sau variabilei care urmeaz. El are forma general: sizeof(type) sau sizeof(var_name) De exemplu, dac tipul float are lungimea de 4 octei i f este o variabil de tip float, urmtoarele instruciuni sunt evaluate la 4: sizeof f; sizeof (float); Dac utilizm sizeof cu type, atunci type trebuie nchis ntre paranteze. Nu sunt necesare paranteze dac folosim nume de variabile, dar folosirea lor nu conduce la o eroare. Utiliznd sizeof nu numai c economisim timpul necesar determinrii manuale a dimensiunii anumitor obiecte, dar realizm portabilitatea programelor pentru alte medii de programare. n continuare este prezentat o variant mbuntit a programului anterior, care utilizeaz sizeof.
#include<stdio.h> #include<stdlib.h> int main (void) { FILE *fp; int i; if((fp=fopen("fisier","w"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); }

76

i=100; if(fwrite(&i, sizeof(int), 1, fp)!=1) { printf("Eroare de scriere.\n"); exit(1); } fclose(fp); if((fp=fopen("fisier","r"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } if(fread(&i,sizeof i,1,fp)!=1) { printf("Eroare de citire.\n"); exit(1); } printf("i este %d",i); fclose(fp); getch(); }

Accesul random Exemplele prezentate pn acum au scris sau au citit un fiier n mod secvenial, de la nceput i pn la sfrit. Utiliznd o alt funcie a limbajului C, se va putea accesa orice articol al unui fiier. Funcia care ne permite s realizm acest lucru este fseek() i are urmtorul prototip: fseek(FILE *fp, long offset, int origin); n acest caz, fp este asociat cu fiierul ce trebuie accesat. Valoarea offset determin numrul de octei de la origin, care vor determina noua poziie curent. Origin trebuie s fie un macro din cele prezentate n continuare mpreun cu semnificaia lor: SEEK_SET cutare de la nceputul fiierului; SEEK_CUR cutare de la locaia curent; SEEK_END cutare de la sfritul fiierului. Aceste macro-uri sunt definite n STDIO.H. de exemplu, dac dorim ca locaia curent s fie la 100 bytes de la nceputul fiierului, atunci origin va fi SEEK-SET i offset va fi 100. Funcia fseek() returneaz 0 dac operaia de cutare s-a ncheiat cu succes i non-zero dac operaia eueaz. n multe implementri, operaia de cutare se poate face dincolo de sfritul fiierului, dar niciodat nu se poate cuta o locaie situat nainte de nceputul fiierului. Se poate determina locaia curent a fiierului folosind funia ftell(). Prototipul su este: long ftell(FILE *fp); i returneaz locaia curent a fiierului asociat cu fp. Dac apare o eroare, funcia returneaz -1L. n 77

general, accesul random se folosete numai la fiierele binare, deoarece la fiierele text pot avea loc translatri de caractere care fac s nu existe o coresponden direct ntre ceea ce exist n fiier i octetul la care ar trebui s existe ceea ce cutm. Exist o singur situaie n care putem folosi funcia fseek() cu un fiier text i anume atunci cnd cutm o poziie determinat anterior cu ftell(), folosind SEEK_SET ca origin. Reamintim un lucru important: chiar i un fiier care conine numai text poate fi deschis ca un fiier binar. Deci, nu exist restricii n ceea ce privete accesul random la fiierele care conin text. Restriciile vor aprea numai la fiierele deschise ca fiiere text. Ex: S se scrie un program care folosete funcia fseek() pentru a cuta i apoi a citi orice byte dintr-un fiier specificat ca argument n linie comand.
#include<stdio.h> #include<stdlib.h> void main(int argc, char *argv[]) { FILE *fp; long loc; if (argc!=2) { printf ("Specificati numele fisierului in comanda. \n"); exit(1); } if((fp=fopen(argv[1],"rb"))==NULL) { printf("\nNu pot deschide fisierul.\n"); exit(1); } printf("Introduceti locatia ce se va vizualiza:"); scanf("%ld",&loc); if (fseek(fp,loc,SEEK_SET)) { printf("Eroare de cautare..."); exit(1); } printf("Valoarea de la locatia %ld este %d",loc,getc(fp)); fclose(fp); }

78

Bibliografie selectiv recomandat Burileanu, D., Dan, C., Pdure, M., Programare n C. Culegere de probleme, Editura Printech, Bucureti, 2004. Costea, D., Iniiere n limbajul C, Editura Teora, Bucureti, 1995. Cristea, V., Ciumale, C., Kalisz, E., Panoiu, A., Limbajul C standard, Editura Teora, Bucureti, 1992. Dan, C., Burileanu, D., Introducere n programarea calculatoarelor. Limbajul C, Editura Printech, Bucureti, 2001. Kernigham, B.W., Ritchie, D.M., The C Programming Language, Pretice Hall. Knuth, D.E., Tratat de programarea calculatoarelor Algoritmi fundamentali, vol. I, Editura Tehnic, Bucureti, 1973. Nstac, D.I. Programarea calculatoarelor n limbajul C Elemente fundamentale, Editura Printech, Bucureti, 2006. Negrescu, L., Limbajele C i C++ pentru nceptori, Vol. I, Editura Microinformatica, ClujNapoca, 1996. Negrescu, L., Limbajul C Culegere de probleme, Fascicolele 1-2, Cluj-Napoca, 1991. Rusu, I., Gavrilescu, D., Grosu, V., ndrumar de laborator pentru programarea calculatoarelor, Editura MATRIX ROM, Bucureti, 2004. Rusu, I., Gavrilescu, D., Grosu, V., Programarea calculatoarelor n limbaj C, Editura MATRIX ROM, Bucureti, 2002.

79