Sunteți pe pagina 1din 113

BORLAND C

Versiunea 2.0 Manual de utilizare

STRUCTURA GENERAL A UNUI PROGRAM C


ISTORIC, CONCEPIE, EVOLUIE
Limbajul C a fost finalizat n 1972 de Dennis M. Ritchie i Brian W. Kernighan de la firma american Bell Laboratories. Prima versiune a limbajului se numete BCPL apoi urmtoarele poart numele de A, B i C. Cei doi autori au dezvoltat aceste prime versiuni n jurul sistemului de operare UNIX. La vremea respectiv din aproximativ 13000 linii surs ale UNIX-ului doar 200 de linii surs nu erau scrise n limbajul C. De acest fapt se leag detractorii limbajului care spun c limbajul C nu este un limbaj deosebit ci doar un fel de limbaj oficial al sistemului de operare UNIX. n anul 1978 apare manualul The C Programming Language care este de fapt i prima standardizare a limbajului. Cei doi autori intr astfel n istorie... Dup anul 1980 odat cu dezvoltarea hardware apar i primele PC-uri iar acestea implic i produse software adecvate. Principalele firme productoare de sofware -MICROSOFT i BORLAND - au dezvoltat unelte adecvate pentru programarea i utilizarea limbajului C. Deocamdat firma BORLAND deine supremaia prin versiunile mediului BORLAND C. Cele mai folosite sunt versiunile 2.0, 3.1, 4.0. n ultimii doi ani au aprut aa numitele medii visuale: VISUAL C versiunile 4.5 i 5.0 care sunt de fapt extensii ale mediului BORLAND C adaptate programrii orientate obiect i interfeei grafice WINDOWS 95. Mediile de programare BORLANDC pot compila 3 tipuri de programe surs C: fiiere cu extensia .C (fiiere cu prg. standard C); fiiere cu extensia .CP (fiiere cu prg. C+,); fiiere cu extensia .CPP (fiiere cu programe C++).

Menionm c limbajul C++ a fost elaborat de Bjarne Stroustrup de la AT&T. El este un superset al limbajului C i permite principalele concepte ale programrii prin abstractizarea datelor i programrii orientate spre obiecte. Limbajul C este un limbaj hibrid avnd faciliti caracteristice limbajelor de asamblare ct i faciliti ale limbajelor de nalt nivel. Cteva dintre principalele caracteristici ale limbajului C sunt: portabilitate: chiar dac acest concept nu-i definit foarte riguros spunem c un program este portabil dac el poate fi transferat uor de la un tip de calculator la altul; limbajul C este un astfel de limbaj; flexibilitate: compilatorul face un numr mai redus de controale (face multe conversii implicite); programare structurat: limbajul are principalele structuri ale programrii structurate: structura secvenial, structura iterativ i structura de selecie; compactizare: unele instruciuni sunt scrise foarte compact; de exemplu i:=i+1 se poate scrie mai scurt ca i++; lucrul pe bii i calcule cu adrese.

CONCEPTUL DE FUNCIE
Un program C se compune din una sau mai multe funcii. Funcia este o unitate lexical de program compilabil independent. Una dintre funcii este funcie principal, numele ei este predefinit i anume main. Execuia programului ncepe cu prima instruciune din funcia principal. Dm n continuare 2 exemple de structuri de program (fiiere surs):

[directive de preprocesare] [declaraii de date globale] [declaraie prototip funcia f1 . . . declaraie prototip funcia fn] [Void main (void) { declaraii instruciuni instruciuni }] [Implementare funcia f1] . . . [Implementare funcia fn]

[directive de preprocesare] [declaraii de date globale] [declaraie prototip funcia f1 . . . declaraie prototip funcia fn] [Void main (void) { declaraii instruciuni instruciuni }] [implementare funcia f1] . . . [implementare funcia fn]

Funcia principal main este obligatorie pentru orice program celelalte elemente fiind optionale. Pentru ca o anumit funcie s poat fi apelat e necesar ca ea s aib declarat prototipul dac implementarea (definiia) ei se afl dup funcia main (exemplul 1). Dac funcia principal se afl la sfritul fiierului atunci nu mai e necesar prototipul funciei apelate ci doar implementarea ei (exemplul 2). Comparnd structura unui program C cu structura unui program PASCAL se observ nivelul de imbricare diferit. n PASCAL apare o imbricare a procedurilor i funciilor pe cnd n C nu exist o astfel de imbricare (rmne la nivelul fiecrei funcii dac e cazul).

PASCAL C

...

1.2.1. Definiia unei funcii Definiia unei funcii n limbajul C se compune din antet i corp. O funcie poate fi apelat dac este precedat de definiia sau de prototipul ei. Aceste lucruri care sunt valabile n limbajul C se regsesc i n limbajul C++. Antet i prototip Antetul simplificat al unei funcii n C are formatul: tip nume_funcie (lista_parametrilor_formali) unde: tip - reprezint tipul valorii returnate de funcie sau dac funcia nu

returneaz nici o valoare se pune cuvntul cheie void; nume_funcie reprezint un identificator clasic format dintr-un mixaj de litere i cifre, primul caracter fiind obligatoriu litera; printre litere se numr i liniua de subliniere; lista_parametrilor_formali nume de variabile separate prin virgule. Exemple: 1) double radical (double x) gasita 2) double radical_n (double x, int n) // calculeaza radacina de ordinul n din x Prototipul unei funcii este asemntor antetului dar la sfrit se pune caracterul ; 1.2.3. Corpul unei funcii Corpul unei funcii C se compune din declaraii de variabile locale i instruciuni scrise ntre acolade conform figurii de mai jos. { declaraii instruciuni } i pentru c autorii limbajului C consider c un limbaj de programare se nva mai repede scriind i executnd programe ct mai timpuriu vom da un mic exemplu de funcie. int modul (int i) valoare { if (i < 0) if (i = = 0) else } return i; return 0; return i; // determina valoarea absoluta a intregului i si returneaza aceasta // calculeaza radacina patrata din x si returneaza valoarea

1.3. CONSTRUCIILE DE BAZ ALE LIMBAJULUI 1.3.1. Caractere Limbajul C folosete setul de caractere al codului ASCII care se codific prin numere ntregi din intervalul [0,127], adic 128 de coduri. Un astfel de ntreg se pstreaz pe un BYTE (OCTET) adic pe 8 bii. Mulimea caracterelor se mparte n trei grupe: - caractere negrafice (coduri cuprinse ntre 00=NUL i 31) i 127=DEL; - spaiu (codul 32); - caractere grafice (coduri cuprinse ntre 33 i 126). Caracterele grafice se mpart la rndul lor n: litere mari (coduri ntre 65 i 90); litere mici (coduri ntre 97 i 122); cifre (coduri ntre 48 i 59); caractere speciale (celelalte coduri). Caracterele negrafice au diferite funcii. Astfel codul 10 semnific LF (Line Feed), adic deplaseaz cursorul pe linia urmtoare n coloana 1, iar codul 13 semnific CR (Carriage Return) adic deplaseaz cursorul n coloana 1 aceeai linie. n limbajul C combinaia celor dou caractere se noteaz prin: \n i are semnificaia trecerii cursorului la linie nou i coloana 1 (newline). Tabulatorul orizontal (deplasarea cursorului peste un anumit numr de poziii) se precizeaz prin notaia:

\t S mai precizm c urmtoarele 3 caractere: spaiu, tabulator orizontal se mai numesc spaii albe (white spaces). 1.3.2. Nume

newline

n limbajul C, un nume este o succesiune de litere i eventual cifre, care ncepe cu o liter. Ca lungime un nume poate fi orict de lung dar numai primele 32 de caractere se iau n considerare. Folosind notaia BNF (vezi anexa) un nume se poate defini recursiv, ca mai jos: <nume> ::= <litera> | <nume><litera> | <nume><cifra> <litera> ::= A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Z| a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|z|_ <cifra> ::= 0|1|2|3|4|5|6|7|8|9| Numele se folosesc pentru denumirea variabilelor, tablourilor, funciilor, etc. Exemple: A, _start, a_, matrice, matrice_patratica. Dm i cteva contraxemple: &B - conine caracterul &; x+y - conine caracterul +; 1.3.3. Cuvinte cheie Un cuvnt cheie este un cuvnt mprumutat din limba englez, care are un neles predefinit. Aceste cuvinte se scriu cu litere mici. Un cuvnt cheie nu poate avea alt utilizare ntr-un program C dect cea care i-a fost predefinit. Fiind o succesiune de litere, un cuvnt cheie este un nume. Lista cuvintelor cheie se d n anexa de la sfritul crii. Sensul fiecrui cuvnt cheie va rezulta la definirea construciei n care se utilizeaz. Exemple: for, do, if, while, else, break, return, int, long, double, static, extern. 1.3.4. Tipuri de baz n limbajul C distingem cteva tipuri predefinte de date care se mai numesc tipuri de baz. n general un tip de dat are trei atribute bine conturate: mulimea valorilor; reprezentarea (intern i extern) comportamentul (adic operaiile ce se pot face cu acel tip). n tabelul de mai jos dm cuvntul cheie, mulimea valorilor, reprezentarea intern. Cuvnt cheie Mulimea valorilor binar binar int short Reprezentarea intern Lungime (bii) Formatul intern 16 16 reprezentare reprezentare

ntregii din [-215; 215-1] ntregii din [-215; 215-1]

long ntregii din [-231; 231 - 1] unsigned ntregii din [0; 216-1] char float [-3.4*10-38; 3.4*1038] simpl precizie double [-1.7*10-308; 1.7*10308] dubl precizie

32 reprezentare binar 16 reprezentare binar cod ASCII 32 virgul flotant 64 virgul flotant

Facem precizarea c se poate folosi i combinaia unsigned long pentru ntregii fr semn n dubl precizie. Cu tipurile de baz se pot defini i tipuri utilizator folosind instruciunea struct. 1.3.5. Constante O constant are un tip i o valoare. Att tipul ct i valoarea unei constante se definesc prin caracterele care compun constanta respectiv. Constant ntreag zecimal O constant ntreag este un ir de cifre care eventual este prefixat de un semn (+ sau -) i postfixat de litera L sau l. O constant ntreag se reprezint n binar pe 16 sau 32 de bii. Sufixul L sau l foreaz reprezentarea pe 32 de bii. Constant ntreag octal O constant ntreag octal este un ir de cifre octale (cifre cuprinse ntre 0 i 7) precedat de un zero nesemnificativ. Constant ntreag hexazecimal O constant ntreag hexazecimal este un ir de cifre hexazecimale prefixat cu 0x sau 0X. Exemple: Reprezentare Interpretare 12345 ntreg zecimal reprezentat n binar pe 16 bii -12345 ntreg zecimal reprezentat n binar pe 16 bii 12345L ntreg zecimal reprezentat n binar pe 32 bii 012345 ntreg octal reprezentat n binar pe 16 bii (o cifr octal pe 3 bii) 0xabcd ntreg hexa reprezentat n binar pe 16 bii (o cifr pe 4 bii) 12345678 ntreg zecimal reprezentat pe 32 de bii

Constant flotant O constant flotant este un numr raional care se compune din urmtoarele elemente: un semn (+ sau -) care poate lipsi pentru numerele pozitive; o parte ntreag care poate fi i vid; o parte fracionar care poate fi i vid; un exponent care poate fi i vid. Prezena prii fracionare este suficient pentru ca numrul respectiv s reprezinte o constant flotant. Absena prii fracionare implic prezena prii ntregi i a exponentului. Partea ntreag reprezint o succesiune de cifre zecimale. Partea fracionar se compune din caracterul punct urmat de o succesiune de cifre zecimale, care poate fi i vid. Exponentul se compune din litera e sau E urmat de un + sau -, opional, i un ir de cifre zecimale. Constantele flotante se pstreaz n format flotant dubl precizie. Exemple: 3.14; 0.314e1; 3.1415926; 2.71828; 2.; 271828E-5. Constant caracter O constant caracter reprezint un caracter i are ca valoare codul ASCII al caracterului respectiv. O constant caracter grafic se poate scrie incluznd caracterul respectiv ntre caractere apostrof.

Exemple: Constant caracter valoare cod ASCII A B a 0 9 * 65 66 97 48 58 77

Anumite caractere negrafice au notaii speciale la scrierea crora se utilizeaz caracterul backslash (\). Dm n continuare un tabel cu cele mai folosite caractere negrafice: Caracter Reprezentare ca i constant caracter Valoare cod ASCII Backspace Retur de car Newline Apostrof Backslash Tabulator vertical Salt pagin imprimant Carcterul NUL \b \r \n \ \\ \v \f \0 8 13 10 39 92 11 12 0

Constant ir de caractere O constant ir de caractere este o succesiune de zero sau mai multe caractere delimitate prin ghilimele. Ghilimelele nu fac parte din irul de caractere. Exemple: 123; Limbajul C; sir de caractere; sir\; (irul vid). Constanta ir de caractere se reprezint n memoria calculatorului printr-o succesiune de octei n care se pstreaz codurile ASCII ale caracterelor irului, iar ultimul octet conine caracterul NUL care marcheaz sfritul irului de caractere. Dup cum se observ din exemplele date putem s folosim i convenia cu backslash. De reinut c X i X reprezint construcii diferite. 1.3.6. Variabile simple Prin variabil nelegem o dat a crei valoare se poate schimba n timpul execuiei programului care o conine. Unei variabile i se ataeaz un nume prin intermediul cruia o putem referi sau modifica. Totodat valorile pe care le poate lua o variabil trebuie s aparin aceluiai tip. Corespondena dintre numele i tipul unei variabile se realizeaz printr-o construcie special numit declaraie. Dup modul de grupare datele sunt: date izolate denumite i variabile simple; date grupate: - mulimi ordonate de date de acelai tip la care ne referim cu indici, - structuri n care date de tipuri diferite se grupeaz. Declaraia de variabil simpl are formatul general: tip list_de_nume; unde list_de_nume este format fie dintr-un singur nume fie din mai multe, separate prin virgule.

n limbajul utilizate. Exemple: int a,b,c; float x,y,z; char car; long i,j,k; Tablouri

C trebuie declarate toate variabilele nainte de a fi // a, b, c sunt variabile de tip int reprezentate in binar pe 2 octei; // x, y, z sunt variabile de tip float in format flotant simpla precizie pe 32 de bii; // car este o variabila de tip char pe 1 octeti poate conine coduri ASCII; // i, j, k sunt variabile de tip long in format binar pe 32 de bii.

Un tablou ca orice variabil simpl trebuie declarat nainte de a fi utilizat. Dac referirea la elementele tabloului se face cu un singur indice se spune c tabloul este unidimensional (se mai numete vector); dac referirea se face cu doi indici tabloul se numete bidimensional(matrice); iar cu n indici tabloul este n-dimensional. Declaraia unui tablou n forma cea mai simpl este: tip nume[l1][l2]...[ln]; unde: l1, l2, ... ln sunt expresii constante care au valori ntregi ce pot fi evaluate de compilator la ntlnirea lor. Evident c se pot declara mai multe tablouri deodat i atunci numele de tablouri se pot nirui ntr-o list, fiecare separat prin virgul. Exemple: int t[5]; float a[5][3]; // s-a declarat un tablou unidimensional de 5 componente; // s-a declarat un tablou bidimensional de 5*3=15 componente;

La elementele unui tablou ne referim prin variabile cu indici. O variabil cu indici se compune din numele tabloului urmat de unul sau mai muli indici, fiecare indice fiind inclus n paranteze drepte. Numrul indicilor definete dimensiunea tabloului. Indicii sunt expresii care au valori ntregi. Limita inferioar a indicilor este zero. Astfel dac t este tabloul de tip int n exemplul de mai sus, elementele lui sunt: t[0], t[1], t[2], t[3], t[4]. n cazul matricii a declarate mai sus elementele ei vor fi: prima linie: a[0][0], a[0][1], a[0][2]; a doua linie: a[1][0], a[1][1], a[1][2]; ... a cincea linie: a[4][0], a[4][1], a[4][2]; Deci pentru dimensiunea k indicele variaz ntre 0 i lk-1. Repartizarea memoriei pentru un tablou se face astfel: pentru fiecare element al tabloului se repartizeaz o zon de memorie necesar n conformitate cu tipul tabloului respectiv (pentru tipul int cte 2 octei, pentru tipul float cte 4 octei, etc). Numele unui tablou este un pointer, el poate fi utilizat n diferite construcii i el are ca valoare adresa primului su element. n desenul urmtor se indic repartizarea memoriei pentru tabloul t din exemplul de mai nainte: t adresa lui t[0] t[0] t[1] t[2] t[3] t[4]

Comentariu n limbajul C un comentariu se scrie ntre /* i */ pe oricte rnduri. ntre /* i */ se poate scrie o succesiune arbitrar de caractere,

care ns nu poate s conin secvena de terminare (adic */). Comentariul poate fi inserat ntr-un program n orice poziie n care este legal s apar un caracter alb. El constituie o explicaie pentru programator sau pentru utilizator. Compilatorul nu interpreteaz comentariul n nici un mod. Comentariul se recomand a fi inserat n punctele n care programatorul poate lmuri prin explicaii, anumite aspecte ale procesului de calcul sau ale datelor utilizate. n mediile BORLAND C exist i o alt convenie de a marca un comentariu la nivelul unui rnd cu ajutorul a dou caractere slash (//), convenie pe care am folosit-o de altfel n exemplele anterioare. 1.4. PREPROCESARE Un program surs C poate fi prelucrat nainte de a fi compilat. O astfel de prelucrare se numete preprocesare sau precompilare. Acest lucru se realizeaz printr-un program special numit preprocesor. Acesta este apelat automat nainte de a ncepe compilarea. Preprocesorul limbajului C realizeaz urmtoarele: includeri de alte fiiere (de obicei fiiere surs); definiii i apeluri de macrouri simple; compilare condiionat. 1.4.1. Includerea unui fiier surs Fiierele surs pot fi incluse cu ajutorul directivei include. Dou formate se folosesc pentru a include un fiier surs n locul n care apare directiva (de obicei se pune la nceputul programului): #include specificator_de_fisier sau #include <specificator_de_fisier> unde: specificator_de_fiier trebuie s fie un nume de fiier valid din punct de vedere al sistemului de operare DOS i de obicei are extensia .h sau .c. Prima variant este folosit de obicei cnd fiierele de inclus sunt furnizate de utilizator; dac nu este indicat calea atunci fiierele sunt cutate n directorul curent i apoi n directoarele setate pentru directiva include. A doua variant este folosit pentru ncorporarea unui fiier standard care se caut n directoarele setate pentru directiva include (de obicei astfel de fiiere standard sunt livrate n biblioteci ataate mediului de programare C). Odat localizat fiierul dintr-o directiv include se nlocuiete aceasta prin textul fiierului surs. Deci compilatorul nu va mai ntlni directiva include ci textul fiierului inclus de preprocesor. Includerile de fiiere se fac de obicei la nceput pentru ca textele fiierelor surs (date i funcii) s poat fi utilizate n tot fiierul surs de lucru. De aceea, la nceputul fiierelor surs vom ntlni mai multe includeri de fiiere standard: stdio.h, stdlib.h, etc. Textul unui fiier inclus poate la rndul su s conin directiva include. Fiierul stdio.h (prescurtarea de la standard input output header) conine printre altele i funciile standard de intrare-ieire printf i scanf. Fiierele cu extensia .h se mai numesc i fiiere header (fiiere care se pun la nceputul fiierului surs). Un alt exemplu de fiier header este iostream.h folosit n mediul BORLAND C care conine funciile cin (console input) i cout (console output).

1.4.2. Constante simbolice

Cu directiva define se pot defini constante macrouri. Constantele simbolice se definesc astfel: #define nume succesiune_de_caractere

simbolice

Preprocesorul substituie nume cu succesiune_de_caractere peste tot n fiierul surs care urmeaz poziiei directivei define. Dac succesiune_de_caractere nu ncape pe un rnd atunci se poate continua pe rndul urmtor scriind caracterul \ la sfritul primului rnd. Numele nume definit ca mai sus se spune c este o constant simbolic. Se recomand ca nume s fie scris cu litere majuscule pentru a scoate n eviden c este o constant simbolic. Construcia succesiune_de_caractere folosit pentru a defini o constant simbolic poate la rndul ei s conin alte constante simbolice. O constant simbolic poate fi redefinit (tot cu define) sau poate fi anihilat cu undef (#undef nume). Exemple: 1) #define PROCENT 10 ... #define PROCENT 15 ... #undef PROCENT ... #define DIM 100 // s-au definit doua constante simbolice DIM #define DOI_PI (2*3.1415926) // si DOI_PI ... int vector[DIM]; // DIM va fi inlocuit cu 100 ... x=DOI_PI; ... LECIA 2. CLASE DE VARIABILE (DE MEMORIE) Compilatorul C aloc memorie variabilelor din program de dimensiune corespunztoare tipului fiecreia. Memoria se aloc n 2 moduri: static, repartizat ntr-o zon special asociat programului; dinamic, repartizat ntr-o zon special numit stiv (se comport ca o list LIFO). n funcie de modul cum se aloc memorie, vom distinge mai multe clase de variabile. O prim clas de variabile este aceea a variabilelor globale crora li se aloc memorie pe toat durata execuiei programului i ele pot fi utilizate n orice funcie a programului. Alt clas de variabile este clasa variabilelor locale, aceste variabile au o utilizare local la nivelul unei funcii. 2.1. VARIABILE GLOBALE O variabil global are o definiie i attea declaraii de variabil extern cte sunt necesare. Definiia unei variabile globale coincide sintactic cu o declaraie obinuit, dar care este scris n afara oricrei funcii a programului (fiierului surs). Implicit, definiia unei variabile globale determin ca variabila respectiv s fie definit ncepnd din punctul scrierii ei i pn la sfritul fiierului surs respectiv. De aceea se recomand ca definiiile variabilelor globale s fie scrise la nceputul fiierului surs, pentru a fi accesate n orice funcie a fiierului. // din acest punct al fisierului sursa se substituie // PROCENT cu 10 // de aici PROCENT se substituie cu 15 // din acest punct constanta simbolica PROCENT // isi inceteaza existenta

Pentru a utiliza variabilele globale i n alte funcii situate n alte fiiere surs dect n cel n care sunt definite, ele trebuie declarate ca externe n funciile respective. O declaraie de variabil extern coincide cu o declaraie obinuit doar c ncepe cu cuvntul cheie extern. Exemplu: fiierul n care sunt declarate ca variabile globale fiierul n care sunt folosite ca variabile externe int i; float f; void functie(. . .) void main(void) { extern int i; { i = 10; extern double f; ...... f = 3.14; f = f*i; ...... }} Variabilele i i f sunt declarate n afara funciei main i n afara oricrei funcii, deci sunt variabile globale. Ele pot fi folosite n toate funciile din fiierul surs care conine definiiile lor. Pentru a le utiliza n funcii situate n alte fiiere surs dect n cel n care sunt definite ele sunt declarate ca externe. Deci variabilele i i f sunt declarate ca externe n funcia functie din al doilea fiier surs. Cele dou fiiere surs care pot fi scrise n momente i de persoane diferite se pot asambla ntr-un singur program cu ajutorul directivei de preprocesare include. 2.2. VARIABILE LOCALE Variabilele locale nu sunt valabile n tot programul. Ele au o utilizare local n dou feluri: ca i variabile automatice (alocate pe stiv) la nivelul unei funcii; ca i variabile statice (alocate n zona programului) la nivel de fiier (eventual i la nivelul unei funcii). Variabilele declarate n interiorul unei funcii i care nu sunt declarate ca externe sunt variabile locale. Lor li se aloc memorie pe stiv la intrarea n funcia n care sunt declarate. La ieirea din funcie, stiva se reface la coninutul dinaintea apelului i variabilele locale pierd alocarea. Deci ori de cte ori se apeleaz o funcie, variabilele locale acesteia (denumite i variabile automatice) se aloc (primesc memorie) pe stiv i la ieirea din funcie variabilele locale sunt terse din stiv. Variabilele locale pot s nu fie alocate pe stiv, ci ntr-o zon de memorie destinat acestui scop. O astfel de variabil local se numete variabil static. O variabil local static se declar printr-o declaraie obinuit, precedat de cuvntul cheie static. O variabil static poate fi declarat att n corpul unei funcii ct i n afara oricrei funcii. n cazul unei variabile statice declarat n interiorul unei funcii alocarea nu se face pe stiv ci ntr-o zon de memorie destinat acestui scop, aceasta fiind deosebirea esenial fa de variabilele automatice. n cazul n care o variabil static este declarat n afara funciilor, ea este definit din punctul n care a fost declarat i pn la sfritul fiierului surs care conine declaraia ei. Spre deosebire de variabilele globale, o variabil static nu poate fi declarat ca extern i deci nu poate fi utilizat n funciile din alte fiiere surs diferite de cel n care a fost declarat. Se recomand ca tablourile mari s fie declarate statice, deoarece dac sunt automatice pot depi capacitatea stivei (care are implicit 4K octei). Exemple:

Fiierul fisier1.c este un fiier surs care conine 2 variabile globale i i d , o variabil static x i dou funcii f i main. Funcia main conine variabila static a iar funcia f conine variabila static b. int i; double d; static int x; // definiia variabilei globale i // definiia variabilei globale d // definiia variabilei statice x, locala fisierului fisier1.c

void main (void) { static char a; // definiia variabilei statice a, locala funciei main float c; // definiia variabilei automatice c, locala funciei main /* in acest moment se pot folosi variabilele i,d,x,a si c */ ... } { int p; // defini ia variabilei automatice p, locala func iei f static float b; // defini ia variabilei statice b, locala func iei f /* in acest moment se pot folosi variabilele i,d,x, p si b */ ... } Variabilele a i c fiind locale funciei main nu pot fi utilizate n funcia f. Analog, variabilele p i b sunt locale n funcia f, nu pot fi utilizate n funcia main. Fiierul fisier2.c conine funciile f1 i componena aceluiai program ca i funciile main fisier1.c f2 i care intr n f din fiierul

static unsigned t; // definitia variabilei statice t, locala fisierului fisier2.c void f1(...) { extern int i; // declaratie externa pentru i extern double d; // declaratie externa pentru d static int k; // definitia variabilei statice k, locala functiei f1 /* in acest moment se pot folosi varibilele i,d, k si t */ ... } void f2(...) { extern int i; // declaratie externa pentru i static double s; // definitia variabilei statice s, locala functiei f2 /* se pot folosi variabilele i, s si t */ ... } Variabila static x definit n fiierul fisier1.c nu poate fi utilizat n fiierul fisier2.c. De asemenea, variabila static t nu poate fi utilizat n fiierul fisier1.c. Variabila global d nu poate fi utilizat n funcia f2, ea nefiind declarat ca i extern. Observaii: 1o. Variabilele globale constituie un mijloc simplu de interfa ntre funciile unui program. Se recomand a fi folosite cnd dorim transferuri de valori ntre dou sau mai multe funcii n aa fel nct modificrile realizate de o funcie s fie accesibile pentru toate funciile programului. Nu trebuie s se fac abuz n utilizarea variabilelor globale deoarece constituie i o surs potenial de erori, pentru c accesul unui mare numr de funcii la anumite date globale conduce deseori la modificri nedorite i greu evideniabile. 2o. Funciile sunt considerate cu atributul implicit extern. Dac ntr-un program exist mai multe fiiere surs atunci o funcie poate fi apelat oriunde, bine neles respectnd convenia definirii ei sau a prototipului ei nainte de a fi folosit. Putem s limitm definind funciile cu atributul static precednd antetul funciei prin cuvntul

cheie static. Astfel funcia respectiv devine local i deci apelabil doar n fiierul n care este definit. 2.3. VARIABILE REGISTRU Limbajul C ofer posibilitatea de a aloca anumite variabile n regitri microprocesorului. Deoarece regitri constituie un tip de memorie ultrarapid n anumite cazuri se poate economisi att timp de execuie ct i memorie. Se pot aloca n regitri numai parametrii funciilor i variabilele automatice de tip int, char i de tip pointer. O variabil registru se declar n mod obinuit, precednd declaraia ei prin cuvntul rezervat register. Alocarea ntr-un registru a unei variabile se face numai dac exist un registru disponibil. n caz contrar, variabila registru se aloc pe stiv exact ca o variabil automatic. Alocarea se face n ordinea declarrii variabilelor registru. Exemplu: void f (register int i) { register char c; register int j; ... } Mai nti se aloc parametrul i ntr-un registru, apoi se aloc c i j n ali doi regitri. Se recomand alocarea n regitri a variabilelor care au o utilizare mare, de exemplu a indicilor de tablouri. 2.4. INIIALIZARE Variabilelor li se pot atribui valori iniiale. Pentru variabilele globale valorile iniiale se atribuie prin definiiile lor, iar n cazul celorlalte variabile se pot atribui valori prin declaraiile lor. Pentru c de multe ori am folosit cuvintele definiia variabilelor sau declaraiile varibilelor precizm c ele au neles distinct n anumite contexte. O variabil global se definete o singur dat i se poate declara ori de cte ori e necesar utilizarea ei n alte fiiere (evident declarat extern). n cazul celorlalte tipuri de variabile definiiile i declaraiile coincid. Totodat definiia i declaraia (prototipul) unei funcii nu coincid. O variabil simpl se poate iniializa printr-o declaraie de forma: tip nume=expresie; Variabilelor globale i statice li se atribuie valori iniiale la lansarea programului. Expresia utilizat n cazul acestor variabile trebuie s fie o expresie constant care s poat fi evaluat de compilator la ntlnirea ei. Aceasta, deoarece variabilele globale i statice se iniializeaz prin valori definite la compilare. Variabilele automatice se iniializeaz la execuie, de fiecare dat cnd se activeaz funcia n care sunt declarate. Din aceast cauz, nu mai este necesar ca expresia s fie o expresie constant. Totui, la ntlnirea ei, trebuie s fie definii operanzii expresiei de iniializare. Exemplu: void f(int n) { int i=10; int k=i+n; ... } La ntlnirea expresiei i+n sunt deja definii ambii operanzi:

i a fost n prealabil iniializat cu valoarea 10; n are o valoare care e transferat la apel. Variabilele globale i statice neiniializate au implicit valoarea egal cu zero, iar varibilele automatice neiniializate au o valoare iniial imprevizibil. Tablourile se pot iniializa printr-o list de expresii incluse ntre acolade. Astfel un tablou unidimensional se poate iniializa folosind urmtorul format: tip nume[n] = { exp1, exp2, . . . expn } La iniializarea tablourilor se pot utiliza numai expresii constante. Numrul expresiilor poate fi mai mic dect numrul elementelor tabloului. Elementele neiniializate au valoarea zero n cazul tablourilor globale i statice. Dac se iniializeaz fiecare element al tabloului atunci numrul n al elementelor acestuia nu mai este obligatoriu n declaraia tabloului respectiv. Deci se poate scrie astfel: tip nume[ ] = { exp1, exp2, . . . expn}; Numrul elementelor tabloului se consider c este egal cu cel al expresiilor care realizeaz iniializarea lor. Pentru un tablou bidimensional vom folosi urmtoarea structur: nti tip nume [n][m] = { {exp11, exp12, . . . exp1m}, // pentru linia {exp21, exp22, . . . exp2m}, // pentru linia a doua . . . {expn1, expn2, . . . expnm}, // pentru ultima linie }; Numrul expresiilor poate fi mai mic dect m n oricare din acoladele corespunztoare celor n linii ale tabloului bidimensional. n acest caz se poate omite doar numrul n (al liniilor tablourilor), m fiind obligatoriu. Formatul de iniializare a tablourilor bidimensionale se extinde imediat pentru tablouri cu mai multe dimensiuni. Tablourile de tip caracter pot fi iniializate folosind un format mai simplu dect cel indicat anterior. Astfel. un tablou de tip caracter se poate iniializa printr-o declaraie de forma: char sir[n] = ir_de_caractere; Evident, marginea superioar n poate fi omis i n acest caz. Totodat compilatorul pune automat caracterul NUL dup ultimul caracter al irului utilizat n iniializare. Exemple: 1) int itab[] = {1,2,3,4,5} // tabloul de tip intreg are 5 elemente itab[0] = 1,. . . itab[4] = 5 // tabloul are 3 linii si 3 coloane.

2) int m[3][3] = {{-1,0,1},{-1},{0,1}};

Elementele primei linii sunt iniializate astfel: m[0][0] = 1; m[0][1] = 0; m[0][2] = 1; n linia a doua se iniializeaz numai m[1][0] = -1; Dac tabloul ar fi declarat global sau static atunci m[1][1] i m[1][2] ar avea valoarea zero. Altfel ele au o valoare imprevizibil. Elementele ultimei linii se iniializeaz astfel: m[2][0] = 0; m[2][1] = 1;

declaraiile de mai jos sunt identice: char sir [ ] = {L,I,M,B,A,J,U,L, ,C}; char sir [ ] = {LIMBAJUL C}; LECIA 3. EXPRESII, OPERANZI, OPERATORI 3.1. EXPRESII O expresie n limbajul C este format fie dintr-un operand fie din mai muli operanzi legai ntre ei prin operatori. O expresie are o valoare i un tip care se determin aplicnd operatorii conform prioritilor i asociativitii acestora. n limbajul C operatorii se asociaz de la stnga la dreapta, exceptnd operatorii unari i de atribuire, care se asociaz de la dreapta la stnga.. Totodat pot fi folosite parantezele rotunde pentru a impune o anumit ordine n executarea operaiilor. 3.2. OPERANZI Un operand n limbajul C poate fi una din urmtoarele elemente: o constant; o constant simbolic; numele unei variabile simple; numele unui tablou; numele unei structuri; numele unei funcii; referirea la elementul unui tablou (variabil cu indici); referirea la elementul unei structuri; apelul unei funcii; o expresie inclus n paranteze rotunde. Unele dintre elementele de mai sus nu au fost nc definite, ele se vor prezenta n leciile viitoare. Exemple: 9876 - constant ntreag; x - variabil simpl; t[i][3] - variabil cu indici; 0xabcd - constant hexazecimal; t - nume de tablou; (expresie) - expresie inclus n paranteze rotunde. f1 - numele unei funcii 3.3. OPERATORI Operatorii limbajului C pot fi grupai n mai multe clase, dar oricum ei pot fi folosii mpreun ntr-o aceeai expresie. Operatorii au ariti diferite: unari, binari, ternari i totodat o anumit prioritate implicit care e redat n tabelul de mai jos. Operatorii de aceeai prioritate se afl trecui n aceeai linie. Liniile tabelulul conin operatorii limbajului C n ordinea descresctoare a prioritilor. Astfel n prima linie se afl operatorii de prioritate maxim, iar n ultima linie operatorul virgul cu prioritatea cea mai mic. Cu excepia operatorilor ., ->,&,*, a parantezelor rotunde (folosite la definiia i apelul funciilor) i a parantezelor drepte (folosite la variabilele cu indici) ceilali operatori vor fi explicai n aceast lecie. ( ) [ ] . -> - (unar) +(unar) * / % + << >> < <= >= > *(unar) &(unar) ! ~ ++ -(tip)

sizeof

= = != & ^ | && | | ? : (ternar) = op= op poate fi: *(binar) / % +(binar) (binar) << >> & ^ | , 3.3.1. Operatori aritmetici Lista operatorilor aritmetici este redat mai jos: - (minus unar); + (plus unar); * / % operatori binari multiplicativi; (nmulire, mprire, restul mpririi ntregi); + - operatori binari aditivi (adunare i scdere). Operatorii de pe aceeai linie au aceeai prioritate. Cei unari au prioritate mai mare dect cei binari. Operatorii multiplicativi au prioritate mai mare dect cei aditivi. Exemple: int i,j,k; float x,y; double t[10]; // se dau cateva exemple de expresii folosind operatorii aritmetici i*x+t[5]; -y+k; i%j; // daca i=9 si j=4 atunci i%j are valoarea 1 i/j; // daca i=9 si j=4 atunci i/j are valoarea 2 x*-y; // - este operatorul unar deci avem x*(-y) 3.3.2. Operatori relaionali Lista operatorilor relaionali este redat astfel: < (mai mic) <= (mai mic sau egal; cele dou caractere ce compun operatorul sunt concatenate) > (mai mare) >= (mai mare sau egal; cele dou caractere ce compun operatorul sunt concatenate) Toi operatorii relaionali au aceeai prioritate. Ea este mai mic dect prioritatea operatorilor aditivi. Rezultatul aplicrii unui operator relaional este 1 sau 0, dup cum operanzii se afl n relaia definit de operatorul respectiv sau nu. Exemple: a= 4 i b= -5 atunci a>0 are valoarea 1; a<=0 are valoarea 0; a+b>0 are valoarea 0; a>=b are valoarea 1; a<0 are valoarea 1; a+b>=b-a are valoarea 1; a+b<=(b-a)*(b-a) are valoarea 0; 3.3.3. Operatori de egalitate Lista operatorilor de egalitate este redat mai jos:

= = (egal; dou semne = concatenate) != (diferit; semnele sunt concatenate). Operatorii de egalitate au ambii aceeai prioritate i este imediat mai mic dect a operatorilor relaionali. Operatorul = = testeaz egalitatea a doi operanzi. Dac operanzii sunt egali atunci rezultatul operaiei = = este 1, n caz contrar este 0. Operatorul != furnizeaz rezultatul 1 cnd cei doi operanzi sunt diferii i 0 cnd sunt egali. Exemple: a= 2 i b=-1 atunci a= =b are valoarea 0; a!=b are valoarea 1; a*b!=a+b are valoarea 1; 3.3.4. Operatori logici Lista operatorilor logici este redat mai jos: ! (negaia logic - operator unar); && (I logic); || (SAU logic). Operatorul ! are aceeai prioritate cu operatorii unari + i -. Operatorul && este mai prioritar dect operatorul ||, dar are o prioritate mai mic dect operatorii de egalitate. n limbajul C nu exist valori logice speciale. Valoarea fals se reprezint prin zero. Orice valoare diferit de zero reprezint valoarea adevrat. Dac operatorul ! se aplic la un operand a crui valoare este zero, atunci rezultatul este 1. Dac acelai operator se aplic la un operand a crui valoare este diferit de zero, atunci rezultatul este 0. Dm n continuare tabelele operatorilor logici binari aplicate valorilor 0 i 1. && 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0 || 0 1 sau exclusiv 0 1

Chiar dac pentru sau exclusiv nu exist operator el se poate realiza prin expresia urmtoare aplicat operanzilor a i b: !a&&b||!b&&a sau folosind parantezele rotunde ((!a) &&b)||((!b)&&a). Operatorii logici se evalueaz de la stnga la dreapta. Dac la evaluarea unei expresii se ajunge ntr-un punct n care se cunoate valoarea ntregii expresii, atunci restul expresiei nu se mai evalueaz. Dac a=0 i b=1 atunci expresia ! a||b are valoarea 1 pentru c !a are deja valoarea 1. 3.3.5. Operatori logici pe bii Lista operatorilor logici pe bii este redat mai jos n ordinea descrectoare a prioritilor: ~ (operator unar; complement fa de 1) >> << (deplasri la dreapta, respectiv la stnga) & (I pe bii) ^ (SAU-EXCLUSIV pe bii) | (SAU pe bii) Operatorul ~, fiind unar, are aceeai prioritate ca i ceilali operatori unari ai limbajului C. El schimb fiecare bit 1 al operandului n 0 i invers.

Operatorul >> realizeaz deplasarea echivalent cu o mprire ntreag cu puteri a echivalent cu [a/23]. Operatorul << realizeaz deplasarea echivalent cu o nmulire cu puteri a lui 2; a << a*8.

la dreapta care lui 2; a >> 3

este este

la stnga care este 3 este echivalent cu

Pentru operatorii &, |, ^ dm n continuare tabelele operaiilor: & 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0 | 0 1 ^ 0 1

Observaii: 1o. Operanzii care nu ocup un cuvnt (16 bii) se extind la un cuvnt. De exemplu expresia ~0 are ca rezultat un cuvnt cu toi bii egali cu 1. 2o. Operatorii logici pe bii se execut bit cu bit spre deosebire de operatorii logici care se evalueaz global. De exemplu dac x=2 i y=1 sunt variabile de tip int atunci: x&&y are valoarea 1 pentru c ambii operanzi sunt diferii de 0. x&y are valoarea 0 conform schemei de mai jos 0000 0000 0000 0010 0000 0000 0000 0001 & 0000 0000 0000 0000 3o. Operatorul & se folosete frecvent pentru a anula bii din configuraia unui cuvnt, iar operatorul | pentru a seta (pune) bii ntrun anumit mod; 4o. Operanzii trebuie s fie ntregi (de tipul int sau long). 5o. Atenie la deplasri nu se modific valoarea operandului; deci trebuie s facem o atribuire; de exemplu a = a << 3 va modifica valoarea lui a pe cnd a << 3 nu modific valoarea lui a. Exemple: Fie declaraia: int i; atunci expresia i >> 8 & 255 are ca rezultat valoarea celui mai semnificativ octet a lui i; i >> 8 deplaseaz octetul mai semnificativ al lui i n poziia mai puin semnificativ; se face apoi un I logic pe bii cu masca 255 care pstreaz octetul mai puin semnificativ. 2) Fie expresia: (x >> 6) & ~(~ 0 << 3) S presupunem c x are valoarea n bii 1010 1011 1000 1101. Atunci x>>6 are valoarea 1111 1110 1010 1110 Al doilea operand pregtete o masc astfel: ~0 1111 1111 1111 1111 ~0<<3 1111 1111 1111 1000 ~(~0<<3) 0000 0000 0000 0110 Rezultatul final este 0000 1111 0000 dat de: 0000 0000 0111 1110 1010 1110 0000 0000 0110

Practic s-a obinut valoarea biilor 8,7,6 a lui x (numerotai de la dreapta la stnga ncepnd cu 0).

3.3.6. Operatori de atribuire n forma cea mai simpl operatorul de atribuire se noteaz cu = i se utilizeaz n construcii de forma: v=expresie; (v este fie o variabil element de structur). simpl, fie variabil cu indici sau un

Aceast construcie se mai numete expresie de atribuire. Ea este considerat ca fiind un caz particular de expresie. Tipul ei coincide cu tipul lui v, iar valoarea ntregii expresii este chiar valoarea atribuit lui v. O expresie de forma: v1=(v=expresie); este i ea legal i se efectueaz n felul urmtor : se evalueaz expresia expresie i valoarea ei se atribuie lui v; valoarea lui v se atribuie apoi i lui v1. Deoarece operatorii de atribuire se asociaz de la dreapta la stnga, expresia de mai sus se poate scrie i fr paranteze: v1=v=expresie; n general, putem realiza atribuiri multiple printr-o expresie de forma: vn =. . . =v1=v=expresie Dac expresia din dreapta semnului egal are un tip diferit de cel al variabilei v, atunci nti valoarea ei se convertete spre tipul variabilei v i pe urm se realizeaz atribuirea, Pentru operaia de atribuire, n afara semnului egal se mai poate folosi i succesiunea : op= unde prin op se nelege unul din operatorii binari aritmetici sau logici pe bii, adic unul din urmtorii: % / * - + & ^ | << >> Acest mod de construcie se folosete pentru a compacta un anumit tip de atribuire. Astfel expresia: v op = expresie; este identic cu expresia de atribuire: v = op expresie; Exemple: int i, j; double x, y; int v[10]; i=5; j=10; x=y=10.01; i +=1; // echivalenta cu i=i+1 si cu i++ x*=3; // echivalenta cu x=x*3 j<<=10; // echivalenta cu j=j<<10 v[i]*=i // echivalenta cu v[i]=v[i]*i x /= x-y // echivalenta cu x = x/(x-y)

3.3.7. Operatori de incrementare i decrementare Aceti operatori sunt unari i au aceeai prioritate cu ceilali operatori unari ai limbajului C. Operatorul de incrementare se noteaz prin ++ i mrete valoarea operandului cu unu, iar operatorul de decrementare se noteaz prin - - i micoreaz valoarea operandului cu unu. Operatorii sunt folosii prefixat i postfixat. Astfel operatorii prefixai au notaia: ++operand; - - operand; Ei se aplic mai nti i apoi se folosete valoarea lor. Astfel operatorii postfixai au notaia: operand++; operand - -; Se folosete valoarea operanzilor i apoi se aplic incrementarea sau decrementarea. Menionm c aceti operatori se pot aplica numai la urmtorii operanzi: variabil simpl; variabil cu indici; referire la elementul unei structuri. Exemple: int i,j; double x,y; int vector [5]; j=i++; y=--x; i=++vector[j]

// este echivalent cu j=i si i=i+1; // este echivalent cu x=x-1 si y=x; // este echivalent cu vector[j]=vector[j]+1 si i=vector[j]

3.3.8. Operatorul de conversie explicit (expresie cast) forma: Pentru forarea tipului unui operand se folosete o construcie de (tip) operand Prin aceasta valoarea operandului se convertete spre tipul indicat n paranteze. Exemplu: int i,j; double y; i=8; j=5; y=i/j; // y are valoarea 1, pentru ca se face impartirea intreaga i/j Dac vom converti operanzii i i j spre tipul double se va obine rezultatul corect adic 1.6. Deci: int i,j; double y; i=8; j=5; y=(double) i / (double) j; // y are valoarea 1.6, Construcia (tip) este un operator unar prin care se expliciteaz conversia dorit. Are aceeai prioritate ca restul operatorilor unari. 3.3.9. Operatorul dimensiune (sizeof)

Pentru a determina lungimea n octei a unei date se poate folosi construcia: sizeof (data) unde data poate fi: numele unei variabile simple; numele unui tablou; numele unei structuri; numele unui tip; referirea la elementul unui tablou sau structur. Exemple: int i; long l; float f; double d; char c; int itablou[5]; double dtablou[5]; sizeof (i) sizeof (l) sizeof (f) sizeof (d) sizeof (c) sizeof (itablou[1]) sizeof (dtablou[1]) sizeof (itablou) sizeof (dtablou)

// are valoarea 2; // are valoarea 4; // are valoarea 4; // are valoarea 8; // are valoarea 1; // are valoarea 2; // are valoarea 8; // are valoarea 10; // are valoarea 40;

Regula conversiilor implicite n general o expresie C conine operanzi de tipuri diferite. Pentru operatorii binari exist situaii cnd operanzii nu sunt de acelai tip i trebuie executate conversii astfel nct operatorii s se aplice pentru operanzi de acelai tip. Aceste conversii le face automat compilatorul. Exist o regul a conversiilor implicite care are urmtorii pai: fiecare operand de tip char se convertete spre tipul int i fiecare operand de tipul float se convertete spre double; dac unul dintre operanzi este de tip double atunci i cellalt se convertete spre tipul double i rezultatul va avea tipul double; dac unul dintre operanzi este de tip long, atunci i cellalt se convertete spre tipul long i rezultatul va avea tipul long; dac unul dintre operanzi este de tip unsigned, atunci i cellalt se convertete spre tipul unsigned i rezultatul va fi de tipul unsigned; la acest pas se ajunge numai dac ambii operanzi sunt de tip int i deci operaia se execut cu operanzii respectivi, iar rezultatul va fi de tip int. Aplicnd regula de mai sus pas cu pas (la fiecare operator n momentul efecturii lui), se ajunge n final la evaluarea ntregii expresii i prin acesta se determin tipul expresiei. Regula conversiilor implicite nu se aplic pentru operatorul de atribuire (valoarea expresiei din partea dreapt a semnului de atribuire se convertete spre tipul variabilei din stnga semnului egal). Exemple: int i, j, k; float a, b; double x, y;

unsigned p; long r; char c; expresii i-j/k nu a/b x+y i+a i spre double i-3.14 i+3 i+x i-c x+10 p-10 r*5 (double)(i/j) int a spre double b spre double nu a spre double double i spre double nu i spre double c spre int 10 spre double 10 spre unsigned 5 spre long se realizeaz mprirea ntreag ntre i i j i rezultatul se convertete spre double double double double int double int double unsigned long double conversii tipul expresiei

Dac rezultatul unei operaii depete domeniul de valori ce corespunde tipului rezultatului, valoarea respectiv se trunchiaz i rezultatul este eronat. 3.3.11. Operatori condiionali Operatorii condiionali sunt ? i : i se folosesc mpreun n construcii de forma: exp1 ? exp2 : exp3 Evaluarea se face astfel: se evalueaz expresia exp1; dac exp1 este diferit de zero, atunci valoarea i tipul expresiei condiionale sunt egale cu valoarea i tipul expresiei exp2; altfel cu expresia exp3. Exemplu: procesul de determinare a maximului a dou numere a i b este: dac a>b sfdac atunci altfel max=a max=b

n limbajul C se poate realiza acest proces cu ajutorul operatorilor condiionali astfel: max= a>b ? a : b Dac a>b atunci expresia condiional are valoarea i tipul lui a altfel expresia condiional are valoarea i tipul lui b. Operatorul virgul Operatorul , este folosit pentru gruparea mai multor expresii ntruna singur. Cu ajutorul acestui operator (care are prioritatea cea mai mic) se construiesc expresii de forma: exp1, exp2,. . ., expn Aceast expresie are valoarea i tipul ultimei expresii (deci a lui expn). Exemplu: k= (i=10, j=j+5; i+j)

Se execut pe rnd cele dou atribuiri de la stnga la dreapta din parantezele rotunde apoi se face suma i+j i n final se atribuie aceast sum lui k, LECIA 4. INTRRI / IEIRI STANDARD Limbajul C nu posed instruciuni de intrare / ieire. Aceste operaii se realizeaz prin apeluri de funcii din bibliotecile standard ale mediului de programare. De obicei astfel de funcii asigur interfaa programului cu terminalul de la care s-a lansat, cu imprimanta, etc. Se numesc intrri standard i ieiri standard intrrile respectiv ieirile de la terminalul de la care s-a lansat programul. Totodat se presupune c datele de intrare / ieire sunt organizate n fiiere. Unui program C i se ataeaz n mod implicit urmtoarele fiiere: stdin stdout stderr stdprn stdaux intrare standard; ieire standard; ieire standard pentru erori; ieire pe imprimant; intrare / ieire serial.

4.1. FUNCIA STANDARD printf Funcia printf realizeaz ieiri cu format la ieirea standard stdout, deci afiare la terminalul la care care s-a lansat programul. Funcia printf se apeleaz printr-o instruciune cu formatul: int printf (control, lista_expresii); unde control este un ir de caractere care conine: texte de scris; specificatori de format pentru datele care se scriu din lista_expresii. lista_expresii conine expresii; valoarea fiecrei expresii se scrie conform unui specificator de format corespondent din parametrul control. Parametrul control este inclus ntre ghilimele, iar numrul specificatorilor de format coincide cu numrul expresiilor din lista_expresii. Dac dorim s scriem numai un text atunci parametrul de control nu conine nici un specificator de format iar lista_expresii este vid (practic este absent). Un specificator de format are formatul BNF urmtor: %[-][d..d][.d..d][l1]l2 Dup cum se observ un specificator de format ncepe ntotdeauna cu caracterul procent (%). Dup acest caracter poate urma una din construciile urmtoare: - un caracter - opional; prezena acestui caracter indic cadrarea la stnga a datei n cmpul n care se scrie (implicit data se scrie cadrat la dreapta); -un ir de cifre zecimale opional, care definete lungimea minim a cmpului n care se scrie data corespunztoare din lista_expresii; data se scrie astfel: n cazul n care necesit o lungime mai mic se scrie cadrat la dreapta sau stnga (n funcie de absena sau prezena semnului -) n cazul n care necesit o lungime mai mare se va scrie pe attea poziii cte i sunt necesare; -un punct urmat de un ir de cifre zecimale (dup cum se observ opional); acest element indic precizia datei care se scrie:

dac data se scrie n virgul flotant, precizia definete numrul de cifre aflate dup marca zecimal (deci cte zecimale); dac data este un ir de caractere atunci indic cte caractere se scriu. -una sau dou litere, care definesc tipul de conversie din formatul intern n formatul extern: prima litera poate fi l, ceea ce semnific conversia din formatul intern long n formatul extern definit de specificator; a doua liter este obligatorie ntotdeauna i are semnificaiile de mai jos: litera d o x pentru pentru X tipul de conversie realizat - din int intern n zecimal extern - din int intern n octal extern - din int intern n hexazecimal extern (litere mici cifrele mai mari ca 9 deci a,b,c,d,e,f,) - din int intern n hexazecimal extern (litere mici cifrele mai mari ca 9 deci A,B,C,D,E,F) - din unsigned intern n zecimal extern fr semn din binar intern (cod ASCII) n caracterul

u c corespunztor s - din ir de coduri ASCII ntr-un ir de caractere (atenie ultimul cod este NUL (adic \0) f - din float sau double intern n d...d.d...d (implicit 6 cifre zecimale la partea fracionar dac nu e prezent precizia) e - din float sau double intern n d.d...deddd (implicit 6 cifre zecimale la partea fracionar dac nu e prezent precizia) E - din float sau double intern n d.d...dEddd (implicit 6 cifre zecimale la partea fracionar dac nu e prezent precizia) g - se aplic una din conversiile definite de literele f i e alegndu-se aceea care are un numr minim de poziii G - se aplic una din conversiile definite de literele f i E alegndu-se aceea care are un numr minim de poziii

Literele d, o, x, u pot fi precedate de litera l conversia realizndu-se din formatul intern long n loc de int.

Observaie. 1o. Dac caracterul % nu este urmat de una din construciile de mai sus atunci nu reprezint un specificator de format. Funcia printf ntoarce lungimea total n octei a datelor scrise la terminal sau valoarea simbolic EOF n caz de eroare. Precizm c EOF este definit n fiierul header stdio.h astfel: #define EOF 1. Totodat funcia printf poate fi testat i astfel: EOF= = printf (control, lista_expresii) Dac are valoarea adevrat atunci produs o eroare. Exemple: 1) #include<stdio.h> #include<math.h> void main(void) { int i=10; long j=11; float a=1.2, b=1.3; double A=1.4; B=1.5; la scrierea datelor s-a

clrscr(); ecranul

// inceputul instructiunilor executabile; se sterge

printf ("\ni*j = %d",i*j); // incep afisarile printf ("\ni*j = %5d",i*j); printf ("\ni*j = %-5d",i*j); printf ("\ni*j = %5.5d",i*j); printf ("\ni*j = %05d",i*j); printf ("\na*b = %10.1f",a*b); printf ("\nA*B = %10.5lf",A*B); printf ("\nradical(a*b) = %lf",sqrt((double) a*b)); printf ("\nradical(A*B) = %15.10lf",sqrt(A*B)); printf ("\nradical(A*B) = %25.17lf",sqrt(A*B)); printf ("\nradical(A*B) = %25.19lf",sqrt(A*B)); getche(); // asteapta citirea unui caracter de la terminal } Rezultatele execu iei programului sunt: i*j = 110 i*j = 110 i*j = 110 i*j = 00110 i*j = 00110 a*b = 1.6 A*B = 2.10000 radical(a*b) = 1.249000 radical(A*B) = 1.4491376746 radical(A*B) = 1.44913767461894372 radical(A*B) = 1.4491376746189437200 2) #define sir PC WORLD void main (void) { printf(*%10s*,sir); printf(*%-10s*,sir); printf(*%10.5s*,sir); printf(*%-10.5s*,sir); } Rezultatele execuiei programului sunt:

* PC WORLD* *PC WORLD * * PC WO* *PC WO *


4.2. FUNCIA STANDARD scanf Funcia de bibliotec scanf realizeaz intrri cu format de la intrarea standard stdin (intrare de la terminalul de la care s-a lansat programul) i poate fi apelat printr-o instruciune de apel de forma: scanf (control, lista_adrese); Ca i n cazul funciei printf, parametrul control este delimitat de ghilimele i poate conine texte i specificatori de format. Caracterele albe din parametrul control sunt neglijate. Celelalte caractere care nu fac parte dintr-un specificator de format, trebuie s existe la intrare n poziii corespunztoare. Ele se folosesc n scopul realizrii unor verificri asupra datelor care se citesc. Un specificator de format ncepe i n acest caz prin caracterul procent, apoi: un caracter * opional; un ir de cifre, opional, care definete lungimea maxim a cmpului din care se citete data de sub controlul specificatorului de format; una sau dou litere care definesc tipul conversiei din formatul extern n formatul intern. Cmpul controlat de un specificator de format ncepe cu primul caracter care nu este alb i se termin:

fie la caracterul dup care urmeaz un caracter alb; fie la caracterul dup care urmeaz un caracter care nu corespunde specificatorului de format care controleaz acel cmp; fie la caracterul prin care se ajunge la lungimea maxim indicat n specificatorul de format. Condiia c) este absent n definirea cmpului dac n specificatorul de format nu este indicat lungimea maxim a cmpului. Literele care termin un specificator de format sunt asemntoare cu cele utilizate la funcia printf. n acest caz este realizat conversia invers fa de cea realizat de funcia printf. Litera Tipul conversiei realizate

d - data din cmpul de intrare este un ir de cifre zecimale, precedat eventual de un semn i se convertete din zecimal extern n binar de tip int o - ca i n cazul literei d, dar din octal extern n binar de tip int. x - ca i n cazul literei d, dar din hexazecimal extern n binar de tip int; se utilizeaz literele mici a-f sau mari A-F pentru cifrele mai mari ca 9. X - ca i n cazul literei x; u - data este un ir de cifre zecimale care formeaz un numr ntreg fr semn i se convertete din zecimal extern n binar tipul unsigned. c - data se consider format din caracterul curent de la intrare i parametrului corespunztor i se atribuie codul ASCII al acestui caracter; n acest caz nu se face avans peste caracterele albe, ca i n cazul celorlali specificatori. s - data se consider c este ir de caractere; irul ncepe, conform regulii generale, cu primul caracter care nu este alb i se termin la caracterul dup care urmeaz un caracter alb sau cnd s-au citit attea caractere cte indic dimensiunea maxim din specificatorul de format; f - data de intrare reprezint un numr flotant; deci conversie din flotant extern n virgul flotant simpl precizie; data care se citete conine un punct sau un exponent, sau att punct ct i exponent. Literele d, o, x i u pot fi precedate de litera l i n acest caz conversia se realizeaz spre long, n loc de int. De asemenea, litera f va fi precedat de litera l pentru a face conversii spre formatul virgul flotant dubl precizie. Lista_adrese conine adresele zonelor receptoare ale datelor citite prin intermediul funciei scanf. Fiecare dintre parametri reprezint adresa unei zone receptoare sub forma: &nume Ea determin adresa zonei de memorie rezervat variabilei nume. Caracterul & din construcia de mai sus reprezint un operator unar, numit operatorul adres. El are aceeai prioritate ca i ceilali operatori unari din limbajul C. Exemplu: void main (void) { int i; long n; float x; scanf ( %d %ld %f ,&i,&n,&x); // citeste datele din stdin si le atribuie lui i,n,x. scanf( %d %*ld %f , &i,&x); // caracterul * indica faptul ca valoarea pentru variabila n // nu se citeste }

Funcia scanf returneaz numrul de cmpuri citite corect din fiierul stdin. Dac apare o eroare la citire (din diverse motive de exemplu neconcordan dintre specificatorul de format i datele din fiierul stdin) atunci funcia nu va returna numrul total de cmpuri; citirea se va ntrerupe n locul detectrii erorii i scanf va returna numrul de cmpuri citite pn n acel moment. Deci de multe ori pentru a valida formal intrarea datelor (atenie nu e o validare calitativ) se va folosi o construcie de forma: nr=scanf(...) Prin construcia de mai sus se poate pune n eviden sfritul de fiier, deoarece n acest caz scanf returneaz valoarea EOF. Sfritul de fiier se poate genera de la tastatur acionnd n acelai timp tastele CTRL i Z (deci CTRL / Z). Deorece funcia scanf citete datele de la intrarea standard prin intermediul unei zone de memorie intermediare (numit zon tampon sau buffer), ea are acces la caracterele din zona tampon numai dup acionarea tastei ENTER (RETURN). De aceea dup acionarea combinaiei CTRL / Z se va tasta i ENTER. Totodat aceast intermediere face posibil eventuale corecturi nainte de a aciona tasta ENTER. Exemplu: Vom citi un ntreg de cinci cifre de la intrarea standard i vom afia cifrele respective precedate fiecare de dou spaii. void main (void) { int c1,c2,c3,c4,c5; // c1,c2,c3,c4,c5 sunt cifrele intregului citit scanf(%1d %1d %1d %1d %1d, &c1,&c2,&c3,&c4,&c5); // se citesc cifrele intregului in cele 5 variabile cu %1d printf(%3d%3d%3d%3d%3d,c1,c2,c3,c4,c5); } Pentru a citi iruri de caractere se va folosi funcia scanf fr a mai pune operatorul de adres n faa numelui irului de caractere, deoarece numele unui tablou reprezint un pointer (deci conine o adres i anume adresa primului element de tablou). Exemplu: Vom citi numele i prenumele unei persoane i le afim pe ecran. #define MAX 20 void main(void) { char nume[MAX+1], prenume[MAX+1]; //declararea tablourilor de caractere scanf (%20s %20s,nume, prenume); //nu s-a mai pus operatorul de adresa printf (\nnumele: %s, prenumele: %s,nume,prenume); } 4.3. FUNCIA STANDARD putchar Funcia standard putchar se poate utiliza pentru a scrie un caracter n fiierul standard de ieire stdout, n poziia curent a cursorului. Ea se apeleaz folosind o instruciune de apel de forma: putchar (expresie); unde expresie este codul ASCII al caracterului care se scrie la ieirea standard. Practic putchar nu este o funcie n sensul definiiei ce am dat-o n lecia 1, ci este un macrou definit n fiierul header stdio.h, care folosete o funcie special destinat prelucrrii fiierelor, funcia putc, astfel: #define putchar(c) putc(c,stdout) Exemplu:

void main (void) { putchar (A); // scrie caracterul A in fisierul de iesire in pozi ia curenta a cursorului putchar (A+2) // scrie caracterul de cod ASCII A+2=65+2=67, adica litera C putchar (\n); // scrie caracterul newline; deci inceput de linie nou } 4.4. FUNCIA STANDARD getchar Aceast funcie citete de la intrarea standard, stdin, caracterul curent i returneaz codul ASCII al caracterului citit. Tipul valorii returnate este int. La ntlnirea sfritului de fiier (CTRL/Z) se returneaz valoarea EOF (adic -1). Ca i putchar, getchar este un macrou definit n fiierul header stdio.h, cu ajutorul unei funcii speciale, getc destinate prelucrrii fiierelor, astfel: #define getchar() getc(stdin) De obicei getchar se folosete n expresii de atribuire de forma: c=getchar(); Dup citirea caracterului curent de la intrarea standard se atribuie variabilei c codul ASCII al caracterului cititi sau EOF la ntlnirea sfritului de fiier. Exemple: 1) #include <stdio.h> void main (void) { putchar(getchar() A + a);

// citeste o litera mare si o rescrie ca litera mica }

2) exemplul urmtor testeaz dac s-a citit o liter mare i numai n acest caz aplic transformarea ei n liter mic. n cazul n care la intrare nu se afl o liter mare se rescrie caracterul respectiv. #include <stdio.h> void main (void) { intc c; putchar(((c = getchar() )>= A && c<= Z) ? c-A+a :c); } 4.5. FUNCIILE STANDARD getche I getch Funcia getche citete de la intrarea standard caracterul curent i returneaz codul ASCII al caracterului citit. Aceast funcie are acces direct la caracter dup ce a fost tastat i are forma: getche(); Funcia getch este similar cu getche cu singura condiie c citirea se face fr ecou (deci caracterul tastat nu se reafieaz pe terminal). De fapt prezena/absena sufixului e precizeaz c funcia e sau nu cu ecou. Cele dou funcii fac citiri fr intermedierea zonei tampon. LECIA 5. INSTRUCIUNI 5.1. SCURT ISTORIC AL METODELOR DE PROGRAMARE Vom prezenta n continuare cteva metode de programare dar nu exhaustiv, nefiind aici cadrul adecvat (eventual ntr-un curs de Software Engineering). O clasificare cronologic a ceea ce vrem s prezentm ar fi urmtoarea: programarea artizanal; programarea procedural; programarea modular;

programarea structurat; programarea prin abstractizarea datelor; programarea orientat spre obiecte. 5.1.1. Programare artizanal Aceast metod de fapt nu este o metod propriu-zis ci este prima modalitate de programare odat cu apariia calculatoarelor. Intuiia i experiena programatorului joac un rol important. Fiecare programator i are propriile reguli de programare. Programele sunt monolitice (un singur corp de instruciuni), lungi i greu de neles de alt programator. nsui cel ce a elaborat un astfel de program ntmpin dificulti de nelegere a propriului program dup un timp oarecare. 5.1.2. Programare procedural Odat cu apariia primelor limbaje de nalt nivel se utilizeaz programarea procedural. Necesitatea ca anumite secvene de program s fie folosite de mai multe ori duce la organizarea acestora n uniti distincte de program numite n diverse limbaje subprograme, subrutine, proceduri, etc. De multe ori procedurile trebuie s fie generale deci procesarea s fac abstractizare de valorile datelor. De exemplu o procedur de calcul al radicalului de ordinul 2 trebuie s calculeze acest lucru din orice numr real pozitiv iar pentru cele negative s semnaleze eroare. Procedurile trebuie deci parametrizate cu anumite variabile numite parametri formali. Valorile de la apel ale parametrilor formali se numesc parametri efectivi. Programarea procedural are la baz deci utilizarea procedurilor, iar acestea realizeaz o abstractizare prin parametri. La apelare o procedur funcioneaz dup principiul cutiei negre (black box): se cunosc intrrile i ieirile rezultate din acestea dar nu i modul de transformare care nu e important n acest moment. n majoritatea limbajelor procedurale de programare se consider 2 categorii de proceduri: proceduri care definesc o valoare de revenire (denumite i funcii); proceduri care nu definesc o valoare de revenire. n limbajele C i C++ procedurile de ambele categorii se numesc funcii. 5.1.3. Programarea modular Pe msur ce complexitatea aplicaiilor a crescut, a aprut ideea de a descompune problemele n subprobleme mai simple care la rndul lor pot fi descompuse n altele mai simple i aa mai departe. n felul acesta se ajunge la o descompunere arborescent a problemei date n subprobleme mai simple. Programarea subproblemelor devine o problem mai simpl i fiecare subproblem are o anumit independen fa de celelalte subprobleme. De asemenea, interfaa ei cu celelalte subprobleme este limitat i bine precizat prin procesul de descompunere a problemei iniiale. De obicei, programarea unei subprobleme, component a descompunerii arborescente a problemei iniiale, conduce la realizarea unui numr relativ mic de proceduri (funcii). Aceste funcii pot prelucra n comun anumite date. Unele dintre ele sunt independente de funciile realizate pentru alte subprobleme componente ale descompunerii arborescente. Altele realizeaz chiar interfaa cu subproblemele nvecinate. Despre funciile obinute n urma programrii unei subprobleme se obinuiete s se spun c sunt nrudite. De obicei, aceste funcii, mpreun cu datele pe care le prelucreaz, se pstreaz ntr-un fiier i se compileaz independent. O colecie de funcii nrudite, mpreun cu datele pe care le prelucreaz n comun formeaz un modul. n felul acesta, problema iniial se realizeaz printr-un program alctuit din module.

module.

Programarea

modular

are

la

baz

elaborarea

programelor

pe

O parte din datele utilizate n comun de funciile modulului, sau chiar toate datele modulului, nu sunt necesare i n alte module. Aceste date pot fi protejate sau cum se mai spune, ascunse n modul. Limbajul C i C++, permite ascunderea datelor n modul folosind date care au clasa de memorie static. Mai mult, pot fi declarate i funciile ca statice i atunci ele vor fi ascunse n modul (nu pot fi apelate din afara modului). Ascunderea funciilor n modul se face pentru acele funcii care nu se utilizeaz la realizarea interfeei modulului cu celelalte module. Ascunderea datelor i funciilor n module permite protejarea datelor i prentmpin utilizarea eronat a funciilor. 5.1.4. Programarea structurat Descompunerea unei probleme n subprobleme mai simple se poate face succesiv n mai multe etape, pn cnd subproblemele sunt direct programabile sub forma unor proceduri sau module. Aceast descompunere succesiv se mai numete rafinare pas cu pas (stepwise refinement).. Evident c se obine o descompunere arborescent. Procedurile se pot organiza sau nu n module. n cadrul procedurilor se folosesc anumite structuri de control a execuiei. Aceasta impune o anumit disciplin a programrii. Structurile de control de sunt: secvena; iteraia (pretestat, posttestat, cu numr prestabilit de ciclri); alternativa (simpl, complet, generalizat). Instruciunea de baz (primitiv) n cadrul acestor structuri de control este instruciunea de atribuire. Aceast abordare a programrii s-a nscut din necesitatea eliminrii instruciunii de control GO TO care face saltul necondiionat la o instruciune precizat, alta dect instruciunea urmtoare ei. Profesorul Dijsktra de la Universitatea din Eindhoven spunea, prin anul 1965, calitatea unui programator este invers proporional cu numrul de instruciuni GO TO folosite i a impus D-structurile de control: secvena; iteraia pretestat; alternativa simpl. D-structurile se regsesc n toate limbajele procedurale. Corespondena ar fi: secvena este echivalent cu execuia instruciunilor n ordinea scrierii lor n programul surs; b) iteraia pretestat echivalent cu WHILE ... DO; c) alternativa simpl echivalent cu IF ... THEN. continuare. O ilustrare grafic a celor trei D-structuri se d n

da S1 C nu da S2 S C S nu . Sn

S-a demonstrat ulterior (Bohm i Jacopini) c orice algoritm se poate descrie doar cu D-structurile dar pentru o mai bun lizibilitate i nelegere a programelor surs s-au adugat i iteraia postestat (REPEAT ... UNTIL), iteraia cu numr prestabilit de ciclri (FOR ... DO), alternativa complet (IF ... THEN ... ELSE) i alternativa generalizat (CASE ... OF). n unele limbaje se folosesc i alte structuri pe lng cele de mai sus pentru o ct mai fidel reflectare a algoritmului. 5.1.5. Programarea prin abstractizarea datelor n toate aceste tehnologii anterioare se urmrete mai mult organizarea programului i mai puin rezolvarea ct mai natural a problemei. Programarea prin abstractizarea datelor i programarea orientat spre obiecte propun metodologii n care conceptele deduse din analiza problemei s poat fi reflectate ct mai fidel n program i s se poat manevra cu instanieri ale acestor concepte ct mai natural. Se realizeaz o mai mare fidelitate a programului fa de problem. De exemplu dac ntro problem n care se proceseaz numere complexe e nevoie s se lucreze ntr-o form ct mai apropiat cu forma matematic se poate introduce tipul COMPLEX (tip care nu exist n limbajele de programare) i apoi se pot declara variabile de acest tip. Mai mult ar trebui s se poat face toate operaiile matematice asupra datelor de tip COMPLEX. n general un TAD (Tip Abstract de Date) are dou componente fundamentale: datele membru (reflect reprezentarea tipului); funciile membru (reflect comportamentul tipului). 5.1.6. Programarea orientat spre obiecte Un neajuns al programrii prin abstractizarea datelor este faptul c nu permite exprimarea legturilor dintre diferite concepte (TADuri). Singura legtur dintre concepte care se poate exprima, este aceea c datele membru ale unei clase pot fi obiecte ale unei alte clase. Acest lucru nu este suficient n cazul n care conceptele sunt strns dependente ntre ele formnd structuri ierarhice. Exprimarea ierarhiilor conduce la atribute suplimentare cum sunt cele de motenire. Aceste atribute conduc la un nou model de programare pe care l numim programare orientat obiect. n vrful unei ierarhii se afl fenomenul sau forma de existen care are trsturi comune pentru toate celelalte componente ale ierarhiei respective. Pe nivelul urmtor al ierarhiei se afl componentele care pe lng trsturile comune de pe nivelul superior, mai au i trsturi suplimentare, specifice. O ierarhie, de obicei, are mai multe nivele, iar situarea unui element pe un nivel sau altul al ierarhiei este uneori o problem deosebit de complex. Dm n exemplul urmtor o ierarhie arborescent pentru conceptele de paralelogram, dreptunghi, romb i ptrat. Paralelogram

Dreptunghi Romb

Ptrat

Dac paralelogramul se afl n vrful ierarhiei atunci pe nivelul imediat inferior se aeaz dreptunghiul (paralelogramul cu un unghi drept) dar i rombul (paralelgramul cu 2 laturi alturate congruente). Apoi

ptratul se poate defini fie ca un dreptunghi cu laturile congruente fie ca un romb cu un unghi drept. Conceptul de pe fiecare nivel se observ c motenete proprietile conceptului imediat superior din care este derivat. La ora actual, toate ramurile cunoaterii tiinfice sunt pline de ierarhii rezultate n urma clasificrii cunotinelor acumulate n perioada lung de observare a fenomenelor i formelor de existen a lumii materiale i spirituale. Clasificrile ierarhice ale cunotinelor pot fi ntlnite att n domeniile care pleac de la cele mai concrete forme ale lumii materiale, cum sunt botanica, zoologia, biologia, etc ct i n domenii care studiaz concepte dintre cele mai abstracte, cum ar fi matematica sau filozofia. Aceste ierarhii sunt rezultatul definirii conceptelor dup regula includerii genul proxim i diferena specific. Limbajul C dispune de un set bogat de instruciuni care permit scrierea de: programe structurate, programe flexibile, programe compacte. Totodat limbajul C permite aplicarea metodelor de programare procedural, programare modular i programare structurat. Pe lng aceste metodologii limbajul C++ permite i programarea prin abstractizarea datelor i programarea orientat spre obiecte. Vom descrie n continuare instruciunile limbajului C. Ca o caracteristic sintactic toate instruciunile limbajului se termin prin caracterul ;, excepie fcnd instruciunile care se termin cu acolada nchis. Limbajul C dispune de un set bogat de instruciuni care permit scrierea de: programe structurate, programe flexibile, programe compacte. Totodat limbajul C permite aplicarea metodelor de programare procedural, programare modular i programare structurat. Pe lng aceste metodologii limbajul C++ permite i programarea prin abstractizarea datelor i programarea orientat spre obiecte. Vom descrie n continuare instruciunile limbajului C. Ca o caracteristic sintactic toate instruciunile limbajului se termin prin caracterul ;, excepie fcnd instruciunile care se termin cu acolada nchis. 5.2. INSTRUCIUNEA VID Instruciunea vid se reduce la caracterul ;. Ea nu are nici un efect. Adesea este nevoie de ea la construcii n care se cere prezena unei instruciuni, dar nu este necesar s se execute nimic n punctul respectiv. 5.3. INSTRUCIUNEA EXPRESIE Instruciunea expresie se obine scriind punct i virgul dup o expresie, deci: expresie; Exist cazuri particulare ale instruciunii expresie: expresia de atribuire, care de altfel este cel mai important caz particular al instruciunii expresie: expresie;

apelul unei funcii: nume_funcie (par1, par2, . . . parn); incrementrile i decrementrile pre i post fixate: variabil++; ++variabil; variabil- -; - - variabil; Exemplu: void main (void) { int i; float f; double d; i=10; // instructiune de atribuire i++; // i se mareste cu 1 f=i; // instructiune de atribuire (valoarea lui i se converteste in float) d=++f; // incrementare lui f si atribuirea valorii lui d putchar(a); // instructiune de apel } 5.4. INSTRUCIUNEA COMPUS Instruciunea compus este o succesiune de declaraii urmate de instruciuni, succesiune inclus ntre acolade: { declaraii instruciuni } Pot lipsi declaraiile sau instruciunle dar nu n acelai timp. Dac delaraiile sunt prezente, atunci ele definesc variabile care sunt valabile numai n instruciunea compus respectiv. Exemplu: ... { int i=100; // variabila i este definita in aceasta instructiune compusa i++; // i are valabilitate doar intre acolade; dupa acolada inchisa i isi printf (i=%d\n,i); // pierde valabilitatea } Observaii: 1o. Dup acolada inchis a unei instruciuni compuse nu se pune ;. 2o. Corpul unei funcii are aceeai structur ca i instruciunea compus, deci o funcie are formatul: antetul funciei instruciune compus 5.5. INSTRUCIUNEA if Instruciunea if permite s realizm o ramificare a execuiei n funcie de valoarea unei expresii. Ea are dou formate ce permit aplicarea structurii de alternativ simpl i compus. Formatul 1: if (expresie) instructiune; Efectul: se evalueaz expresia din paranteze; dac valoarea expresiei este diferit de zero (deci conform conveniei are valoarea adevrat), atunci se execut instructiune, altfel se trece la instruciunea urmtoare. Formatul 2: if (expresie) instructiune_1; else

instructiune_2;

Efectul: se evalueaz expresia din paranteze; dac valoarea expresiei este diferit de zero (deci conform conveniei are valoarea adevrat), atunci se execut instructiune_1, altfel se execut instructiune_2; apoi n ambele cazuri se trece la instruciunea urmtoare. Observaii: 1o. Se pot folosi instruciuni if imbricate, nivelul de imbricare fiind oarecare (deci nu exist o limitare a numrului de imbricri). 2o. Pentru mai multe imbricri se folosete regula asocierii if-lui cu else astfel: un else se pune n coresponden cu primul if care se afl naintea lui n textul surs i nu este inclus n instruciunea care l precede pe el i nici nu i corespunde deja un else. Exemple void main (void) { float x,y,a; x=-5; y=10; if (x<0) // ultimul else se asociaza cu primul if iar if (y<0) a=1; // penultimul else se asociaza cu cel de-al doilea if else a=2; else a=0; } void main (void) { float x,y,a; x=-5; y=10; if (x<0) // ultimul else se asociaza cu primul if deoarece cel de-al { if (y<0) a=1; } // de-al doilea if este inclus in instructiunea compusa care else a=0; // il precede pe if } void main (void) // citeste trei intregi si scrie minimul dintre ei {int i,j,k,min; scanf (\ndati i=%d, &i); scanf (\ndati j=%d, &j); scanf (\ndati k=%d, &k); if (i>j) min=j; else min=i; if (k<min) min=k; printf (min(%d,%d,%d)= %d\n,i,j,k,,min); } 5.6. INSTRUCIUNEA while Instruciunea while are urmtorul format: while (expresie) instructiune; Cu ajutorul instruciunii while se realizeaz structura repetitiv pretestat (condiionat anterior). Efectul: se evalueaz valoarea expresiei din paranteze; dac expresia are valoarea diferit de zero, atunci se execut instructiune i se reia punctul 1), altfel se trece la instruciunea urmtoare instruciunii while. Deci instructiune se execut repetat atta timp ct expresia din parantez este diferit de zero. Se observ c dac expresia are valoarea zero de la nceput, atunci instructiune nu se execut niciodat.

Antetul ciclului while este construcia while (expresie) iar instructiune formeaz corpul ciclului. n cazul n care este necesar s se execute repetat mai multe instruciuni, se utilizeaz o instruciune compus format din instruciunile respective. Exemplu: Vom crea un program care citete un ntreg n i scrie n!. Algoritmul n pseudocod este urmtorul: Citeste n f=1 i=2 CtTimp i<=n execut f=f*i; i=i+1 SfritCtTimp Scrie n,f Programul n C este: #include<stdio.h> void main (void) { int n,i; double f; f=1.0; i=2; printf(\n dati n= ); scanf(%d,&n); while (i<=n) { f=f*i; i++; } printf(\nn=%d, iar n!=%g\n,n,f); } Corpul ciclului while se poate scrie mai compact astfel: while (i<=n) f*=i++; 5.7. INSTRUCIUNEA for Instruciunea for, ca i instruciunea while, se utilizeaz pentru a realiza o structur repetitiv pretestat. Formatul instruciunii este: for(exp1; exp2; exp3) instructiune; Antetul ciclului este definit de for(exp1; exp2; exp3) iar instructiune formeaz corpul ciclului. Prima expresie exp1 constituie partea de iniializare a ciclului, iar exp3 este partea de reiniializare a ciclului. Condiia de continuare a ciclului este exp2. De obicei exp1 i exp3 reprezint atribuiri. Efectul: se execut secvena de iniializare definit de expresia exp1; se evalueaz exp2; dac exp2 are valoarea zero, atunci se iese din ciclu, adic se trece la instruciunea urmtoare instruciunii for, altfel se execut instruciunea din corpul ciclului; se execut apoi secvena de reiniializare definit de exp3, apoi se reia secvena de la punctul 2). Observaii: 1o. Ca i n cazul instruciunii while, instruciunea din corpul ciclului for poate s nu se execute niciodat dac exp2 are valoarea zero chiar la prima evaluare. 2o. Expresiile din antetul instruciunii for pot fi i vide; totui caracterele ; vor fi ntotdeauna prezente.

3o. Comparnd instruciunile for i while observm c instruciunea for cu formatul anterior se poate realiza cu secvena urmtoare folosind while: exp1; while (exp2) { instructiune; exp3; } Invers, o instruciune while de forma: while (exp) instructiune este echivalent cu urmtoarea instruciune for: for(; exp; ) instructiune. Autorii limbajului C propun ca instruciunea for s se foloseasc cu prioritate pentru ciclurile care au pas. Exemple: Vom da o secven de instruciuni care nsumeaz elementele unui tablou: s=0; for(i=0; i<n; i++) s=s+tab[i]; sau scris mai compact: for (s=0, i=0; i<n; i++) s+=tab[i]; n continuare vom da un mic program caracterelor citite de la intrarea standard stdin. #include <stdio.h> void main(void) { long n; for (n=0; getchar()!=EOF; n++); printf (\nnumarul caracterelor citite =%ld\n,n); } sau scris cu instruciunea while #include <stdio.h> void main(void) { long n=0; while (getchar()!=EOF) n++; printf (\nnumarul caracterelor citite =%ld\n,n); } 5.8. INSTRUCIUNEA do-while Aceast instruciune realizeaz structura repetitiv condiionat posterior (posttestat) dar modificat fa de REPEAT .. UNTIL. Aceast instruciune s-a introdus pentru o mai mare flexibilitate n scrierea programelor. Formatul ei este: do instructiune; while (exp); Efectul: se execut instruciunea instructiune; se evalueaz expresia exp din paranteze; dac valoarea expresiei este zero se trece la instruciunea urmtoare instruciunii do-while; altfel se revine i se execut din nou instructiune. Observaii: 1o. Structura realizat de instruciunea do-while poate fi realizat printr-o secven n care se folosete instruciunea while astfel: instructiune; while (exp) instructiune; 2o. Se observ c n cazul instruciunii do-while, corpul ciclului se execut cel puin odat, spre deosebire de ciclurile while i for unde corpul ciclului poate s nu se execute niciodat.

care

afieaz

numrul

Exemplu: Vom da un program care calculeaz rdcina ptrat dintr-un numr real a>=0. #include<stdio.h> #define EPS 1e-10 void main (void) { double x1,x2,y,a; clrscr(); // sterge ecranul printf(\ndati un numar real pozitiv a=); if (scanf(%lf,&a) !=1 || a<0) printf (numarul citit nu este pozitiv\n); else { x2 = 1.0; do { x1 = x2; x2 = 0.5 *(x1+a/x1); // formula de iteratie if ((y=x2-x1) < 0) y = -y; } while (y >= EPS); printf (radacina patrata din:%g este: %.2lf\n,a,x2); // 2 zecimale } //sfirsit else } 5.9. INSTRUCTIUNEA switch Instruciunea switch permite realizarea structurii alternativa generalizat. Ea este echivalent cu o imbricare de structuri de alternativ simple. Utilizarea instruciunii switch face n schimb programul mult mai clar. Formatul instruciunii switch este urmtorul: switch (exp) { case c1: sir1 break; case c2: sir2 . . . case cn: default: } c1,. . . cn sunt constante sau constante simbolice; sir1, . . . ,sirn, sir sunt iruri de instruciuni. Efectul: se evalueaz expresia din parantez; se compar pe rnd valoarea expresiei cu valorile constantelor c1, . . . , cn; dac valoarea expresiei coincide cu valoarea lui ck, se execut secvena de instruciuni definit prin sirk; n cazul n care valoarea expresiei nu coincide cu nici una din constantele c1, . . . , cn, se execut secvena de instruciuni definit prin sir; dup execuia secvenei sirk sau sir se trece la instruciunea urmtoare instruciunii switch, adic la prima instruciune aflat dup acolada nchis care termin instruciunea switch respectiv; evident, acest lucru are loc dac irul care se execut nu impune, el insui, un alt mod de continuare a execuiei, de exemplu o revenire din funcia respectiv, un salt la o anumit instruciune, etc. Observaii: 1o. Ramura default nu este obligatorie. n lipsa ei, dac valoarea expresiei nu coincide cu nici una din constantele c1,. . . , cn, instruciunea switch respectiv nu are nici un efect. 2o.Construcia break reprezint o instruciune. Ea termin fiecare ramur de instruciuni sir1, . . . , sirn, provocnd saltul la instruciunea unde: break; sirn break; sir

urmtoare instruciunii switch sau, cum se mai spune, realizeaz ieirea din instruciunea switch. 3o. Instruciunea break nu este obligatorie. n cazul n care este absent, se execut secvenial urmtoarea ramur. De exemplu dac avem secvena: switch (exp) { case c1: sir1 case c2: sir2 } ea se execut n felul urmtor: dac valoarea expresiei este egal cu c1 se execut sir1 i apoi sir2; dac valoarea expresiei este egal cu c2 se execut sir2; daca valoarea expresiei difera de valorile c1 i c2 instruciunea switch de mai sus nu este efectiv, se trece la instruciunea urmtoare care urmeaz dup switch. secvena de mai sus se putea realiza i astfel: if (exp = = c1) { sir1 sir2 }else if (exp = = c2) sir2 Exemplu: Vom citi din fiierul de intrare construcii de forma: op1 operator op2, unde op1 i op2 sunt numere ntregi (operanzi ntregi) iar operator este un operator aritmetic {+, -, *, /}. La ieire se va scrie valoarea expresiei citite. De exemplu dac se citete secvena 100/3 se va afia rezultatul 33. Programul permite citirea i evaluarea mai multor astfel de expresii, pn la ntlnirea sfritului de fiier. #include <stdio.h> void main (void) { int op1,op2,operator,rezultat,i; while (( i=scanf(%d %c %d, &op1,&operator, &op2)) != EOF) if (i = = 3 ) // ramura adevarat inseamna ca s-au citit 3 campuri corecte { switch (operator) { case +: rezultat = op1 + op2 ; // avem adunare break; case - : rezultat = op1 op2; // avem scadere break; case * : rezultat = op1 * op2; // avem inmultire break; case / : // avem impartire intreaga if (op2 = = 0) { printf (divizor nul\n); rezultat = 0; } else rezultat = op1 / op2; break; default : printf (operator eronat\n); rezultat = 0; } // sfarsit switch printf (%d %c %d %d\n, op1, operator, op2, rezultat); } else printf (expresie eronat \n); // sfarsit if si while } 5.10. INSTRUCIUNEA break break; Formatul instruciunii este urmtorul:

De obicei instruciunea break se folosete pentru a iei dintr-un ciclu. Dac exist mai multe cicluri imbricate instruciunea break va trece controlul la ciclul de nivel imediat superior (deci imbricarea rmne, nu

se iese din toate ciclurile). O alt utilizare este n instruciunea switch, dup cum am observat n paragraful anterior. Un alt exemplu de utilizare frecvent este ieirea dintr-un ciclu infinit de forma: for ( ; ; ) {. . . if (exp) break; . . . } 5.11. INSTRUCIUNEA continue Formatul instruciunii este urmtorul: continue; Efectul: n ciclurile while i do-while ea realizeaz saltul la evaluarea expresiei care decide asupra continurii ciclului; n ciclul for ea realizeaz saltul la pasul de reiniializare. Observaie: 1o. Instruciunea continue se utilizeaz numai n corpul unui ciclu, permind, dup caz, s se treac la pasul urmtor al ciclului sau s se ias din ciclu. 5.12. INSTRUCIUNEA goto Conform principiilor programrii structurate instruciunea goto nu ar fi necesar. Dar ea a fost introdus n limbaj, deoarece, n anumite cazuri, se dovedete a fi util, asigurnd o flexibilitate mai mare n programare. De multe ori ieirea dintr-un ciclu imbricat n alte cicluri se realizeaz mai simplu cu ajutorul instruciunii goto. n lipsa ei ar trebui s folosim mai muli indicatori i teste asupra valorilor acestora pentru ieirea din ciclu. Saltul fcut de goto se face la o instruciune care este prefixat de o etichet. Prin etichet vom nelege un nume urmat de caracterul :. Etichetele sunt locale unei funcii. Instruciunea goto are urmtorul format: goto eticheta; Efectul: se realizeaz saltul la instruciunea prefixat de eticheta al crei nume se afl scris dup cuvntul cheie goto. 5.13. APELUL I REVENIREA DINTR-O FUNCIE 5.13.1. Apelul unei funcii n limbajul C funciile sunt de dou tipuri: funcii care returneaz o valoare la revenirea din ele; funcii care nu returneaz nici o valoare la revenirea din ele. O funcie care nu returneaz nici o valoare la revenirea din ea se apeleaz printr-o instruciune de apel. Ea are urmtorul format: nume (lista_parametrilor_efectivi); (*) unde: nume este numele funciei; lista_parametrilor_efectivi este fie vid, fie se compune din una sau mai multe expresii separate prin virgul. Instruciunea de apel este un caz particular al instruciunii expresie. Parametrii efectivi (de la apel) trebuie s corespund cu cei formali (de la definirea funciei) prin ordine, tip i numr.

n cazul n care o funcie returneaz o valoare, ea poate fi apelat fie printr-o instruciune de apel, fie sub forma unui operand al unei expresii. Observaii: 1o. Dac nu dorim s utilizm valoarea returnat de funcia respectiv, apelul se face printr-o instruciune de apel. 2o. Dac dorim s utilizm valoarea returnat de funcie, vom folosi apelul funciei drept operand ntr-o expresie, operandul avnd formatul (*). Exemple de apeluri de funcii folosite pn acum sunt apelurile funciilor standard printf, scanf, getchar i putchar. Funciile printf i putchar au fost apelate prin instruciuni de apel, valorile returnate de ele nefiind utilizate. n schimb funciile scanf i getchar au fost apelate n ambele moduri, att prin instruciuni de apel, ct i ca operanzi n diferite expresii. 5.13.2. Prototipul unei funcii O funcie poate fi apelat dac ea este definit n fiierul surs nainte de a fi apelat. Dup cum am prezentat n lecia 1 nu ntotdeauna este posibil acest lucru i n astfel de cazuri apelul funciei trebuie s fie precedat de prototipul ei. Prototipul unei funcii are ca scop s informeze compilatorul despre: tipul valorii returnate de funcie; tipurile parametrilor. n felul acesta, la apelul unei funcii, compilatorul poate face teste cu privire la tipul expresiilor care reprezint parametrii efectivi, precum i unele conversii necesare asupra valorii returnate de funcie. Observaii: 1o. Tipurile parametrilor pot s lipseasc. n acest caz, compilatorul nu controleaz tipurile parametrilor efectivi, singura informaie coninut de prototip fiind tipul valorii returnate de funcia respectiv. 2o. Absena att a prototipului unei funcii, ct i a definiiei funciei nainte de a fi apelat este posibil; n acest caz se presupune c funcia returneaz o valoare de tip int. 3o. n practic se recomand utilizarea prototipurilor pentru toate funciile nainte de a fi apelate. n acest scop, ele vor fi scrise la nceputul fiierelor surs. Formatele posibile ale unui prototip sunt: formatul formatul formatul formatul 1: 2: 3: 4: tip tip tip tip nume nume nume nume (lista_declaratiilor_de_parametri); (lista_ tipurilor_parametrilor); (void); ();

Formatul 2 este cel mai utilizat. Formatul 3 se poate folosi pentru orice funcie care nu are parametri. Formatul 4 se poate folosi pentru orice funcie la al crei apel nu se doresc teste referitoare la tipul parametrilor efectivi. Funciile din biblioteca standard a limbajului C au prototipurile definite n fiierele de tip .h. 5.13.3. Apel prin valoare i apel prin referin La apelul unei funcii, fiecrui parametru formal i se atribuie valoarea parametrului efectiv care-i corespunde. Deci, la apelul unei funcii se transfer valorile parametrilor efectivi. Din aceast cauz se spune c apelul este prin valoare (call by value). n anumite limbaje de programare, la apel nu se transfer valorile parametrilor efectivi ci

adresele acestora. n acest caz se spune c apelul este prin referin (call by refference). ntre cele dou tipuri de apeluri exist o diferen esenial i anume: n cazul apelului prin valoare funcia apelat nu poate modifica parametrii efectivi din funcia apelant, neavnd acces la ei. n cazul apelului prin referin, funcia apelat, dispunnd de adresele parametrilor efectivi, i poate modifica. n limbajul C apelul se realizeaz implicit prin valoare. n cazul c un parametru este numele unui tablou atunci transferul se realizeaz prin referin deoarece numele unui tablou este un pointer i conine adresa primului element al tabloului. Transferul prin referin se realizeaz cu ajutorul variabilelor de tip pointer i cu ajutorul operatorului de adres (&).

5.13.4. Revenirea dintr-o funcie Revenirea dintr-o funcie se poate face n dou moduri: la ntlnirea instruciunii return; dup execuia ultimei sale instruciuni, adic a instruciunii care precede acolada nchis ce termin corpul funciei respective. Instruciunea return are dou formate: return; sau return expresie; Primul format se utilizeaz cnd funcia nu returneaz o valoare, iar cel de-al doilea cnd funcia returneaz o valoare. n acest ultim caz, funcia returneaz valoarea expresiei specificate. Observaie: 1o. Cnd revenirea se face dup execuia ultimei instruciuni a funciei nu se returneaz o valoare; revenirea n acest caz, se face ca i cum acolada nchis de la sfritul corpului funciei ar fi precedat de instruciunea return. Exemplu: vom da un exemplu de apel rdacina ptratic dintr-un numr nenegativ. #include<stdio.h> double radacina_2 (double) // prototipul functiei al funciei care determin

void main (void) // functia principala care citeste d // si afiseaza radacina patrata din d { double d; clrscr(); // sterge ecranul if (scanf (%lf,&d) != || d<0) printf (numarul dat este eronat\n); else printf (d=%f, radacina patrata = %.10g\n, d, radacina_2(d)); #define EPS 1e-10 double radacina_2 (double x) { double x1,x2,y; x2 = 1.0; do { x1 = x2; x2 = 0.5 *(x1+x/x1); // formula de iteratie if ((y=x2-x1) < 0) y = -y; } while (y >= EPS); return x2; } Observaie:

1o. Limbajul C dispune de o bibliotec matematic n care sunt incluse o serie de funcii pentru calculul valorilor funciilor elementare. Exist o funcie numit sqrt (cu prototipul double sqrt (double);). Fiierul care conine biblioteca matematic se numete math.h i trebuie inclus n fiierul surs de lucru dac se dorete utilizarea funciilor definite n el. LECIA 6. POINTERI Un pointer este o variabil care are ca valori adrese. Pointerii se folosesc pentru a face referire la date cunoscute prin adresele lor. Astfel, dac p este o variabil de tip pointer care are ca valoare adresa zonei de memorie alocat pentru variabila ntreag x atunci construcia *p reprezint chiar valoarea variabilei x. n construcia de mai sus, *p, caracterul * se consider ca fiind un operator unar care furnizeaz valoarea din zona de memorie a crei adres este coninut n p. Operatorul unar * are aceeai prioritate ca i ceilali operatori unari din limbajul C. Dac p conine adresa zonei de memorie alocat variabilei x, vom spune c p pointeaz spre x sau c p conine adresa lui x. Pentru a atribui unui pointer adresa unei variabile, putem folosi operatorul unar &. Astfel, dac dorim ca p s pointeze spre x, putem utiliza construcia: p = &x; n limba romn se utilizeaz i alte denumiri pentru noiunea de pointer: referin, localizator; reper; indicator de adres. 6.1. DECLARAIA DE POINTER Un pointer se declar ca orice variabil cu deosebirea c numele pointerului este precedat de caracterul *. Astfel, dac, de exemplu, dorim s declarm variabila p utilizat anterior pentru a pstra adresa variabilei ntregi x, vom folosi declaraia urmtoare: int *p; Tipul int stabilete n acest caz faptul c p conine adrese de zone de memorie alocate datelor de tip int. Declaraia lui p se poate interpreta n felul urmtor: *p reprezint coninutul zonei de memorie spre care pointeaz p, iar acest coninut are tipul int. n general, un pointer se declar prin: tip *nume; ceea ce nseamn c nume este un pointer care pointeaz spre o zon de memorie ce conine o dat de tipul tip. Comparnd declaraia de pointer anterioar cu una obinuit: tip nume; putem considera c: tip * dintr-o declaraie de pointer obinuit. De aceea, construcia tip * reprezint tip dintr-o declaraie

se spune c reprezint un tip nou, tipul pointer. Exist cazuri n care dorim ca un pointer s fie utilizat cu mai multe tipuri de date. n acest caz, la declararea lui nu dorim s specificm un tip anume. Aceasta se realizeaz folosind cuvntul cheie void: void *nume; Exemple:

1) void main (void) { int x,y; int *p; y=x+10; // aceast atribuire este echivalenta cu secventa urmatoare p=&x; y=*p+100; x=y; p=&x; (*p)++; } // este echivalenta cu secventa

2) funcia permutare de mai jos realizeaz transferul parametrilor prin adres: void permutare (int *x, int *y) // x si y sunt pointeri { int temp; temp = *x; // temp ia valoarea ce se afla la adresa continuta in x *x=*y; // in zona a carei adresa se afla in x se transfera continutul // zonei a carei adresa se afla in y *y=temp; // in zona a carei adresa se afla in y se transfera valoarea // lui temp } Apelul funciei permutare se face astfel: permutare (&a, &b); pentru a schimba valorile lui a cu b.

6.2. LEGTURA DINTRE POINTERI I TABLOURI Numele unui tablou este un pointer deoarece el are ca valoare adresa primului su element. Totui exist o diferen ntre numele unui tablou i o variabil de tip pointer, i anume unui nume de tablou nu i se poate atribui alt adres. Deci numele unui tablou trebuie considerat ca fiind un pointer constant. Dac x este un parametru formal ce corespunde unui parametru efectiv care este un nume de tablou, x poate fi declarat fie ca tablou fie ca pointer spre tipul tabloului. Exemplu: Fie funcia cu antetul urmtor: unsigned lungime (char x[ ]); S presupunem c aceast funcie determin lungimea unui ir de caractere i se poate apela prin: l=lungime(tablou); unde tablou este de tip caracter. Antetul funciei lungime poate fi schimbat n felul urmtor: unsigned lungime (char *x); Cele dou declaraii sunt identice deoarece declaraia: char x[ ]; definete pe x ca numele unui tablou de tip caracter; dar atunci el este un pointer spre caractere deci se poate declara prin: char *x; 6.3. OPERAII CU POINTERI

Asupra pointerilor se pot face diferite operaii. Deoarece ei conin adrese atunci operaiile se realizeaz cu adrese. 6.3.1. Incrementare i decrementare Operatorii de incrementare i decrementare se pot aplica variabilelor de tip pointer. Efectul: operatorul de incrementare aplicat asupra unui operand de tip pointer spre tipul tip mrete adresa coninut de operand cu numrul de octei necesari pentru a pstra o dat de tipul tip. operatorul de decrementare se execut n mod analog, cu singura diferen c n loc s se mreasc adresa, ea se micoreaz cu numrul corespunztor de octei. De obicei decrementrile i incrementrile adreselor sunt mai rapide ca execuie cnd se au n vedere prelucrri de tablouri. Exemplu: int tab[10]; int *p; int i=0; p=&tab[i]; p++; // p contine adresa lui tab[1] // cu p se pot face referiri la orice element de tablou 6.3.2. Adunarea i scderea unui ntreg dintr-un pointer Dac p este un pointer, sunt corecte expresiile de forma: p+n i p-n unde n este de tip ntreg. Efectul: expresia p+n mrete valoarea lui p cu n*nr_tip, unde nr_tip este numrul de octei necesari pentru a memora o dat de tipul tip spre care pointeaz p; analog expresia p-n micoreaz valoarea lui p cu n*nr_tip. Dac x este un tablou de tipul tip, atunci x este pointer, deci o expresie de forma: x+n; este corect i deoarece x este un pointer spre primul su element x[0], x+n va fi un pointer spre elementul x[n]. Rezult c valoarea elementului x[n] se poate reprezenta prin expresia: *(x+n); Astfel variabilele cu indici se pot nlocui prin expresii cu pointeri. Aceasta permite ca la tratarea tablourilor s se foloseasc expresii cu pointeri n locul variabilelor cu indici. Versiunile cu pointeri sunt de obicei optime n raport cu cele realizate prin intermediul indicilor. 6.3.3. Compararea a doi pointeri Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi comparai folosind operatorii de relaie i de egalitate. Astfel, dac p i q sunt doi pointeri care pointeaz spre elementele tab[i], respectiv tab[j] ale tabloului tab, expresiile urmtoare au sens: p<q p!=j p= =q. Observaii: 1o. Pointerii nu pot fi comparai dect n condiiile amintite mai sus (deci dac sunt pointeri spre elementele aceluiai tablou).

2o. Operatorii = = i != permit compararea unui pointer i cu o constant simbolic special avnd numele NULL. Aceste comparaii permit s stabilim dac un pointer conine o adres sau nu. Astfel, dac expresia: p= = NULL este adevrat, p nu conine o adres. Dac expresia respectiv are valoarea fals atunci p conine o adres. Constanta simbolic NULL este definit n fiierul stdio.h . 6.3.4. Diferena a doi pointeri Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi sczui. Rezultatul diferenei a doi pointeri este definit astfel: fie t un tablou de un tip oarecare i p i q doi pointeri, p conine adresa elementului t[i] iar q conine adresa elementului t[i+n]. Atunci diferena q-p are valoarea n. 6.3.5. Exemple Vom da cteva funcii asupra irurilor de caractere: funcia lungime unsigned lungime (char*x) { int i; for (i=0; *x != \0; i++) x++; return i; } funcia copiaz void copiaza(char *x, char *y) { while(*x++ = = *y++); } funcia concateneaza void concateneaza (char *x, char *y) // concateneaza sirul de adresa y la sfarsitul sirului // de adresa x { while (*x) x++; // avans de adresa pana la sfarsitul sirului x while (*x++= *y++); } funcia compara int compara (char *x, char *y) { while (*x= = *y) { if (*x= = \0) return 0; x++; y++; return *x - *y; // daca diferenta caracterelor este // negativa atunci x<y altfel x>y } } 6.4. ALOCAREA DINAMIC A MEMORIEI Biblioteca standard a limbajului C pune la dispoziia utilizatorului funcii care permit alocarea de zone de memorie n timpul execuiei programului. O astfel de zon de memorie poate fi utilizat pentru a pstra date temporare. Zona respectiv poate fi eliberat n momentul n care nu mai sunt necesare datele care au fost pstrate n ea. Alocarea de zone de memorie i eliberarea lor n timpul execuiei programelor permite gestionarea optim a memoriei de ctre programator. Un // copiaza din zona de adresa y // in zona de adresa x

// sau for (i=0; *x++; i++);

astfel de mijloc de gestionare a memoriei l vom numi alocare dinamic a memoriei. Vom indica dou funcii din bibloteca limbajului C utilizate frecvent n alocarea dinamic a memoriei. Prototipurile lor se afl n fiierele standard alloc.h i stdlib.h, deci pentru a le utiliza vom include unul din aceste fiiere. Funcia malloc permite alocarea unui bloc de memorie a crui dimensiune se specific n octei. Funcia returneaz un pointer spre nceputul zonei alocate. ntruct acest pointer trebuie s permit memorarea oricrui tip de dat n zona alocat, el este de tip void *. Prototipul funciei este: void *malloc (unsigned n); unde n este numrul de octei al zonei de memorie care se aloc. n cazul n care n este prea mare, funcia returneaz pointerul NULL. Funcia free elibereaz o zon de memorie alocat prin malloc. Prototipul ei este: void free (void *p); unde p este pointerul returnat de malloc pointerul spre nceputul zonei care se elibereaz. Exemplu: Funcia memchar memoreaz un ir de caractere ntr-o zon de memorie alocat prin funcia malloc. Ea returneaz adresa de nceput a zonei n care s-a salvat irul de caractere, deci returneaz un pointer spre tipul char. #include <stdio.h> #include <alloc.h> #include <string.h> char *memchar (char *s) { char *p; if ((p=(char *)malloc(strlen(s)+1) ) != NULL { strcpy (p,s); return p; } else return NULL; } Observaii: 1o. n fiierul stdio.h exist definiia constantei NULL. 2o. Fiierul alloc.h s-a inclus deoarece conine prototipul funciei malloc. 3o. Fiierul string.h conine prototipurile funciilor strlen i strcpy. 4o. Funcia malloc se apeleaz pentru a rezerva strlen(s)+1 octei; strlen returneaz numrul de octei cuplai de caracterele proprii ale lui s (fr caracterul NULL). Cum n zona de memorie rezervat prin malloc se pstreaz i caracterul NULL, lungimea returnat de funcia strlen s-a mrit cu 1. 5o. Pointerul returnat de malloc a fost convertit spre char *, deoarece el este de tip void *. Acest pointer se atribuie lui p, deci p pointeaz spre nceputul zonei de memorie alocate prin apelul funciei malloc. Se testeaz dac acest pointer este diferit de NULL (deci dac s-a putut aloca memoria de dimensiunea cerut). n caz afirmativ, se transfer irul prin apelul funciei strcpy, returnndu-se apoi valoarea pointerului p. la alocare, deci este

6.5. POINTERI SPRE FUNCII

Numele unei funcii este un pointer spre funcia respectiv. El poate fi folosit ca parametru efectiv la apeluri de funcii. n felul acesta, o funcie poate transfera funciei apelate un pointer spre o funcie. Aceasta, la rndul ei, poate apela funcia care i-a fost transferat n acest fel. Exemplu: Un exemplu matematic n care este nevoie de un astfel de transfer este cel cu privire la calculul aproximativ al integralelor definite. S presupunem c dorim s calculm integrala definit din funcia f(x), ntre limitele a i b, folosind formula trapezului: I= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h) unde h=(b-a)/h. n continuare construim o funcie care calculeaz partea dreapt a acestei relaii. Numim aria_trapez aceast funcie. Observaii: 1o. Deoarece funcia f(x) din relaia de mai sus nu este definit n acest moment, ea trebuie s figureze printre parametrii funciei aria_trapez, mpreun cu limitele de integrare i valoarea lui n. 2o. Funcia aria_trapez returneaz valoarea aproximativ a integralei i ea se va apela printr-o expresie de atribuire, de exemplu: aria=aria_trapez (a, b, n, f); 3o. Funcia aria_trapez returneaz o valoare flotant n dubl precizie. De asemenea, i funcia f(x) returneaz o valoare flotant n dubl precizie. De aici rezult c prototipul funciei aria_trapez este urmtorul: double aria_trapez (double a, double b, int n, double (*f)()); sau double aria_trapez (double, double, int , double (*)()); 4o. Este necesar ca naintea apelului funciei aria_trapez funcia f(x) s fie definit sau s fie prezent prototipul ei , de exemplu: double f(); 5o. Construcia double (*f) () se interpreteaz n felul urmtor: - *f nseamn c f este un pointer; - (*f)() nseamn c f este un pointer spre o funcie; - double (*f) () nseamn c f este un pointer spre o funcie care returneaz o valoare flotant n dubl precizie. 6o. Trebuie s se includ *f ntre paranteze, deoarece construcia double *f(); este corect, dar nseamn altceva, parantezele rotunde fiind prioritare operatorului unar *. n acest caz, se declar f ca o funcie ce returneaz un pointer spre o valoare flotant n dubl precizie. 7o. Ultimul parametru formal al funciei aria_trapez corespunde parametrului efectiv f i deci el trebuie declarat ca i pointer spre o funcie ce returneaz o valoare flotant n dubl precizie. Conform observaiei 5), dac p este numele parametrului formal ce corespunde parametrului efectiv f, atunci p se declar astfel: double (*p)(); 8o. n corpul funciei aria_trapez va trebui s apelm funcia f(x) pentru a calcula valorile: f(a), f(b), f(a+h), . . . , f(a+(n-1)h). n momentul programrii funciei aria_trapez, nu se cunoate numele funciei concrete, ci numai pointerul p spre ea. De aceea, vom nlocui numele funciei prin *p, deci vom folosi apelurile: (*p)(a), (*p)(b), (*p)(a+h), . . . ,(*p)(a+(n-1)h) double aria_trapez(double x, double y, int m, double(*p)()); { double h,s; int i; h=(y-x)/m; for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h); s+=((*p)(x) + (*p)(y))/2;

s=h*s; return s; } Vom utiliza funcia aria_trapez pentru a calcula integrala definit din funcia sin(x2) pe intervalul [0,1], cu o eroare mai mic dect 10 -8. Vom nota cu In urmtoare sum: In= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h) Paii algoritmului sunt urmtorii: Pasul 1. Se alege o valoare iniial pentru n, de exemplu 10. Pasul 2. Se calculeaz In. Pasul 3. Se calculeaz I2n prin dublarea lui n. Pasul 4. Dac |In-I2n| < 10-8, algoritmul se ntrerupe i valoarea integralei, cu precizia admis, este I2n; altfel se dubleaz n, se pune In=I2n; n, i se trece la pasul 3. #define A 0.0 #define B 1.0 #define N 10 #define EPS 1e-8 #include <stdio.h> #include <math.h> double sinxp(double); // prototipul functiei sin(x*x) double aria_trapez(double, double, int, double (*)()); void main (void) // functia principala { int n=N; double in, i2n, vabs; in=aria_trapez (A, B, n, sinxp); do { n=n*2; i2n=aria_trapez(A, B, n, sinxp); if ((vabs= in-i2n) < 0) vabs = -vabs; in=i2n; } while (vabs >= EPS); printf (valoarea integralei este : %g.10\n,i2n); } double aria_trapez(double x, double y, int m, double(*p)()); { double h,s; int i; h=(y-x)/m; for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h); s+=((*p)(x) + (*p)(y))/2; s=h*s; return s; } double sinxp (double x) { return sin (x*x); } 6.6. TRATAREA PARAMETRILOR DIN LINIA DE COMAND n linia de comand folosit la apelul execuiei unui program se pot utiliza diferii parametri. Aceti parametri pot fi utilizai folosind parametrii argc i argv ai funciei principale. Parametrul argc este de tip ntreg i indic numrul de parametri din linia de comand. Parametrul argv este un tablou de pointeri spre zonele n care sunt pstrai parametrii liniei de comand. Acetia se consider iruri de caractere. Astfel antetul funciei principale va fi :

main (int argc, char *argv[ ]) Exemplu: Considerm c la lansarea programului prog s-au furnizat parametrii: MARTIE 1956 n acest caz argc=4, iar tabloul argv conine pointerii: - argv[0] - pointer spre numele programului (calea, numele i extensia .EXE - argv[1] - pointer spre irul 31; - argv[2] - pointer spre irul MARTIE; - argv[3] - pointer spre irul 1991. Observaii: 1o. Lansarea unui program se face cu prima instruciune a funciei principale. Deci parametrii argc i argv au deja n acest moment valorile indicate mai sus, putnd fi analizai chiar cu prima instruciune executabil. 2o. n mod frecvent, aceti parametrii reprezint diferite opiuni ale programului, date calendaristice, nume de fiiere, etc. 3o. argv[0] este ntotdeauna pointerul spre numele fiierului cu imaginea executabil a programului. void main ( int argc, char *argv[]) // va afisa parametrii din linia de comanda { int i; for (i=0; i<argc; i++;) printf (%s\n,argv[i]); }

6.7. MODIFICATORUL const Am vzut anterior c o constant se definete prin caracterele care intr n compunerea ei. De asemenea, n acelai capitol s-a artat c putem atribui un nume unei constante printr-o construcie #define. Un astfel de nume se spune c este o constant simbolic i el se substituie prin irul de caractere care i corespunde, n faza de preprocesare. Un alt mod de a defini o constant este acela de a folosi modificatorul const ntr-o declaraie. Printr-o astfel de declaraie, unui nume i se poate atribui o valoare constant. n acest caz, numele respectiv nu mai este tratat de preprocesor i el poate fi folosit n program n mod analog cu numele variabilelor. Unui astfel de nume declarat cu ajutorul modificatorului const nu i se poate schimba valoarea printr-o expresie de atribuire, ca i unei variabile obinuite. Formatele declaraiei cu modificatorul const sunt urmtoarele: tip const nume = valoare; const tip nume = valoare; tip const nume; const tip nume; const nume = valoare; const nume; Exemplu: void main (void) { const i=10; // i devine egal cu 10; nu este posibila o atribuire i=0 const pi = 3.1415926 char *const s=martie; // s este un pointer constant spre zona in care este // pastrat sirul de caractere martie. Valoarea lui s // nu poate fi schimbata dar continutul zonei spre // care pointeaza s poate fi schimbat *s= 1; // schimba litera m cu 1 *(s+1)=2 // schimba litera a cu 2

char const *s=aprilie; // s este un pointer spre o zona constanta. // valoare lui s poate schimbata dar sirul aprilie // nu poate fi modificat const char *s=aprilie // este identica cu declaratia de mai sus. } Modificatorul const se folosete frecvent la declararea parametrilor formali de tip pointer. O astfel de declaraie are formatul: const tip *nume_parametru_formal; Un parametru formal declarat prin construcia : tip *nume_parametru_formal; corespunde unui parametru efectiv a crui valoare este o adres. La apel, valoarea parametrului formal devine egal cu aceast adres. Datorit acestui fapt, funcia apelat poate s modifice data aflat la adresa respectiv. Dac se folosete modificatorul const utilizat la declararea unui astfel de parametru formal atunci se interzice funciei apelate s modifice data de la adresa recepionat la apel de ctre parametrul formal corespunztor. Acest mecanism este folosit frecvent n cazul funciilor de tratare a irurilor de caractere. De exemplu funcia strlen din biblioteca standard a limbajului C are prototipul: unsigned strlen (const char *s); Ea se apeleaz prin expresii de atribuire de forma: i=strlen(x); unde x este un pointer spre o zon de memorie n care se afl un ir de caractere. Funcia strlen determin lungimea irului aflat la adresa recepionat de ctre parametrul s. Ea nu are voie s modifice irul respectiv i din aceast cauz parametrul s se declar folosind modificatorul const. 6.8. STIVA Prin stiv (stack n englez) nelegem o mulime ordonat de elemente la care accesul se realizeaz conform principiului ultimul venit primul servit. n englez stiva se mai numete i list LIFO (Last In First Out). O modalitate simpl de a implementa o stiv este pstrarea elementelor ei ntr-un tablou unidimensional. n acest tablou se vor pstra elementele stivei unul dup altul. De asemenea, ele se pot scoate din tablou n ordinea invers pstrrii lor. La un moment dat se poate scoate ultimul element pus pe stiv i numai acesta. Despre ultinul element pus n stiv se spune c este vrful stivei, iar despre primul element c este baza stivei. Accesul este pemis doar la vrful stivei: un element se poate pune pe stiv numai dup elementul aflat n vrful stivei i dup aceast operaie el ajunge vrful stivei; se poate scoate de pe stiv numai elementul aflat n vrful stivei i dup aceast operaie n vrful stivei rmne elementul care a fost pus pe stiv naintea lui. Vom numi stack tablou de tip int afectat stivei i next variabila care indic prima poziie liber din stiv. Deci stack[0] este baza stivei iar stack[n] va fi vrful stivei. Vom defini mai multe funcii asociate tabloului stack: - push funcia care pune un element n stiv; - pop funcia care scoate un element din stiv;

- clear devine vid;

funcia de iniializare a stivei; dup apelul ei stiva

#define MAX 1000 static int stack[1000]; static next = 0; void push(int x) { if (next < MAX) else

// indicele pentru baza stivei // pune pe stiva valoarea lui x

stack [next++]=x; printf (stiva este depasita\n); } int pop() { if (next >0) else } void clear(void) { next=0; } LECIA 7. RECURSIVITATE Spunem c o funcie C este recursiv dac ea se autoapeleaz nainte de a se reveni din ea. Funcia se poate reapela fie direct, fie indirect (prin intermediul altor funcii). La fiecare apel al unei funcii, parametrii i variabilele locale se aloc pe stiv ntr-o zon independent. De asemenea, orice apel recursiv al unei funcii va conduce la o revenire din funcie la instruciunea urmtoare apelului respectiv. La revenirea dintr-o funcie se realizeaz curarea stivei, adic zona de pe stiv afectat la apel parametrilor i variabilelor automatice se elibereaz. Un exemplu simplu de funcie apelata recursiv este funcia de calcul al factorialului. Putem defini recursiv funcia factorial astfel: factorial(n)= 1, dac n=0 factorial(n)=n*factorial(n-1), dac n>0 n limbajul C avem : double factorial (int) { if (n= = 0) return 1.0; else return n*factorial(n-1); } Observaii: 1o. n general, o funcie recursiv se poate realiza i nerecursiv, adic fr s se autoapeleze. 2o. De obicei, recursivitatea nu conduce nici la economie de memorie i nici la execuia mai rapid a programelor. Ea permite ns o descriere mai compact i mai clar a funciilor. Acest lucru rezult i din exemplul de mai sus de calcul al factorialului. 3o. n general, funciile recursive sunt de preferat pentru procese care se definesc recursiv. Exist i excepii. De exemplu algoritmul de generare a permutrilor de n obiecte poate fi descris recursiv astfel: // videaza stiva // scoate elementul din varful stivei si returneaza valoarea lui

return stack [--next]; { printf (stiva este vida\n); return 0; }

avnd n memorie toate cele (n-1)! permutri, atunci permutrile de n obiecte se genereaz nsernd pe n n toate poziiile posibile ale fiecrei permutri de n-1 obiecte. Dar ne aducem aminte c 10!=3628800 i capacitatea stivei se depete repede. Exemple: Programul determin recursiv cmmdc (algoritmul lui Euclid) a dou numere ntregi (de tip long): cmmdc (a,b) = b, dac a%b =0 (restul mpririi lui a la b e zero) cmmdc (a,b) = cmmdc (b,a%b), n caz contrar. #include <iostream.h> #include <conio.h> long cmmdc(long a, long b) { if (!(a % b)) return b; else return cmmdc(b, a % b); } void main(void) { long x,y; clrscr(); cout << "dati un numar natural="; cin >> x; cout << "dati alt numar natural="; cin >> y; cout << "cmmdc(" << x << "," << y << ")=" << cmmdc (x,y); } Am folosit funciile de intrare / ieire cin i cout, imediat se observ modul lor de utilizare. Programul unidimensional determin recursiv suma unor elemente de tablou

a[1]+a[2]+ . . . + a[n] #include <iostream.h> #define MAX 100 int a[MAX]; // suma(n)= 0, // suma(n)=suma(n-1) + a[n] int suma(int n) { if (!n) return 0; else return a[n]+suma(n-1); }

daca n=0 daca n>0

void main(void) {int n,i; cout << "dati n= "; cin >> n; for (i=1; i<=n; i++) { cout << "a[" << i << "]= "; cin >> a[i]; } cout << "suma numerelor este " << suma(n); } Programul determin recursiv Fibonacci definit dup cum urmeaz: termenul al n-lea din irul lui

fibonacci[0]=0 fibonacci[1]=1 fibonacci[n]=fibonacci[n-1]+fibonacci[n-2],

dac n>1

#include<iostream.h> long fibonacci (long n) {if (!n) return 0; else if (n==1) return 1; else return fibonacci(n-1) + fibonacci(n-2); } void main (void) { long n; cout << "dati n = "; cin >> n; cout << "fibo(" << n << ") =" << fibonacci (n); } Programul determina maximul dintr-un vector de numere astfel: M(n)= a[1] dac n=1 M(n)= max { M(n-1),a[n] } #include<iostream.h> #define MAX(x,y) (x > y ? x : y) int a[100]; int M(int n) { if (n= =1) else } return a[1]; return MAX (M(n-1), a[n]); dac n>1

void main(void) {int n,i; cout << "dati n="; cin >> n; for (i=1; i<=n; i++) { cout << "a[" << i << "]= "; cin >> a[i]; } cout << "maximul este " << M(n); } 5) Programul afiseaz un ir de caractere n mod recursiv, caracter cu caracter, considernd c irul de caractere este format din primul caracter(capul) + restul irului de caractere (coada). #include <iostream.h> #include <conio.h> #define max 100 char sir [max]; int n; void afis (int m) { if (m = = n+1) return; else { cout << sir[m]; afis(m+1); } }

void main (void) {int i; do { cout << "\ndati lungimea sirului ="; cin >> n; } while ( (n< 0) || (n > max)); for(i=1; i<=n; i++) { cout << "sir[" << i << "]="; cin >> sir[i]; } afis(1); getch(); } 6) Programul ce urmeaz e oarecum asemntor cu exemplul anterior doar c afieaz irul de caractere de la sfrit spre nceput. #include <iostream.h> #include <conio.h> #define max 100 char sir [max]; void afis (int m) { if (m==0) return; else { cout << sir[m]; afis(m-1); } } void main (void) {int n,i; do {cout << "\ndati lungimea sirului ="), cin >> n;} while ( (n< 0) || (n > max)); for(i=1; i<=n; i++) { cout << "sir[" << i << "]="; cin >> sir[i]; } afis(n); getch(); } 7) ntregi: Programul sorteaz prin metoda quicksort un vector de numere

#define dim 50 #include <stdio.h> #include <conio.h> int x[dim+1],i,n; void tipsir () { for (i=1; i<=n; i++) { printf("%3d",x[i]); if (!(i % 20)) printf ("\n"); } } void quik(int st, int dr) {int i,j,y; i=st; j=dr; y=x[i]; do { while ((x[j] >= y) && (i<j)) j - -; x[i]=x[j]; while ((x[i] <= y) && (i<j)) i++; x[j]=x[i]; }

while (i != j); x[i]=y; if (st < i-1) if (i+1 < dr) x[j]=x[i]; }

quik(st,i-1); quik(i+1,dr);

void citire (void) { int cod = 0; n = dim+1; while ( n <= 0 || n > dim || ! cod ) { printf ("\ndati dim. sir:"); cod=scanf ("%d",&n); } i = 1; while (i<=n) { printf ("x[%2d]=",i); scanf ("%d", &x[i]); i++; } } void main(void) { clrscr(); citire(); clrscr(); printf ("\n\nsir initial\n"); tipsir(); quik(1,n); printf ("\n\nsir sortat\n"); tipsir(); getche(); } LECIA 8. STRUCTURI, TIPURI UTILIZATOR Dup cum am vzut datele de acelai tip se pot grupa n tablouri. Limbajul C permite gruparea unor date de tipuri diferite sub alte forme de organizare numite structuri. Tablourile au un tip i anume tipul comun elementelor lor. Astfel, distingem tablouri de tip ntreg, de tip caracter, de tip flotant, etc. n cazul structurilor, nu mai avem un tip comun. Fiecare structur reprezint un nou tip de date, tip care se introduce prin declaraia structurii respective. Un exemplu simplu de structur este data calendaristic, cu componentele urmtoare: ziua; luna; anul. unde: ziua i anul sunt date de tip ntreg iar luna este un tablou de caractere. Structura ca i tabloul, este o muline ordonat de elemente. n exemplul de mai sus se consider c ziua este primul ei element, luna este al doilea iar anul este ultimul ei element. Trebuie s precizm c referirea la componentele unei structuri nu se mai face cu ajutorul indicilor ci prin calificare. 8.1. DECLARAIA DE STRUCTUR O structur se poate declara n mai multe feluri, astfel: Formatul 1: struct nume_structura { lista_declaratii

}; Cu ajutorul acestui format se introduce un nou tip de dat cu numele nume_structur. Lista de declaraii este format din declaraii obinuite. Tipul data_calendaristica l putem introduce astfel: struct data_calendaristica { int ziua; char luna[11]; int anul; }; O astfel de declaraie se numete declaraie de tip. S reinem c unui nou tip de date nu i se aloc memorie, el este doar contabilizat ca un nou tip utilizator pe lng tipurile predefinite ale limbajului C. Formatul 2: struct nume_structura { lista_declaratii }lista_variabile; Un astfel de format introduce tipul utilizator nume_structura i declar o list de varibile n care fiecare element din list are tipul nume_structur. Prin exemplu urmtor se introduc variabilele dc1 i dc2 ca date elementare de tipul data_calendaristica i tabloul dc de 13 componente. struct data_calendaristica { int ziua; char luna[11]; int anul; } dc1, dc2, dc[13]; Formatul 3: struct { lista_declaraii } lista_variabile; Acest format se foloseste dac nu vrem sa dm nume noului tip structurat i totodat dac nu mai vrem s-l folosim. Deci nu vom mai pute declara alte date de tipul structurat nou introdus pentru c tipul nu are nume. Exemplu: struct { int ziua; char luna[11]; int anul; } dc1, dc2, dc[13]; S-au declarat varibilele dc1, dc2 i tabloul dc avnd noul tip structurat utilizator dar nu se mai dorete s declarm alte date de acest tip. Observaii: 1o. Dac se folosete formatul 1 atunci pentru a declara date de tipul utilizator nou introdus se folosete o construcie de forma: struct nume_ structura lista_variabile; Compilatorul aloc memorie varibilelor din lista de variabile, tratnd aceast construcie ca i declaraiile obinuite. 2o. Componentele unei structuri pot fi ele nsele date structurate. O component care nu este structurat se numete component elementar. 3o. Ca i n cazul celorlalte tipuri de variabile se pot defini structuri globale, statice sau automatice. Structurile statice se declar precednd declaraiile lor prin cuvntul static, iar cele externe prin cuvntul cheie extern. 4o. Elementele unei date de tip structur pot fi iniializate dup modelul iniializrii variabilelor care au tipuri predefinite.

Exemple: 1) Introducem tipul utilizator data_calendaristica astfel: struct data_calendaristica { int ziua; char luna[11]; int anul; }; pentru a iniializa o dat de tipul data_calendaristic vom scrie: struct data_calendaristica dc1={31, martie, 1956}; 2) Dac declarm un nou tip date_personale i n care vrem s folosim tipul data_calendaristica, vom scrie: struct date_personale { char nume[30]; char adresa[50]; struct data_calendaristica data_angajarii; }; 8.2. ACCESUL LA ELEMENTELE UNEI STRUCTURI Pentru a avea acces la componentele unei date structurate va trebui s folosim o calificare de forma: nume_data_structurata.nume_componenta Astfel dac avem tipul structurat data_calendaristica introdus in exemplele anterioare i declarm data dc astfel: struct data_calendaristica dc; atunci pentru a ne referi la componentele construciile: dc.ziua dc.anul dc.luna (atenie este pointer spre caractere) Dac avem declarat un tablou astfel: struct data_calendaristica tdc[10]; atunci pentru fiecare component i ne vom referi astfel: tdc[i].ziua tdc[i].anul tdc[i].luna (este pointer) tdc[i].luna[0], tdc[i].luna[1], . . . , tdc[i].luna[11] Ca i tablourile structurile se pot transfera prin parametrii, transfernd un pointer spre data structurat respectiv, adic adresa de nceput a zonei alocate structurii. Deci, printr-un apel de forma: functie(&data_structurata); se transfer funciei functie adresa de nceput a zonei alocate structurii data_structurata. Dac data_structurata este o structura de tipul tip, atunci antetul funciei functie este urmtorul: void functie(tip *p) unde p este pointer spre tipul structurat tip. Pentru data structurat funciei functie este: dc de tipul data_calendaristica antetul datei dc vom folosi

data_nasterii,

void functie(struct data_calendaristica *p) iar apelul pentru data dc se face functie(&dc); Printr-un astfel de apel, funcia apelat nu are acces la numele datei structurate transferate, ci numai la pointerul spre ea. De aceea se pune problema accesului la componentele datei structurate prin pointerul la ea. n acest caz numele datei structurate se va nlocui prin *p. Deci, n cazul datei structurate dc, transferate ca i mai sus, n locul construciei dc.zi vom scrie: (*p).zi nlocuind numele datei structurate dc prin *p, unde p este un pointer spre dc. Observaie: 1o. Parantezele rotunde din construcia de mai sus sunt obligatorii, deoarece punctul este un operator prioritar operatorului unar *. 2o. Construcia de mai sus poate fi nlocuit prin p->zi care este identic cu ea. Simbolul -> se compune din caracterele - i > scrise unul dup cellalt fr spaiu ntre ele. El se numete sgeat i este considerat a fi un operator cu aceeai prioritate ca i punctul, deci de prioritate maxim. 8.3. ATRIBUIRI DE NUME PENTRU TIPURI DE DATE Dup cum tim tipurile de baz ale limbajului C, numite i tipuri predefinite se identific printr-un cuvnt cheie (int, char, float, etc). Totodat prin instruciunea struct, programatorul poate s introduc un tip nou. Programatorul poate s atribuie un nume unui tip (predefinit sau utilizator) cu ajutorul construciei: typedef tip nume_nou_tip; unde: tip este numele unui tip predefinit sau al unui tip utilizator (introdus cu struct); nume_nou_tip este noul nume atribuit tipului respectiv. Dup ce s-a atribuit un nou nume unui tip, numele respectiv poate fi utilizat pentru a declara date de acel tip, la fel cum se utilizeaz n declaraii cuvintele cheie int, char, float, etc. Observaii: 1o. De obicei numele atribuit unui tip se scrie cu litere mari. 2o. Un exemplu de astfel de nume exist n fiierul stdio.h pentru tipul fiier, cruia i s-a atribuit numele FILE. Exemple: Fie declaraiile: typedef int INTREG; typedef float REAL; n continuare, denumirile INTREG i REAL se pot folosi la fel ca i cuvintele cheie int i float. Cu alte cuvinte, declaraia: INTREG i, j, tablou[10]; este identic cu declaraia urmtoare: int i, j, tablou[10]; Analog: REAL x, y, z;

este identic cu declaraia: float x, y, z; typedef struct data_calendaristica { int ziua; char luna[11]; int anul; } DC; Prin aceast declaraie se atribuie denumirea DC tipului structurat data_calendaristica. n continuare putem declara date de tip DC: DC data_nasterii, data_angajarii; DC data_curenta ={31,august,1998}; typedef int *PI; Prin aceast declaraie se introduce un sinonim pentru tipul pointer spre ntregi: int *. Putem s declarm n continuare pointeri spre ntregi astfel: PI p; care este echivalent cu: int *p; 4) Declaraia { double real; double imaginar; } COMPLEX; typdef struct

introduce numele COMPLEX pentru datele de tip complex. Funcia urmtoare returneaz modulul unui numr complex: typedef struct double real; double imaginar; } COMPLEX; #include <math.h> double modul (COMPLEX *x) // returneaza modulul numarului // spre care pointeaza x { return sqrt (x->real * x->real + x->imaginar * x->imaginar); } { 8.4. UNIUNE Limbajul C ofer utilizatorului posibilitatea de a folosi aceeai zon de memorie pentru a pstra date de tipuri diferite n momente diferite ale execuiei programului. Astfel, de exemplu, putem utiliza o zon de memorie pentru a pstra la un moment dat o dat flotant, iar ulterior s reutilizm aceeai zon pentru o dat ntreag sau de tip pointer. Reutilizrile zonelor de memorie conduc la utilizarea mai eficient a acesteia, uneori putndu-se obine o economie substanial a spaiului de memorie alocat programului. O uniune se declar printr-o construcie asemntoare declaraiei de structur. Deosebirea const n nlocuirea cuvntului struct prin union: union nume { tip_membru_1; . . . tip_membru_2; } Exemplu:

{ int i; float f; double d; };

union u

Prin aceast declaraie s-a definit tipul de date u. n continuare, putem declara date de tipul u printr-o declaraie de forma: union u u1; unde u1 este o dat de tip u creia i se aloc o zon de memorie care poate fi utilizat pentru a pstra date de tipurile int, float sau double. Deoarece tipul double necesit memoria cea mai mare se aloc 8 octei. Astfel zona u1 poate pstra pe oricare din celelalte componente ale uniunii dar n momente diferite ale execuiei programului. Accesul la componentele unei uniuni se face la fel ca i n cazul structurilor. Astfel, pentru a ne referi la componenta i a uniunii u1 definit n exemplul anterior folosim construcia: u1.i sau dac p este pointer spre tipul u declarat prin union u *p; atunci construcia p -> i permite accesul la componenta i a uniunii spre care pointeaz p. Pentru a evita erorile legate de evidena n fiecare moment a datei care se prelucreaz se ataeaz unei uniuni o dat menit s indice componenta curent. Aceast dat este specificat pentru fiecare uniune. De exemplu pentru uniunea u1 definit anterior este important s se tie dac zona de memorie conine un ntreg, un flotant n simpl precizie sau un flotant n dubl precizie. Se definesc trei constante simbolice: #define INTREG 1 #define F_SIMPLU 2 #define F_DUBLU 3 Modificm tipul u atand data tip_curent de tip int astfel: struct u {int tip_curent; union { int i; float f; double d; } uu; }; Declarm structura us astfel: struct u us; n acest caz, n momentul n care se pstreaz o dat n zona rezervat uniunii, se atribuie componentei tip_curent una din constantele definite anterior: INTREG, dac se pstreaz un ntreg; F_SIMPLU dac se pstreaz un flotant n simpl precizie; F_DUBLU dac se pstreaz un flotant n dubl precizie. Astfel cnd se folosete componenta de tip int se va asocia atribuirea: us.tip_curent=INTREG; Analog se vor folosi i atribuirile urmtoare cnd se vor folosi componentele de tip float sau de tip double: us.tip_curent=F_SIMPLU; respectiv: us.tip_curent=F_DUBLU;

n felul acesta, se poate testa, n fiecare moment, tipul de dat prezent n zona rezervat. Aceasta se poate face printr-o secven de instruciuni if sau prin intermediul instruciunii switch. if (us.tip_curent = = INTREG) else if (us.tip_curent = = FSIMPLU) else if (us.tip_curent = = FDUBLU) else eroare // se foloseste us.uu.i // se foloseste us.uu.f // se foloseste us.uu.d

sau folosind switch avem o construcie mai clar de forma: switch (us.tip_curent) { case INTREG: // se foloseste us.uu.i break; case FSIMPLU: // se foloseste us.uu.f break; case FDUBLU // se foloseste us.uu.d break; default: // eroare } Programul urmtor geometrice: cerc; dreptunghi; ptrat; triunghi. calculeaz ariile pentru urmtoarele figuri

Programul citete datele pentru o figur geometric, calculeaz aria figurii respective i scrie rezultatul: La intrare se folosesc urmtoarele formate: - pentru cerc C raza; - pentru dreptunghi D lungime laime; - pentru ptrat P latur; - pentru triunghi T latur latur latur; - sfrit fiier EOF. n cazul triunghiului, se utilizeaz formula lui HERON pentru calculul ariei: aria = sqrt (p*(p-a)(p-b)(p-b)) unde p este semiperimetrul, iar a, b, c sunt cele 3 laturi. #include <stdio.h> #include <math.h> #define PI 3.14159265 #define EROARE -1 #define CERC 1 #define PATRAT 2 #define DREPT 3 #define TRIUNGHI 4 typedef struct { int tip; union { double raza double lp ; double ld[2] ; double lt[3] ;

// tipul figurii // cerc // patrat // dreptunghi // triunghi

} fig; }FIG; void main (void) // calculeaza arii { double aria,p; int i; char car[2]; FIG zfig; for(; ;) // citeste primul caracter,el defineste tipul figurii geometrice { printf(se cere o litera mare\n); if ((i = scanf("%1s",car)) == EOF) break; if (i != 1) { printf (" se cere o litera mare\n"); continue; } zfig.tip = EROARE; switch(car[0]) {case 'C': // cerc printf("se cere raza cercului in flotanta\n"); i = scanf("%lf", &zfig.fig.raza); if(i != 1) { printf("se cere raza cercului in flotanta\n"); break; } zfig.tip = CERC; // se pastreaza tipul figurii break; case 'P': // patrat printf("se cere latura patratului in flotanta\n"); i = scanf("%lf",&zfig.fig.lp); if( i !=1) { printf("se cere latura patratului in flotanta\n"); break; } zfig.tip = PATRAT; break; case 'D': // dreptunghi printf("se cer laturile dreptunghiului in flotanta\n"); i = scanf("%lf %lf",&zfig.fig.ld[0],&zfig.fig.ld[1]); if(i != 2) { printf("se cer laturile dreptunghiului in flotanta\n"); break; } zfig.tip = DREPT; break; case 'T': // triunghi printf("se cer laturile triunghiului in flotanta\n"); i = scanf("%lf %lf %lf", &zfig.fig.lt[0], &zfig.fig.lt[1],&zfig.fig.lt[2]); if(i != 3) { printf("se cer laturile triunghiului in flotanta\n"); break; } zfig.tip =TRI; break; printf("laturile nu formeaza un triunghi\n"); break; default: printf("se cere una din literele urmatoare\n"); printf("C pentru cerc\n"); printf("P pentru patrat\n"); printf("D pentru dreptunghi\n");

printf("T pentru triunghi\n"); } // sfarsit switch switch (zfig.tip) {case CERC: // aria cercului printf("raza=%g aria=%g\n", zfig.fig.raza, PI*zfig.fig.raza*zfig.fig.raza); break; case PATRAT: // aria patratului printf("latura =%g aria=%g\n",zfig.fig.lp, zfig.fig.lp*zfig.fig.lp); break; case DREPT: // aria dreptunghiului printf("lungimea =%g latimea =%g\n", zfig.fig.ld[0], zfig.fig.ld[1]); printf("aria=%g\n", zfig.fig.ld[0]*zfig.fig.ld[1]); break; case TRIUNGHI: // aria triunghiului p=(zfig.fig.lt[0] + zfig.fig.lt[1] + zfig.fig.lt[2])/2; if(p>zfig.fig.lt[0] && p>zfig.fig.lt[1] && p>zfig.fig.lt[2]) {p=p*(p-zfig.fig.lt[0])*(p-zfig.fig.lt[1])* (p-zfig.fig.lt[2]); printf("a=%g b=%g c=%g\n", zfig.fig.lt[0], zfig.fig.lt[1], zfig.fig.lt[2]); printf("aria = %g\n",sqrt(p)); } else { printf ( laturile nu formeaza un triunghi); break; } default : // avans pana la newline sau EOF while ((i = getchar()) != \n && i != EOF); } // sfarsit switch if (i = = EOF) break; } // sfarsit for } // sfarsit main

8.5. CAMP Limbajul C permite utilizatorului definirea i prelucrarea datelor pe bii. Utilizarea datelor pe bii este legat de folosirea indicatorilor care de obicei sunt date care iau numai dou valori 0 sau 1. Nu este justificat ca un astfel de indicator s fie pstrat ca un ntreg pe 16 bii i nici mcar pe un octet. Indicatorul poate fi pstrat pe un singur bit. n acest scop, limbajul C ofer posibilitatea de a declara date care s se aloce pe bii (unul sau mai muli bii). Acest lucru i gsete aplicare n programele de sistem. Astfel, de exemplu, atributele variabilelor dintr-o tabel de simboluri pot fi pstrate pe bii, ceea ce conduce la o economisire substanial a memoriei ocupate de tabela respectiv. Prin camp nelegem un ir de bii adiaceni coninui ntr-un cuvnt calculator. Cmpurile se grupeaz formnd o structur. Un cmp se declar ca i o component a unei structuri i el are tipul unsigned (ntreg fr semn). Totodat n declaraia cmpului se indic i dimensiunea lui n bii. n general, o structur cu componente cmpuri are forma: struct { camp1; . . . campn; } nume; unde campi (i=1,...,n) are unul din formatele de mai jos: unsigned nume : lungime_n_bii sau : lungime_n_bii

Exemplu:

struct { unsigned a:1; unsigned b:1; unsigned c:2; unsigned d:2; unsigned e:3; } indicatori; Data indicatori se aloc ntr-un cuvnt calculator, adic pe 16 bii. Componentele ei sunt: a un bit; b un bit; c doi bii; d doi bii; e trei bii. La cmpuri ne putem referi la fel ca i la componentele oricrei structuri. Deci la indicatorii de mai sus ne putem referi prin urmtoarele construcii: indicatori.a indicatori.b indicatori.c indicatori.d indicatori.e Alocarea biilor este dependent de calculator. De obicei biii se aloc de la dreapta spre stnga ca n figura de mai jos: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 a b c d e Observaii: 1o. Dac un cmp nu poate fi alocat n limitele unui cuvnt, el se aloc n ntregime n cuvntul urmtor. 2o. Nici un cmp nu poate avea o dimensiune mai mare dect 16 bii. 3o. Formatul fr nume (al doilea format) pentru cmp se folosete pentru cadraje. Acest lucru este util atunci cnd sunt zone de bii neutilizate n cadrul unui cuvnt. De asemenea, utilizarea formatului cu lungime egal cu zero permite ca alocarea cmpurilor urmtoare lui s se fac n cuvntul urmtor. 4o. O structur care are i componente cmpuri poate avea i componente obinuite. 5o. Nu se pot defini tablouri de cmpuri. 6o. Unui cmp nu i se poate aplica operatorul adres. Cmpurile se utilizeaz frecvent la scrierea unor programe de sistem, cum ar fi : drivere pentru periferice, compilatoare, etc. Utilizarea cmpurilor poate conduce la programe cu o portabilitate redus. Totodat, accesul la date pe bii conduce la creterea numrului de operaii, fiind necesare deplasri i operaii pe bii suplimentare, fapt ce poate conduce att la creterea timpului de execuie a programelor, ct i la creterea memoriei utilizate. Ori datele pe bii se folosesc chiar n ideea de a economisi memorie. 8.6. TIPUL ENUMERAT

Tipul enumerat permite utilizatorului s foloseasc n program nume sugestive n locul unor valori numerice. De exemplu, n locul numrului unei luni calendaristice, se poate folosi denumirea ei: ian feb mar n locul valorilor 0 i 1 se pot folosi cuvintele FALS i ADEV RAT. Prin aceasta, se introduce o mai mare claritate n programe, deoarece valorile numerice sunt nlocuite prin diferite sensuri atribuite lor. Un tip enumerat se introduce printr-o declaraie de forma: enum nume {nume0, nume1, . . . , numen}; Prin aceast declaraie se definete tipul enumerat nume, iar numei are valoarea i. O form mai general a declaraiei de mai sus permite programatorului s foreze valorile numelor din acolad. n acest scop, se pot folosi construcii de forma: numei= eci unde eci este o expresie constant de tip int. Cu alte cuvinte, unui nume i se poate atribui o valoare sau valoarea lui coincide cu a numelui precedent mrit cu 1. Dac primului nume din acolad nu i se atribuie o valoare, el are valoarea 0. Numele nume0, nume1,. . . , numen trebuie s fie nume diferite. Ele sunt constante i valoarea lor se stabilete prin declaraia n care au fost scrise. Domeniul lor de valabilitate este definit de domeniul de valabilitate al declaraiei prin care se definesc: instruciunea compus care conine declaraia; fiierul surs n care este scris declaraia, dac este extern oricrei funcii. Valorile atribuite lui nume0, nume1, . . . , numen sunt de obicei diferite, dar unele pot s i coincid. Dup ce s-a introdus un tip enumerat, se pot declara date de tipul respectiv printr-o declaraie de forma: enum nume lista_de_variabile; Datele de tip enumerat se consider de tip int i utiliza n program oriunde este legal s apar o dat de tip int. se pot

Observaii: 1o. Se pot utiliza, ca i n cazul structurilor, construcii de forma: enum nume {nume0, nume1,. . . , numen} lista_de_variabile; sau enum { nume0, nume1,. . . , numen} lista_de_variabile; 2o. De asemenea, se poate atribui un nume unui tip enumerat: utiliza construcia typedef pentru a

typedef enum nume {nume0, nume1,. . . , numen} NUME; n continuare se pot declara date de tipul NUME, astfel: NUME lista_de_variabile; Exemple: enum luna{ian=1,feb,mar,apr,mai,iun,iul,aug,sep,oct,nov,dec}; enum luna luna_calendaristica Prin prima declaraie se introduce tipul enumerat luna. Mulimea de valori asociate acestui tip este format din numerele ntregi 1,2, . . . , 12. Se pot utiliza denumirile: ian ia valoarea 1

feb ia valoarea 2 . . . dec ia valoarea 12 A doua construcie declar data luna_calendaristica de tipul luna. Ei i se pot atribui valori prin expresii de atribuire de forma: luna_calendaristica = mar sau luna_calendaristica = mai + 4 typedef enum {luni, marti,miercuri,joi,vineri,sambata,duminica} ZI; ZI z; Variabila z este de tip ZI. Se poate utiliza n expresii de forma: z=marti; if(z<sambata) // trateaza ziua de lucru else // trateaza zi de odihna LECIA 9. LISTE 9.1. DATE STRUCTURATE DEFINITE RECURSIV Limbajul C permite definirea de tipuri structurate recursiv (autorefereniate). Acest lucru se face cu ajutorul pointerilor, i anume un element al structurii poate s fie un pointer spre tipul de dat introdus prin structura respectiv: struct nume { declaratii struct nume *p; declaratii }; Un tip definit ca mai sus se spune c este un tip autoreferit sau recursiv. O dat structurat declarat printr-un astfel de tip se spune c este autoreferit sau recursiv. Datele structurate recursive au numeroase aplicaii n prelucrarea listelor nlnuite i arborescente. Dou tipuri structurate t1 i t2 pot s conin fiecare un pointer spre celalalt. n acest caz se va proceda ca mai jos: struct t1; // o declaratie inainte fara de care nu se poate // declara tipul t2 struct t2 { declaratii struct t1 *pt1; declaratii }; struct t1 { declaratii struct t2 *pt2; declaratii }; 9.2. LISTE NLNUITE Datele structurate se pot organiza n tablouri sau n structuri recursive introducnd n tipul structurat unul sau mai muli pointeri spre tipul structurat respectiv. Astfel se stabilete o relaie de ordine (uneori chiar mai multe) ntre elementele mulimii de date structurate; de asemenea, mulimea rescpectiv se poate organiza n mod dinamic, adugnd elemente noi sau suprimndu-le pe cele care nu mai sunt necesare.

Definiie O mulime dinamic de structuri recursive de acelai tip i care satisfac una sau mai multe relaii de ordine introduse prin pointeri se numete list nlnuit. Elementele listei se mai numesc noduri. Cele mai utilizate tipuri de list sunt: lista simplu nlnuit; lista circular simplu nlnuit; lista dublu nlnuit; lista circular dublu nlnuit;. Cele patru tipuri de liste sunt exemplificate grafic astfel: capul

list liniar simplu nlnuit; capul

list liniar circular simplu nlnuit; capul

list liniar dublu nlnuit;

capul

list liniar circular dublu nlnuit; 9.3. LISTA LINIAR SIMPLU NLNUIT O list simplu nlnuit; este o list nlnuit; ale crei noduri satisfac o singur relaie de ordine introdus prin pointeri. Tipul unui nod dintr-o list simplu nlnuit; se poate declara n dou moduri: struct tnod { declaratii struct tnod *urmator; declaratii }; typedef struct tnod { declaratii struct tnod *urm tor; declaratii

} TNOD; Observaii 1o. Varianta b) este mai folosit. 2o. Pointerul urmtor introduce o relaie de ordine ntre nodurile de tip TNOD. 3o. Ultimul nod al listei va avea pointerul urmator = NULL. 4o. Pentru nodurile interioare ale listei pointerul urmator va avea valori adrese; dac urmator din nodul a pointeaz spre nodul b, spunem c nodul b este succesorul lui a. Operaiile ce se pot efectua asupra unei liste simplu nlnuit; crearea listei; accesul la un nod al listei; inserarea unui nod nlnuit; tergerea unui nod dintr-o list; tergerea unei liste. Memorarea listelor se poate face: dinamic (n memoria intern); static (n fiiere). Pentru modul dinamic se va folosi funcia malloc la crearea listei ct i la inserarea de noduri, iar la tergerea de noduri funcia free. 9.4. CREAREA I AFIAREA UNEI LISTE Vom crea o list ce conine n noduri informaii despre numere ntregi i ptratele lor. Avem dou funcii: una de creare care ntoarce adresa capului listei i o funcie de afiare a informaiei din noduri. Vom comenta instruciunile importante din program.

#include <stdio.h> #include <alloc.h> typedef struct nod { int nr; int patrat; struct nod* leg; } NOD;

// definirea tipului NOD

NOD *creaza(void) // functia de creare, intoarce adresa capului { NOD *cap,*p,*pc; int i,lung; printf("\n\n\n\ creare lista simplu inlantuita\n\n"); lung = sizeof(NOD); pc=(NOD *)malloc(lung); // pc este un pointer curent, in el se vor pune adresel noi cap=pc; printf("dati informatia elementului : "); scanf ("%d",&i); while (i>0) // crearea listei se termina la numar negativ { p=pc; // pointer ce pastreaza adresa noua p->nr=i; // incarcarea informatiei p->patrat=i*i; pc=(NOD *)malloc(lung); // se cere o noua adresa de memorie p->leg=pc; // se leaga pointerul leg la noua adresa printf("dati informatia elementului : "); scanf ("%d",&i); } p->leg=NULL; // ultimul nod al listei are pointerul leg = NULL free(pc); // eliberarea ultimei adrese care de fapt nu face parte din lista

return cap; }

// returneaza adresa capului listei

void afisare(NOD *p) // functia de afisare a listei { while (p != NULL) // cat timp n-am ajuns la ultimul nod { printf ("\n numarul %d si patratul sau %d", p->nr,p->patrat); p=p->leg; // trecerea la urmatorul nod al listei } } void main (void) // functia principala { NOD *capul; clrscr(); capul=creaza(); // lista e cunoscuta prin adresa capului afisare(capul); getch(); } LECIA 10. PRELUCRAREA FIIERELOR 10.1. FIIERE n general, prin fiier nelegem o colecie ordonat de elemente numite nregistrri, care sunt pstrate pe diferite suporturi de memorie extern. Suportul de memorie extern cel mai folosit este suportul magnetic (de obicei discuri sub forma de flopy i hardiscuri sau band magnetic care e din ce n ce mai rar folosit). Suportul magnetic este reutilizabil deoarece zona utilizat pentru a pstra nregistrrile unui fiier poate fi ulterior reutilizat pentru a pstra nregistrrile altui fiier. Datele introduse de la un terminal se consider c formeaz un fiier de intrare. nregistrarea n acest caz, de obicei, este format din datele tastate la terminal pe un rnd deci caracterul de rnd nou (newline) este terminator de nregistrare. n mod analog, datele care se afieaz pe terminal formeaz un fiier de ieire. nregistrarea poate fi format din caracterele unui rnd. Un fiier are o nregistrare care marcheaz sfritul de fiier. n cazul fiierelor de intrare de la tastatur sfritul de fiier se genereaz prin: CTRL/Z El poate fi pus n eviden folosind constanta simbolic EOF definit n fiierul stdio.h. Prelucrarea fiierelor implic un numr de operaii specifice acestora. Dou operaii sunt absolut necesare la prelucrarea oricrui fiier: deschiderea fiierului; nchiderea fiierului. Aceste operaii de deschidere i nchidere a unui fiier se pot realiza prin intermediul unor funcii speciale din biblioteca standard a limbajului C. Alte operaii privind prelucrarea fiierelor sunt: crearea unui fiier; consultarea unui fiier; actualizarea unui fiier; adugarea de nregistrri ntr-un fiier; poziionarea ntr-un fiier; tergerea unui fiier. Prelucrarea fiierelor face apel direct la sistemul prelucrare al fiierelor. realizeaz prin utilizarea se poate face la dou nivele. Primul nivel de operare i se numete nivelul inferior de Cel de-al doilea nivel de prelucrare se unor proceduri specializate n prelucrarea

fiierelor care, printre altele, pot rezerva i gestiona automat zone tampon necesare realizrii operaiilor de intrare/ieire, i se numete nivelul superior de prelucrare al fiierelor

10.2. NIVELUL INFERIOR DE PRELUCRARE AL FIIERELOR La acest nivel de prelucrare se folosesc 5 funcii: open (creat)- pentru deschiderea fiierelor; read - pentru citirea din fiier; write - pentru citirea din fiier; lseek - pentru poziionarea n fiier; close - pentru nchiderea fiierului. 10.2.1. Deschiderea unui fiier Orice fiier nainte de a fi prelucrat trebuie deschis. Aceast operaie se realizeaz prin intermediul funciei open al crui prototip este urmtorul: int open (const char *cale, int acces); unde: cale este un pointer spre un ir de caractere care definete calea spre fiierul care se deschide (n cea mai simpl form este numele fiierului dac se afl n directorul curent) acces este o variabil de tip ntreg care poate lua una din valorile: - O_RDONLY - fiierul se deschide numai n citire (consultare); - O_WRONLY - fiierul se deschide numai n scriere (creare); (sau O_CREAT) - O_RDWR - fiierul se deschide n citire/scriere; - O_APPEND - fiierul se deschide la sfrit pentru adugare; - O_BINARY - fiierul se prelucreaz binar; - O_TEXT - fiierul este de tip text. Unele valori din cele de mai sus se pot combina cu ajutorul operatorului |. De exemplu O_RDWR | O_BINARY pentru deschiderea fiierului n scriere/citire binar. Observaii: 1o. Funcia open ntoarce descriptorul de fiier care este o valoare intreag ce va identifica fiierul n toate celelate operaii care se vor realiza asupra lui. Dac deschiderea unui fiier nu reuete (de obicei unul din parametrii este eronat) atunci funcia open returneaz valoarea 1. 2o. Pentru a putea utiliza funcia open trebuie incluse fiierele header io.h i fcntl.h. 3o. Pentru a crea un fiier nou se va folosi funcia creat n locul funciei open cu prototipul: int creat (const char *cale, int mod); unde: - cale are aceeai semnificaie ca i la funcia open; - mod este un ntreg care poate fi definit prin constantele simbolice de mai jos: S_IREAD - se poate citi fiierul; S_IWRITE - se poate scrie n fiier; S_IEXEC - se poate executa programul coninut n fiier. Utilizarea funciei presupune includerea fiierelor io.h i stat.h 4o. Implicit fiierul se consider c este de tip text. Exemple: char nume_fisier[ ]=fis1.dat; int df; df = open (nume_fisier, O_RDONLY);

Prin apelul de mai sus se deschide n citire fiierul fis1.dat din directorul curent. int df; df = open (c:\\borlandc\\help.txt,O_APPEND); Se deschide n adugare fiierul help.txt din directorul borlandc de pe discul C. 10.2.2. Citirea dintr-un fiier (consultare) Funcia folosit pentru operaia de citire dintr-un fiier n memorie se numete read i are prototipul urmtor: int read (int df, void *buf, unsigned lung); unde: - df este descriptorul de fiier a crui valoare a fost definit la deschidere; buf este pointerul spre zona de memorie n care se recepioneaz nregistrarea care se citete; lung este lungimea n octei a inregistrrii citite. Observaii: 1o. La fiecare apel funcia returneaz nregistrarea curent. La primul apel se citete prima nregistrare din fiier, la al doilea apel se citete a doua, etc. Ordinea nregistrrilor n fiier este cea definit la crearea fiierului. 2o. La un apel al funciei read se citesc cel mult lung octei nregistrarea avnd definit lungimea la scrierea n fiier. Funcia rentoarce numrul de octei citii, 0(zero) la sfrit de fiier, sau 1 la eroare. De obicei se folosesc frecvent nregistrri de 512 octei sau chiar mai mari. 3o. Funcia read poate fi folosit pentru a citi de la intrarea standard. n acest caz, descriptorul de fiier este 0 (stdin are 0, stdout are 1, stderr are 2 stdprn are 3 stdaux are 4). Programatorul nu trebuie s deschid fiierele standard deoarece ele sunt deschise automat la lansarea n execuie a programului. 4o. Utilizarea funciei read, presupune includerea fiierului io.h.

10.2.3. Scrierea ntr-un fiier (creare, actualizare, adugare) Pentru a scrie ntr-un fiier se folosete funcia write. Se presupune c fiierul este deschis n prealabil prin funcia creat sau open. Ea este asemntoare cu funcia read, doar c realizeaz transferul invers, adic din memorie n fiier i are prototipul: int write (int df, void *buf, unsigned lung); Observaii: 1o. Funcia returneaz numrul octeilor scrii n fiier. Acesta este egal cu lung i definete lungimea nregistrrii scrise n fiier. n cazul n care numrul returnat de funcia write difer de parametrul lung scrierea a fost eronat i se rentoarce valoarea 1. 2o. Utilizarea funciei write implic includerea fiierlui io.h. 10.2.4. Poziionarea ntr-un fiier

Pentru a avea acces aleator la nregistrrile unui fiier se folosete o funcie de poziionare n fiier pe anumite nregistrri dorite. Pe fiierele care au suporturi magnetice este posibil poziionarea cu ajutorul funciei lseek care are prototipul urmtor: long lseek (int df, long deplasament, int origine) unde: df este descriptorul de fiier; deplasament definete numrul de octei peste care se va deplasa capul de scriere/citire al discului; origine are una din valorile: 0 deplasamentul se consider de la nceputul fiierului; 1 deplasamentul se consider din poziia curent a capului de scriere/citire; 2 deplasamentul se consider de la sfritul fiierului. Observaii: 1o. Funcia returneaz poziia capului de citire/scriere fa de nceputul fiierului n numr de octei sau 1L la eroare. 2o. Funcia nu realizeaz nici un transfer de informaie ci doar poziioneaz capul de citire/scriere n fiier. Deci pentru transfer e nevoie de funciile read sau write. 3o. Utilizarea funciei presupune includerea fiierului io.h. 4o. Apelul lseek (df, 0L, 0) permite o poziionare la nceput de fiier iar apelul lseek (df, 0L, 2) permite o poziionare la sfrit de fiier.

10.2.5. nchiderea unui fiier La sfritul prelucrrii unui fiier acesta trebuie nchis. Acest lucru se realizeaz automat dac programul se termin prin apelul funciei exit. Programatorul poate nchide un fiier folosind funcia close. Se recomand nchiderea unui fiier de ndat ce s-a terminat prelucrarea lui. Aceasta din cauz c numrul fiierelor ce pot fi deschise simultan este limitat. Limita este dependent de sistemul de operare i ea variaz, de obicei, n intervalul 15-25. De obicei numrul de buffere (zone tampon) asociate fiierelor se precizeaz n fiierul autoexec.bat. Menionm c fiierele standard din limbajul C nu se nchid de programator. Funcia close are prototipul urmtor: int close (int df); unde df este descriptorul fiierului care se nchide. Observaii: 1o. La o nchidere normal, funcia returneaz valoarea 0 i 1 n caz de incident. 2o. Utilizarea funciei close implic includerea fiierului io.h. Exemple Vom deschide fiierul fis1.dat n creare i vom scrie n el dou nregistrri. #include<io.h> #include<fcntl.h> void main (void) { int df,i; df = open("fis1.dat", O_CREAT);// se deschide fisierul fis1.dat in creare if (df != -1) // se testeaza daca fiserul s-a deschis corect { write (df,"cioban vasyle\n", 14);// se scriu doua inregistrari

write (df,"cioban andrei\n", 14); } else printf (nu s-a deschis fisierul); close (df); }

// se inchide fisierul

Se afieaz coninutul fiierului fis1.dat #include<io.h> #include<fcntl.h> #include <stdio.h> #include <conio.h> void main (void) {int df; char s[14]; df = open("fis1.dat", O_RDONLY); // se deschide fisierul n citire if (df != -1) // se testeaza daca deschiderea e corecta { read (df, s, 14); printf ("%.14s",s); read (df, s, 14); printf ("%.14s",s); } else printf (nu s-a deschis fisierul); close (df); getch(); // se asteapta un caracter de la tastatura } programul urmtor copiaz intrarea standard la ieierea standard folosind o zon tampon de 80 de caractere: #define LZT 80 #include <io.h> void main (void) // copiaza intrarea standard la iesirea standard { char zona_tampon[LZT]; int i; while ((i = read (0, zona_tampon, LZT)) > 0) write (1, zt, i); } 10.3. NIVELUL SUPERIOR DE PRELUCRARE A FIIERELOR Dup cum am amintit, la acest ajutorul unor proceduri specializate. Deschiderea unui fiier Funcia fopen se utilizeaz pentru deschiderea unui fiier. Ea returneaz un pointer spre tipul FILE (tipul fiier), tip definit n fiierul stdio.h. Tipul FILE este un tip structurat i el depinde de sistemul de operare. n caz de eroare, funcia fopen returneaz pointerul NULL. Prototipul funciei fopen este urmtorul: FILE *fopen (const char *cale, const char *mod); unde: cale are aceeai semnificaie ca i n cazul creat. nivel fiierele se prelucreaz cu

funciilor open

mod este un pointer spre un ir de caractere care definete modul de prelucrare al fiierului dup deschidere. Acest ir de caractere se definete n felul urmtor: - r - deschidere n citire (read); - w - deschidere n scriere (write); - a - deschidere pentru adugare; - r+ deschidere pentru modificare (citire sau scriere); - rb - citire binar; - wb - scriere binar; - r+b - citire/scriere binar.

Observaii: 1o. Dac se deschide un fiier inexistent cu modul w sau a, atunci el este deschis n creare. 2o. Dac se deschide un fiier existent cu modul w, atunci coninutul vechi al fiierului se pierde i se va crea unul nou cu acelai nume. 3o. Menionm c, stdin, stdout, stderr, stdaux i stdprn sunt pointeri spre tipul FILE i permit ca funciile de nivel superior de prelucrare a fiierelor s poat trata intrarea standard i ieirile standard pe terminal i imprimant la fel ca i fiierele pe celelalte suporturi. Singura deosebire const n aceea c aceste fiiere nu se deschid i nici nu se nchid de ctre programator. Ele sunt deschise automat la lansarea n execuie a programului i se nchid la apelul funciei exit. 4o. Apelul funciei se realizeaz prin construcia: FILE *pf; pf = fopen (FIS1.DAT,w); 10.3.2. Prelucrarea pe caractere a unui fiier Fiierele pot fi folosind dou funcii simple: putc pentru scriere; getc pentru citire. scrise i citite caracter cu caracter,

Funcia putc are prototipul: int putc (int c, FILE *pf); unde: c este codul ASCII al caracterului care se scrie n fiier; pf este pointerul spre tipul FILE a crui valoare a fost returnat de funcia fopen la deschiderea fiierului n care se scrie; pf poate fi i stdout, sdterr, stdaux, stdprn. Funcia putc returneaz valoarea lui c respectiv 1 n caz de eroare. Funcia getc are prototipul: int getc (FILE *pf); unde: pf este pointerul spre tipul FILE a crui valoare a fost returnat de funcia fopen la deschiderea fiierului; n particular pf poate fi stdin. Funcia getc returneaz codul ASCII al caracterului citit sau EOF la sfrit de fiier sau eroare. nchiderea unui fiier nchiderea unui fiier se realizeaz cu ajutorul funciei fclose care are prototipul: int fclose (FILE *pf); unde: pf este pointerul spre tipul FILE a crui valoare a fost definit la deschiderea fiierului prin intermediul funciei fopen. Funcia fclose returneaz: 0 la nchiderea normal a fiierului; 1 n caz de eroare. Exemple: Programul urmtor copiaz intrarea stdout, folosind funciile getc i putc. #include <stdio.h> void main (void) { int c; while (( c = getc (stdin)) != EOF) putc (c, stdout); }

standard

la

ieirea

standard

Programul urmtor copiaz intrarea standard la imprimant. #include <stdio.h> void main (void) { int c; while (( c = getc (stdin)) != EOF) putc (c, stdprn); } Programul urmtor scrie la ieirea stdout caracterele unui fiier a crui cale este argumentul din linia de comand. Dac nu exist un argument n linia de comand, atunci se citete de la intrarea standard. #include <stdio.h> void main (int argc, char *argv[ ] ) { FILE *pf; int c; if (argc = = 1) pf = stdin; // nu exista argument in linia de comanda else // se deschide fisierul a carui cale se afla in argv[1] if (( pf = fopen (*++argv,r)) = = NULL) { printf (nu se poate deschide fisierul %s\n,*argv); exit (1); } while (( c = getc (pf)) != EOF) putchar(c); exit (0); } Operaiile de intrare-ieire cu format Biblioteca standard a limbajului C conine funcii care permit realizarea operaiilor de intrare/ieire cu format. Astfel se pot utiliza funciile fscanf i fprintf, prima pentru citire cu format dintr-un fiier, iar a doua pentru scriere cu format ntr-un fiier. Funcia fscanf este asemntoare cu funcia scanf. Ea are un parametru n plus fa de scanf. Acest parametru este un pointer spre tipul FILE i el definete fiierul din care se face citirea. Acest pointer este primul parametru al funciei fscanf. Funcia poate fi apelat printr-o expresie de atribuire de forma: nr = fscanf (pf, control, lista_de_parametrii ); unde : pf este un pointer spre tipul FILE i valoarea lui a fost definit prin apelul funciei fopen; definete fiierul din care se face citirea; ceilali parametri sunt identici cu cei utilizai la apelul funciei scanf. Funcia fscanf, ca i funcia scanf, returneaz numrul cmpurilor citite din fiier. La ntlnirea sfritului de fiier se returneaz valoarea EOF definit n fiierul stdio.h. Pentru pf = stdin, funcia fscanf este identic cu scanf. Funcia fprintf , ca i funcia printf, returneaz numrul caracterelor scrise n fiier sau 1 n caz de eroare. Pentru pf = stdout, funcia fprintf este identic cu printf. De asemenea, se pot utiliza pointerii standard obisnuii: stderr, stdaux, stdprn. Exemplu: Vom scrie un program cu ajutorul cruia se va crea un fiier cu numele "FIS.DAT" i care conine nregistrri cu numele, prenumele i adresa unor persoane. #include <stdio.h> void main(void) {

int n=0, i=1; // n este numarul de nregistrari ce se va scrie in fisier char nume[25], prenume[30], adresa[50]; FILE *pf; printf("\n Dati numarul de inregistrari n= "); scanf("%d",&n); pf=fopen("FIS.DAT","w"); if (pf = = NULL) { printf ("Eroare la deschidere"); return; } do { printf("\n Nume : "); scanf("%s",nume); printf("\n Prenume : "); scanf("%s",prenume); printf("\n Adresa : "); scanf("%s",adresa); fprintf(pf,"%s %s %s", nume, prenume, adresa); i++; } while (i<=n); fclose(pf); } 10.3. 5. Intrri-ieiri de iruri de caractere Biblioteca standard a limbajului C conine funciile fgets i fputs care permit citirea respectiv scrierea ntr-un fiier ale crui nregistrri sunt iruri de caractere. Funcia fgets are prototipul: char *fgets (char *s, int n, FILE *pf); unde: s este pointerul spre zona n care se face citirea caracterelor; n-1 este numrul maxim de caractere care se citesc; pf este pointerul spre tipul FILE care definete fiierul din care se face citirea. De obicei s este numele unui tablou de tip char de dimensiune cel puin n. irul se termin cu \0 (caracterul NUL). La ntlnirea caracterului \n, citirea se oprete. n acest caz, n zona receptoare se transfer caracterul \n i apoi caracterul NUL (\0). n mod normal, funcia returneaz valoarea pointerului s. La ntlnirea sfritului de fiier se returneaz valoarea NULL. Funcia fputs scrie ntr-un fiier un ir de caractere care se termin prin \0. Ea are prototipul: int fputs (const char *s, FILE *pf); unde: s este pointerul spre zona care conine irul de caractere care se scrie; pf este pointerul spre zona care conine irul de caractere care se scrie. Funcia fputs returneaz codul ASCII al ultimului caracter scris sau 1 n caz de eroare. Aceste funcii sunt realizate folosind funcia getc pentru fgets i putc pentru fputs. Pentru a citi de la intrarea standard stdin, se poate folosi funcia gets, care nu mai are parametrii pf i n. Parametrul pf este implicit stdin. Funcia gets citete caracterele de la intrarea standard pn la ntlnirea caracterului \n care nu mai este pstrat n zona spre care pointeaz s. irul de caractere citit se termin i n acest caz prin \0.

n mod analog, pentru a scrie la ieirea standard stdout se poate folosi funcia puts, care nu mai are parametrul pf, acesta fiind implicit stdout. n rest, funcia puts este la fel ca i funcia fputs. 10.3.6. Poziionarea ntr-un fiier Cu ajutorul funciei fseek se poate deplasa capul de citire/scriere al discului n vederea prelucrrii nregistrrilor fiierului ntr-o ordine oarecare, diferit de cea secvenial (acces aleator). Aceast funcie este asemntoare cu funcia lseek. Ea are prototipul urmtor: int fseek (FILE *pf, long deplasament, int origine); unde: pf este pointerul spre tipul FILE care definete fiierul n care se face poziionarea capului de citire/scriere; deplasament i origine au aceeai semnificaie ca i n cazul funciei lseek. Funcia fseek returneaz valoarea zero la poziionare corect i o valoare diferit de zero n caz de eroare. Funcia ftell indic poziia capului de citire n fiier. Ea are prototipul: long ftell (FILE *pf); unde: pf este pointerul spre tipul FILE care definete fiierul n cauz. Funcia returneaz o valoare de tip long care definete poziia curent a capului de citire/scriere, i anume reprezint deplasamentul n octei a poziiei capului fa de nceputul fiierului. 10.3.7. Prelucrarea fiierelor binare Fiierele organizate ca date binare (octeii nu sunt considerai ca fiind coduri de caractere) pot fi prelucrate la acest nivel folosind funciile fread i fwrite. n acest caz se consider c nregistrarea este o colecie de date structurate numite articole. La o citire, se transfer ntr-o zon special, numit zon tampon, un numr de articole care se presupune c au o lungime fix. n mod analog, la scriere se transfer din zona tampon un numr de articole de lungime fix. Cele dou funcii au prototipurile de mai jos: unsigned fread (void *ptr, unsigned dim, unsigned nrart, FILE *pf); unde: - ptr este pointerul spre zona tampon ce conine articolele citite (nregistrarea citit); - dim este un ntreg ce reprezint lungimea unui articol; - nrart este un ntreg ce reprezint numrul articolelor care se transfer; - pf este un pointer spre tipul FILE care definete fiierul din care se face citirea. unsigned fwrite (const void *ptr, unsigned dim, unsigned nrart, FILE *pf); Parametrii funciei fwrite au aceeai semnificaie ca i parametrii funciei fread. Ambele funcii returneaz numrul articolelor transferate sau 1 n caz de eroare. Exemplu:

Programul urmtor citete de la intrarea standard stdin datele ale cror formate sunt definite mai jos i le scrie n fiierul miscari.dat din directorul curent. tip 1 denumire TELVIZOR um buc cod 0001 pret 3000000 cantitate 200

#include <stdio.h> #define MAX 60 typedef struct { char tip[2]; char den[MAX]; int val; char unit[3]; long cod; float pret; float cant; } ARTICOL;

// 6 articole in zona tampon union { ARTICOL a[6]; char zt[6*sizeof(ARTICOL)]; }buf; int cit (ARTICOL *str) // citeste datele de la intrarea standard si le scrie in { // structura spre care pointeaza str int nr,c; float x,y; while ((nr = scanf("%1s %60s %3d %2s %ld", str -> tip, str ->den, &str ->val, str -> unit,&str -> cod)) != 5|| scanf("%f %f", &x,&y) != 2) { if(nr = = EOF) return (EOF); printf ("rind eronat ; se reia\n"); // avans pina la newline while ((c = getchar ()) != '\n' && c != EOF); if (c == EOF) return (EOF); } // sfarsit while str -> pret = x; str -> cant = y; return (nr); } // sfarsit cit void main (void) // creaza fisierul miscari.dat cu datele citite de la intrarea standard { FILE *pf; ARTICOL a; int i,n; if((pf = fopen("miscari.dat", "wb")) == NULL) { printf("nu se poate deschide in creare fisierul miscari.dat\n"); exit(1); } for ( ; ; ) { // se umple zona tampon a fisierului for (i = 0 ;i<6 ; i++) { if((n=cit(&a)) == EOF) break; buf.a[i]=a; } // se scrie zona tampon daca nu este vida if(i !=0 )

if(i!=fwrite(buf.zt, sizeof(ARTICOL), i, pf)) { printf("eroare la scrierea in fisier\n"); exit(1); } if(n = = EOF) break; } fclose(pf); } 10.4. TERGEREA UNUI FIIER prototipul: Un fiier poate fi ters apelnd funcia unlink. Aceasta are

int unlink (const char *cale); unde: - cale este un pointer spre un ir de caractere identic cu cel utilizat la crearea fiierului n funcia creat sau fopen. Funcia unlink returneaz valoarea zero la o tergere reuit, respectiv -1 n caz de eroare. LECIA 11. FUNCII STANDARD Vom descrie cteva clase de funcii, numite clase de funcii standard aflate n bibliotecile mediului BORLAND C. Funciile i macrourile utilizate frecvent n majoritatea aplicaiilor se pot grupa n urmtoarele clase: funcii de prelucrare a fiierelor; funcii de alocare dinamic a memoriei; macrouri de clasificare; macrouri de transformare a simbolurilor; funcii care realizeaz conversii; funcii de prelucrare a irurilor de caractere; funcii de calcul; funcii pentru controlul proceselor; funcii de gestiune a datei i a orei; funcii de gestiune a ecranului. Funciile anterioare. de la punctele a) i b) au fost descrise n lecii

11.1. MACROURI DE CLASIFICARE n aceast clas distingem un numr de macrouri simple care au o utilizare larg n prelucrarea simbolurilor. Definiiile acestor macrouri se afl n fiierul ctype.h Unul dintre macrouri este denumit isascii i are prototipul: int isascii (int c); Macroul returneaz o valoare diferit de zero dac valoarea lui c aparine intervalului de numere ntregi [0,127] i zero n caz contrar. Acest macrou permite s se testeze dac valoarea parametrului su reprezint un cod ASCII sau nu. Celelalte macrouri au prototipul urmtor: int nume (int c); unde nume este unul din urmtoarele: isalpha dac c este codul unei litere; isalnum dac c este codul unei litere sau cifre; isdigit dac c este codul unei cifre; isgraph dac c este codul unui caracter imprimabil inclusiv spaiul; islower dac c este codul unei litere mici;

isprint dac c este codul unui caracter imprimabil inclusiv spaiu; isspace dac c reprezint spaiu, tabulator, retur de car, rnd nou, tabulator vertical, salt la nceput de pagin de imprimant. isupper dac c este codul unei litere mari; isxdigit dac c este codul unei cifre hexazecimale.

Exemplu: Programul urmtor citete un fiier literele mari cu litere mici. Cile spre cele destinaie) sunt argumente n linia de comand: argv[1] este un pointer spre argv[2] este un pointer spre i l rescrie schimbnd dou fiiere (surs i fiierul surs; fiierul destinaie.

#include <stdio.h> #include <ctype.h> void main ( int argc, char *argv[ ] ) { FILE *pf1, *pf2; int c; if (argc != 3) { printf (linia de comanda eronata\n); exit(1); } if (( pf1 = fopen (argv[1],r) ) = = NULL { printf (nu se poate deschide fisierul%s\n, argv[1]); exit(1); } if (( pf2 = fopen (argv[2],w) ) = = NULL { printf (nu se poate deschide fisierul%s\n, argv[2]); exit(1); } while (( c = getc (pf1)) != EOF) if (isascii(c) && isupper (c)) putc (c - A + a, pf2); // c este codul unei litere mari else putc(c, pf2); // c este codul unei litere mici fclose (pf1); fclose (pf2); // afisarea fisierului destinatie if (( pf2 = fopen (argv[2], r) = = NULL) { printf (nu se poate deschide in citire:%s\n, argv[2]); exit(1); } while ((c = getc(pf2)) != EOF) putchar (c); fclose (pf2); } 11.2. MACROURI DE TRANSFORMARE A SIMBOLURILOR n aceast clas distingem macrouri definite tot n fiierul ctype.h. Prototipurile acestor macrouri sunt: int toascii (int c); returneaz ultimii 7 bii ai lui c (care reprezint un cod ASCII); int tolower (int c); transform pe c din liter mare n liter mic;

liter mare.

int toupper (int c);

transform

pe

din

liter

mic

11.3. CONVERSII O dat are un format extern i un format intern. Prin conversie nelegem o transformare a unei date dintr-un format al ei n cellalt. Conversiile se pot face sub controlul unui format sau fr format. Dintre funciile care realizeaz conversii sub controlul formatelor amintim: printf; fprintf; scanf; fscanf; Aceste funcii au fost descrise n leciile anterioare. Vom da n continuare cteva funcii care realizeaz conversii fr format i care sunt utilizate mai frecvent. Aceste funcii au prototipurile n fiierul stdlib.h. Funcia atoi are prototipul: int atoi (const char *ptr); unde: ptr este un pointer spre o zon de tip caracter ce conine cifre zecimale care sunt, eventual, precedate de semnul minus; Efectul: irul de cifre spre care pointeaz ptr este convertit din ntreg zecimal n ntreg binar de tip int. Observaie: 1o. Funcia returneaz rezultatul acestei conversii. Funcia atol are prototipul: long atol (const char *ptr); unde: ptr este un pointer spre o zon de tip caracter ce conine cifre zecimale care sunt, eventual, precedate de semnul minus; Efectul: irul de cifre spre care pointeaz ptr este convertit din ntreg zecimal n ntreg binar de tip long. Observaie: 1o. Funcia returneaz rezultatul acestei conversii. Funcia atof are prototipul: double atof (const char *ptr); unde: ptr este un pointer spre o zon de tip caracter ce conine cifre zecimale care sunt, eventual, precedate de semnul minus (poate conine marca zecimal); Efectul: irul de cifre spre care pointeaz ptr este convertit n virgul flotant dubl precizie. Observaie: 1o. Funcia returneaz rezultatul acestei conversii. Funcia itoa are prototipul: char *itoa (int val, char *sir, int baza) Efectul:

valoarea parametrului val se convertete din ntreg binar de tip int n baza de numeraie definit de parametrul baza i se pstreaz n zona spre care pointeaz sir. Observaie: 1o. Funcia returneaz pointerul sir. Funcia ltoa are prototipul: char *ltoa (long val, char *sir, int baza) Efectul: valoarea parametrului val se convertete din ntreg binar de tip long n baza de numeraie definit de parametrul baza i se pstreaz n zona spre care pointeaz sir. Observaie: 1o. Funcia returneaz pointerul sir. 11.4. FUNCII DE PRELUCRARE A IRURILOR DE CARACTERE Funciile din aceast clas implic includerea fiierului string.h. Indicm mai jos funciile din aceast clas, utilizate mai frecvent. O parte din aceste funcii au mai fost utilizate n diferite exemple din leciile anterioare. Funcii de copiere: char *strcpy (char *dest, const char *sursa); char *strncpy (char *dest, const char *sursa, unsigned n); prima funcie copiaz irul de caractere spre care pointeaz sursa n zona spre care pointeaz dest; a doua funcie realizeaz acelai lucru, dar copiaz cel mult primii n octei din surs; ambele funcii returneaz valoarea pointerului dest. Funcii de concatenare: int strcmp (const char *dest, const char *sursa); char *strncat (const char *dest, const char *sursa, unsigned n); prima funcie copiaz irul spre care pointeaz sursa la sfritul irului din zona spre care pointeaz dest; a doua funcie realizeaz acelai lucru, dar se copiaz cel mult primii n octei din zona spre care pointeaz sursa; ambele funcii returneaz valoarea pointerului dest. Funcii de comparare: int strcmp (const char *sir1, const char *sir2); int stricmp (const char *sir1, const char *sir2); int strncmp (const char *sir1, const char *sir2, unsigned n); int strnicmp (const char *sir1, const char *sir2, unsigned n); aceste funcii compar irurile de caractere din zonele spre care pointeaz pointerii sir1 i sir2; ele returneaz o valoare: negativ, dac irul spre care pointeaz sir1 este mai mic dect cel spre care pointeaz sir2; zero, dac cele dou iruri sunt egale; pozitiv, dac irul spre care pointeaz sir1, este mai mare dect cel spre care pointeaz sir2; prezena literei i (ignore) n numele funciei nseamn c nu se face distincie ntre literele mari i mici;

prezena literei n n numele comparaia pe cel mult n octei.

funciei

nseamn

se

realizeaz

Observaie: 1o. Fie irurile s1 i s2 de lungime l1 i l2. Atunci cele dou iruri sunt egale dac: l1=l2 ( au aceeai lungime); s1[k] = s2 [k] pentru k=0,1,...,l1 2o. irul s1 este mai mic dect irul s2 dac exist un j, j 0 i j min (l1, l2), astfel nct: s1[j] < s2[j]; s1[k] = s2[k], pentru k=0,1, . . . , j-1. 3o. irul s1 este mai mare dect irul s2 dac exist un j, j 0 i j (l1, l2), astfel inct: s1[j] > s2[j]; s1[k] = s2[k], pentru k=0,1, . . . , j-1. Funcia lungime: unsigned strlen (const char *sir); returneaz lungimea irului de caractere spre care pointeaz sir; caracterul NUL care termin irul nu este numrat. 11.5. FUNCII DE CALCUL Majoritatea funciilor matematice au prototipurile n fiierul math.h. Multe dintre acestea se utilizeaz la calculul valorilor funciilor elementare i au prototipul: double nume (double x); unde nume este unul din urmtoarele: acos -arccos; asin -arcsin; atan -arctg; cos -cos; sin -sin; exp -ex; log -ln; log10 -lg; sqrt -rdcina ptrat; ceil -returneaz cel mai mare ntreg mai mare sau egal cu x (partea ntreag); floor -returneaz cel mai mare ntreg mai mic sau egal cu x; fabs -valoarea absolut; sinh -sinus hiperbolic; cosh -cosinus hiperbolic; tanh -tangent hiperbolic; Alte funcii: double atan2 (double y, double x); double pow (double x, double y); double cabs (struct complex z); - returneaz arctg(y/x); - returneaz xy; - returneaz modulul

numrului complex; double poly (double x, int n, double c[ ] ) returneaz valoarea polinomului de grad n n punctul x, coeficienii sunt c[0], . . . c[n].

math.h: n;

Funciile care urmeaz au prototipul n fiierele stdlib.h i int abs (int n); - returneaz valoarea absolut din ntregul returneaz valoarea absolut din

long labs (long n); ntregul long n;

11.6. FUNCII PENTRU CONTROLUL PROCESELOR Aceste funcii au prototipurile n fiierul stdlib.h i n process.h i realizeaz controale asupra programelor: void abort (void); - termin un program n caz de eroare; void exit (int stare) termin un program i returneaz o stare; stare este 0 pentru terminare normal i diferit de zero pentru o terminare anormal; videaz buferele fisierelor, nchide toate fiierele; int system (const definit prin sirul de caractere spre care returneaz 0 la succes Aceste funcii au char *comanda) execut o comand DOS

pointeaz comanda; si 1 la eroare. prototipurile n stdlib.h i n process.h.

11.7. FUNCII PENTRU GESTIUNEA DATEI I OREI Dm mai jos prototipurile a patru funcii pentru citirea/setarea datei i orei. Ele implic includerea fiierului dos.h. void getdate(struct date *d); - ncarc structura de tip date spre care pointeaz d cu datele corespunztoare furnizate de sistemul de operare; void gettime(struct time *t); - ncarc structura de tip time spre care pointeaz t cu datele corespunztoare furnizate de sistemul de operare; void setdate (struct date *t); seteaz data curent n conformitate cu datele de tip date; void settime (struct time *t); seteaz ora curent n conformitate cu datele de tip time spre care pointeaz t. Tipurile date i time sunt definite n fiierul dos.h. astfel: struct date { int da_year; int da_day; int da_mon; }; struct time { unsigned unsigned unsigned unsigned }; char char char char ti_min; ti_hour; ti_hund; ti_sec;

Exemplu: void main (void) // afiseaza data si ora { struct date d; struct time t; getdate (&d); gettime (&t); printf (\n\t%02d/%02d/%04d,d.da_day, d.da_mon, d.da_year); printf (\tora %02d:%02:%02\n, t.ti_hour, t.ti_min, t.ti_sec); } 11.8. ALTE FUNCII DIVERSE DE UZ GENERAL void clrscr (void); prototipul n conio.h void delay(unsigned i); perioad de i milisecunde; void sleep(unsigned i); perioad de i secunde; void nosound (void); void sound(unsigned h); egal cu h Hz. - terge fereastra activ sau tot ecranul; suspend suspend execuia execuia programului programului pentru pentru o o

- oprete difuzorul calculatorului; - pornete difuzorul calculatorului cu un ton

LECIA 12. GESTIUNEA ECRANULUI N MOD TEXT Biblioteca standard a limbajelor C i C++ conine pentru gestiunea ecranului. Acesta poate fi gestionat n 2 moduri: modul text i modul grafic. funcii

Modul text presupune c ecranul este format dintr-un numr de linii i coloane. De obicei exist dou variante: 25 de linii x 80 de coloane = 2000 de carctere sau 25 de linii x 40 de coloane = 1000 de caractere. Poziia pe ecran a unui caracter se definete printr-un sistem de coordonate ntregi (x,y) unde: x - reprezint numrul coloanei n care este situat caracterul; y - reprezint numrul liniei n care este situat caracterul. Colul din stnga sus are coordonatele (1,1) iar colul din dreapta jos (80,25) sau (40,25). n mod implicit funciile de gestiune a ecranului n mod text au acces la tot ecranul. Accesul poate fi limitat la o parte din ecran utiliznd aa numitele ferestre. Fereastra este un dreptunghi care este o parte a ecranului i care poate fi gestionat independent de restul ecranului. Un caracter de pe ecran, pe lng coordonate, mai are i urmtoarele atribute: culoarea caracterului afiat; culoarea fondului; clipirea caracterului. Aceste atribute sunt dependente de adaptorul grafic utilizat. Cele mai utilizate adaptoare sunt: placa MDA, care este un adaptor monocrom; placa HERCULES, care este un adaptor color;

placa CGA, care este un adaptor color; placa EGA, care este un adaptor color; placa VGA, care este un adaptor color de mare performan. Pentru adaptoarele de mai sus se pot utiliza 8 culori de fond i 16 culori pentru afiarea caracterelor. Atributul unui caracter se definete cu ajutorul formulei: atribut = 16 * culoare_fond + culoare_caracter + clipire (*)

unde: culoare_fond (background) = cifr ntre 0 i 7; (8 culori) culoare_caracter (foreground) = ntreg ntre 0 i 15; (16 culori) clipire = 128 (clipire) sau 0 (fr clipire) Tabel cu numele culorilor: Culoare negru albastru verde turcoaz rou purpuriu maro gri deschis gri nchis albastru deschis verde deschis turcoaz deschis rou dechis purpuriu magenta galben alb clipire Constant simbolic BLACK 0 BLUE GREEN 2 CYAN 3 RED MAGENTA BROWN 6 LIGHTGRAY 7 DARKGRAY 8 LIGHTBLUE 9 LIGHTGREEN LIGHTCYAN 11 LIGHTRED LIGHTMAGENTA YELLOW WHITE 15 BLINK Valoare 1 4 5

10 12 13 14 128

12.1. SETAREA ECRANULUI N MOD TEXT Setarea (punerea) se realizeaz cu ajutorul funciei textmode care are prototipul: void textmode (int modtext); unde modtext poate fi exprimat numeric sau simbolic n felul urmtor: Modul text activat alb/negru 40 coloane color 40 coloane alb/negru 80 coloane color 80 coloane monocrom color 43 linii pentru EGA i 50 linii pentru VGA modul precedent Constant simbolic BW40 C40 BW80 C80 MONO C4350 LASTMODE 0 1 2 3 7 64 -1 Valoare

Modul MONO se poate seta pe un adaptor monocolor. Celelalte moduri se pot seta pe adaptoare color. 12.2. DEFINIREA UNEI FERESTRE

Dac dorim s partajm ecranul n zone care s poat fi gestionate independent trebuie s definim ferestre. O fereastr este o zon dreptunghilar de pe ecran care se poate defini cu funcia window al crei prototip este: void window (int stanga, int sus, int dreapta, int jos); unde: -(stanga, sus) = coordonatele colului din stnga al ferestrei; -(dreapta, jos) = coordonatele colului din dreapta jos. La un moment dat o singur fereastr este activ i anume aceea definit de ultimul apel al funciei window. Funciile de gestionare a ecranului n mod text acioneaz ntotdeauna asupra ferestrei active. Dup setarea modului text cu ajutorul funciei textmode este activ tot ecranul. Menionm c funcia window nu are nici un efect dac parametrii de la apel sunt eronai. 12.3. TERGEREA UNEI FERESTRE prototipul: Fereastra activ se poate terge cu ajutorul funciei clrscr cu

void clrscr(void); fereastra activ (sau tot ecranul) devine activ; fondul are culoarea definit prin culoarea de fond curent; clrscr poziioneaz cursorul n poziia (1,1) a fiecrei ferestre. 12.4. GESTIUNEA CURSORULUI Programatorul poate plasa cursorul pe un caracter al ferestrei folosind funcia gotoxy al crei prototip este: void gotoxy (int coloana, int linie); unde: (coloana, linie) sunt coordonate relative la fereastra activ, dac sunt nafara ferestrei active apelul este ignorat; Poziia cursorului se poate determina cu ajutorul funciilor wherex i wherey care au prototipurile: int wherex (void); -returneaz numrul coloanei n care se afl cursorul int wherey (void); -returneaz numrul liniei n care se afl cursorul. Exist cazuri cnd se dorete ascunderea cursorului. Acest lucru se poate realiza printr-o secven special n care se utilizeaz funcia geninterrupt. void ascundcursor (void) // face invizibil cursorul { _AH = 1; _CH = 0x20; geninterrupt (0x10); } Cursorul poate fi reafiat apelnd funcia de mai jos: void afiscursor (void) // face vizibil cursorul { _AH = 1; _CH = 6; _CL = 7; geinterrupt (0x10); } AH, CH, CL reprezint unii din regitrii microprocesorului.

12.5. DETERMINAREA PARAMETRILOR ECRANULUI Utilizatorul are posibilitatea s obin parametrii cureni ai ecranului prin apelarea funciei gettextinfo al crui prototip este: void gettextinfo (struct text_info *p); unde structura text_info este definit n fiierul conio.h astfel: struct text_info { unsigned char winleft; // amplasarea colturilor ferestrei unsigned char wintop; unsigned char winright; unsigned char winbottom; unsigned char attribute; // culoarea fondului, a caracterelor si unsigned char normattr; // clipirea unsigned char currmode; unsigned char screenheight; // dimensiunea ecranului unsigned char screenwidth; unsigned char curx; // pozitia cursorului unsigned char cury; }; 12.6. MODURILE VIDEO ALB/NEGRU Exist dou moduri (intens i normal) care se pot activa cu funciile highvideo i lowvideo ale cror prototipuri sunt: void highvideo (void); void lowvideo (void); Intensitatea iniial este de obicei cea normal. Se poate reveni la intensitatea normal dac se apeleaz funcia normvideo cu acelai prototip ca al celorlalte dou funcii. Exemplu: Vom scrie un program care afieaz parametrii ecranului: #include <stdio.h> #include <conio.h> void main (void) { struct text_info par_ecran; gettextinfo (&par_ecran); printf (\nstanga: %u sus: %u dreapta: %u jos: %u\n, par_ecran.winleft, par_ecran.wintop, par_ecran.winright, par_ecran.winbottom); printf (atribut: %d mod curent: %d\n, par_ecran.normattr, par_ecran.currmode); printf (inaltimea ecranului: %d latimea ecrnului: %d\n, par_ecran.screenheight, par_ecran.screenwidth); printf (coloana cursorului: %d linia cursorului: %d\n, par_ecran.curx, par_ecran.cury); } 12.7. SETAREA CULORILOR Culoarea fondului se seteaz textbackground cu prototipul urmtor: void textbackground (int culoare); cu ajutorul funciei

unde: culoare este un ntreg cuprins ntre 0 i 7 cu semnificaia din tabelul anterior al culorilor. Culoarea caracterelor se seteaz cu ajutorul funciei textcolor cu prototipul urmtor: void textattr (int culoare); unde: culoare este un ntreg ntre 0 i 15. Se pot seta ambele culori, precum i clipirea caracterului folosind funcia textattr de prototip: void textattr (int atribut); unde: atribut e definit cu ajutorul relaiei (*) de la nceputul leciei.

12.8. GESTIUNEA TEXTELOR Pentru afiarea caracterelor colorate n conformitate cu atributele definite prin relaia: atribut = 16 * culoare_fond + culoare_caracter + clipire se pot folosi funciile: putch - pentru afiarea unui caracter; cputs - pentru afiarea color a unui ir de caractere (acelai prototip ca puts); cprintf - pentru afiarea color sub controlul formatelor. Alte prototipuri de funcii: void insline (void); - insereaz o linie cu spaii n fereastr, liniile de sub poziia cursorului se deplaseaz n jos cu o poziie; void clreol (void) - terge sfritul liniei ncepnd cu poziia cursorului; void delline (void) - terge toat linia pe care este poziionat cursorul; int movetext ( int stanga, int sus, int dreapta, int jos, int stanga_dest, int dreapta_dest ); - copiaz un text dintr-o poziie n alta; returneaz: 1 dac textul s-a copiat cu succes i o n caz de eroare. Textele dintr-o zon dreptunghiular a ecranului pot fi salvate sau citite dintr-o zon de memorie cu ajutorul funciilor puttext i gettext i au prototipurile: int gettext (int stanga, int sus, int dreapta, int jos, void *destinatie); unde primii patru parametrii definesc fereastra unde se afl textul de salvat; destinatie este pointerul spre zona de memorie n care se salveaz textul. i int puttext (int stanga, int sus, int dreapta, int jos, void *sursa); unde primii patru parametrii definesc fereastra unde se va scrie pe ecran textul preluat din memorie; sursa este pointerul spre zona de memorie din care se transfer textul. Ele returneaz: 1 la copiere cu succes; 0 la eroare.

Observaie: 1o. Fiecare caracter de pe ecran se pstreaz pe doi octei: pe un octet codul caracterului; pe octetul urmtor atributul caracterului. Exemple: Programul urmtor seteaz o fereastr i modurile video alb/negru. #include <conio.h> void main (void) { textmode (BW80); window (10,4,60,4); clrscr (); lowvideo (); cputs(lowvideo); normvideo (); cputs (normvideo); textmode (LASTMODE); cprintf (\n\r Ac iona i o tast pentru a continua:); getch (); } Programul urmtor afieaz toate combinaiile pentru fond i caractere (adaptor EGA/VGA). de culori posibile

#include <conio.h> #include <stdio.h> void main (void) { static char *tculoare [ ] = { 0 BLACK negru, 1 BLUE albastru, 2 GREEN verde, 3 CYAN turcoaz, 4 RED rosu, 5 MAGENTA purpuriu, 6 BROWN maro, 7 LIGHTGRAY gri deschis, 8 DARKGRAY gri inchis, 9 LIGHTBLUE albastru deschis, 10 LIGHTGREEN verde deschis, 11 LIGHTCYAN turcoaz deschis, 12 LIGHTRED rosu dechis, 13 LIGHTMAGENTA purpuriu magenta, 14 YELLOW galben , 15 WHITE alb}; int i,j,k; struct text_info atribut; gettextinfo (&atribut); for (i = 0; i < 8; i++ ) // i alege culoarea fondului { window (3,2,60,20); k=2; textbackground (i); clrscr(); for (j=0; j <10; j++, k++) // j alege culoarea caracterului { textcolor (j); gotoxy (2,k); if (i = = j) continue; cputs (tculoare[j]); } gotoxy (1,18); printf (actionati o tasta pentru contiuare\n); getch(); } window (atribut.winleft, atribut.wintop, atribut.winright, atribut.winbottom);

textattr (atribut. attribute); clrscr(); } LECIA 13. GESTIUNEA ECRANULUI N MOD GRAFIC Modul grafic presupune c ecranul este format din puncte luminoase (pixeli). Numrul acestora depinde de adaptorul grafic i se numete rezoluie. O rezoluie este cu att mai bun cu ct este mai mare. Adaptorul CGA are o rezoluie de 200 rnduri x 640 de coloane iar EGA de 350 de rnduri x 640 de coloane. Adaptorul VGA i SVGA ofer rezoluii n funcie de mrimea ecranului. Astfel adaptorul VGA i SVGA ofer rezoluii de pn la 768 de rnduri x 1024 de coloane pentru ecranul de 14 (inches). Pentru gestiunea ecranului n mod grafic se pot utiliza peste 60 de funcii standard aflate n biblioteca sistemului. Aceste funcii au prototiopurile n fiierul graphics.h. Totodat ele folosesc pointeri de tip far (de 32 de bii). 13.1. SETAREA MODULUI GRAFIC Modul grafic se seteaz cu ajutorul funciei initgraph. Aceast funcie poate fi folosit singur sau mpreun cu o alt funcie numit detectgraph care determin parametrii adaptorului grafic. Prototipul ei este: void far detectgraph (int far *graphdriver, int far *graphmode); unde: n zona spre care pointeaz graphdriver se pstreaz una valorile: valoare CGA MCGA EGA EGA64 EGAMONO IBM8514 HERCMONO ATT400 VGA PC3270 n zona spre care pointeaz graphmode se memoreaz una din valorile: pentru CGA valoare simbol simbol

din

CGAC0 CGAC1 CGAC2 CGAC3 corespunznd toate pentru o rezoluie de 320*200 de pixeli i permit 4 culori CGAHI are o rezoluie de 640*200 puncte i lucreaz numai alb/negru. pentru EGA valoare simbol

EGALO are 640*200 i 16 culori EGAHI are 640*350

pentru VGA valoare 0 1 2

simbol VGALO are 640*200 VGAMED are 640*350 VGAHI are 640*480

Valorile spre care pointez graphdriver definesc nite funcii standard corespunztoare adaptorului grafic. Aceste funcii se numesc drivere. Ele se afl n subdirectorul BGI. Funcia detectgraph detecteaz adaptorul grafic prezent la calculator i pstreaz valoarea corespunztoare acestuia n zona spre care pointeaz graphdriver. Modul grafic se definete n aa fel nct el s fie cel mai performant pentru adaptorul grafic curent. Cele mai utilizate adaptoare sunt de tip EGA (calculatoare mai vechi) i VGA i SVGA (calculatoarele mai noi). Apelul funciei detectgraph trebuie s fie urmat de apelul funciei initgraph. Aceasta seteaz modul grafic n conformitate cu parametrii stabilii de apelul prealabil al funciei detectgraph. Funcia initgraph are prototipul: void far initgraph (int far * graphdriver, int far *graphmode, char far *cale); unde: graphdriver i graphmode sunt pointeri cu aceeai semnificaie ca n cazul funciei detectgraph; cale este un pointer spre irul de caractere care definesc calea subdirectorului BGI (Borland Graphic Interface), de exemplu: C:\\BOLANDC\\BGI Exemplu: Setarea n mod implicit a modului grafic: int driver, mod_grafic; ... detectgraph (&driver, &mod_grafic); initgraph(&driver, &mod_grafic, C:\\BORLANDC\\BGI); Dup apelul funciei initgraph se pot utiliza celelalte funcii standard de gestiune grafic a ecranului. Din modul grafic se poate iei apelnd funcia closegraph care are prototipul: void closegraph (void); Funcia initgraph mai poate fi apelat i folosind constanta simbolic DETECT astfel: int driver, mod_grafic; ... driver = DETECT; initgraph (&driver, &mod_grafic, C:\\BORLANDC\\BGI); Constanta simbolic DETECT este definit n fiierul graphics.h, alturi de celelalte constante simbolice care definesc driverul. Aceasta are valoarea zero. Prin apelul de mai sus, funcia initgraph apeleaz funcia detectgraph pentru a defini parametrii implicii ai adaptorului grafic. Utilizatorul poate defini el nsui parametri pentru iniializarea modului grafic. 13.2. GESTIUNEA CULORILOR

Adaptoarele grafice sunt prevzute cu o zon de memorie n care se pstreaz date specifice gestiunii ecranului. Aceast zon de memorie poart denumirea de memorie video. n mod grafic, ecranul se consider format, dup cum am precizat, din puncte luminoase numite pixeli. Poziia pe ecran a unui pixel se definete printr-un sistem de coordonate (x,y) cu x coloana i y linia n care este afiat pixelul. n cazul adaptoarelor color, unui pixel i corespunde o culoare. Culoarea pixelilor se pstreaz pe bii n memoria video. Memoria video necesar pentru a pstra starea ecranului setat n mod grafic, se numete pagin video. Adaptoarele pot conine mai multe pagini video. Gestiunea culorilor este dependent de tipul de adaptor grafic existent la microprocesor. Numrul maxim de culori pentru adaptoarele EGA este de 64 (numerotate de la 0 la 63). Cele 64 de culori nu pot fi afiate simultan pe ecran. n cazul adaptorului EGA se pot afia simultan 16 culori. Mulimea culorilor care pot fi afiate simultan pe ecran se numete palet. Culorile din componena unei palete pot fi modificate de utilizator prin intermediul funciilor standard. La iniializarea modului grafic se seteaz o palet implicit. Pentru adaptorul EGA exist un tablou de 64 de culori (cu coduri ntre 0 i 63) din care se selecteaz cele 16 culori pentru palet. Exist i constante simbolice foarte sugestive cu numele culorilor n englez. Funciile de gestiune a culorilor pot avea ca parametri nu numai codurille culorilor ci i aceste constante simbolice. Culoarea fondului este ntotdeauna cea corespunztoare indicelui 0 din palet. Culoarea pentru desenare este cea corespunztoare indicelui 15. Culoarea de fond poate fi modificat cu ajutorul funciei setbkcolor care are prototipul: void far setbkcolor (int culoare); unde: culoare = index n tabloul care definete paleta. Exemplu: setbkcolor (BLUE); seteaz culoarea de fond pe albastru. de fond curent se poate apela

Pentru a cunoate culoarea funcia getbkcolor de prototip: int far getbkcolor (void);

Ea returneaz indexul n tabloul care define te paleta pentru culoarea de fond. Culoarea pentru desenare poate fi modificat folosind funcia setcolor de prototip: void far setcolor(int culoare); unde: culoare = index n tabloul care definete paleta. Exemplu: galben. setcolor (YELLOW); seteaz culoarea pentru desenare n

Culoarea pentru desenare se poate determina apelnd funcia getcolor de prototip: int far getcolor (void); Ea returneaz indexul n tabloul care definete paleta relativ la culoarea pentru desenare. Paleta curent poate fi modificat cu funciile setpalette i setallpalette. Prima funcie se folosete pentru a modifica o culoare din paleta curent i are prototipul: void far setpalette (int index, int cod);

unde: index este un ntreg din {0,. . . , tabloul care definete paleta pentru culoarea cod este un ntreg din intervalul {0, codul culorii care o nlocuiete n palet pe Exemplu: setpalette (DARKGRAY, 45); corespunztoare indicelui DARKGRAY (adic 8) prin culoarea de cod 45.

15} i reprezint indexul n care se modic; 1,. . . , 63} i reprezint cea veche. modific culoarea

Funcia setallpalette permite modificarea multor culori din compunerea paletei i are prototipul:

simultan

mai

void far setallpalette (struct palettetype far *paleta); unde: palettetype este un tip definit n fiierul graphics.h astfel struct palettetype { unsigned char size; unsigned char colors [MAXCOLOR+1]; }; cu - size dimensiunea paletei colors tablou ale crui elemente au ca valori codurile componente ale paletei care se definete. Modificarea paletei curente cu ajutorul funciilor setpalette i setallpalette conduce la schimbarea corespunztoare a culorilor afiate pe ecran n momentul apelului funciilor respective. Pentru a determina codurile culorilor componente ale paletei curente se va folosi funcia getpalette de prototip: void far getpalette (struct palettetype far *paleta); Paleta implicit poate fi determinat getdefaultpalette de prototip: struct palettetype *far getdefaultpalette(void); Numrul culorilor dintr-o funcia getmaxcolor de prototip: int far getmaxcolor (void); palet poate fi folosind funcia

obinut

apelnd

Funcia returneaz numrul maxim de culori diminuat cu 1. Deci n cazul adaptorului EGA funcia returneaz valoarea 15. O alt funcie care determin dimensiunea paletei este funcia getpalettesize cu prototipul: int far getpalettesize (void); Funcia returneaz numrul culorilor componente ale paletei. n cazul adaptorului EGA funcia returneaz valoarea 16. Exemplu: Programul implicit: urmtor afieaz codurile culorilor pentru paleta

#include <graphics.h> #include <stdlib.h> #include <stdio.h> #include <conio.h> void main (void) { int gd = DETECT, gm, i; struct palettetype far *pal = (void *) 0; initgraph (&gd, &gm, C:\\BORLANDC\\BGI);

pal = getdefaultpalette (); for (i=0; i<16; i++) { printf (colors[%d]=%d\n, i, pal -> colors[i]); getch (); } closegraph(); } 13.3. STAREA ECRANULUI n mod grafic ecranul se compune din n*m pixeli. Pixelul din stnga-sus are coordonatele (0,0), iar pixelul din dreapta-jos are coordonatele (n-1,m-1). n BGI exist mai multe funcii care permit utilizatorului s obin informaii despre: coordonata maxim pe orizontal; coordonata maxim pe vertical; poziia curent (pixelul curent); etc. int far getmaxx (void); returneaz abscisa maxim; int far getmaxy (void); returneaz ordonata maxim; int far getx (void); returneaz poziia (abscisa) pixelului curent; int far gety (void); returneaz poziia (ordonata) pixelului curent.

pe pe

orizontal vertical

Exemplu: #include <graphics.h> #include <stdlib.h> #include <stdio.h> #include <conio.h> void main (void) { int gd = DETECT, gm, cul_fond, cul_desen, curent_x,curent_y, maxx, maxy; initgraph (&gd, &gm, C:\\BORLANDC\\BGI); cul_fond = getbkcolor(); cul_desen = getcolor(); maxx = getmaxx(); maxy = getmaxy(); curent_x = getx(); curent_y = gety(); closegraph(); printf (culoarea fondului = %d\n, cul_fond); printf (culoare desenare = %d\n, cul_desen); printf (abscisa maxima = %d\n, maxx); printf (ordonata maxima = %d\n, maxy); printf (abscisa curenta = %d\n, curent_x); printf (ordonata curenta = %d\n, curent_y); printf (ac iona i o tasta pentru terminare); getch(); } LECIA 14. PROBLEME DIVERSE De multe ori suntem pui n faa unor probleme pe care le nelegem uor dar nu tim cum s le rezolvm ct mai simplu i elegant. V propunem cteva metode care bine nsuite pot duce, uneori, la o rapid rezolvare a problemelor dificile. Evident c, nu toate problemele pot fi ncadrate n aceste tipare propuse dar fiecare programator poate s-i formeze un astfel de "portofoliu" de metode cu care s poate aborda orice problem. Metodele prezentate n continuare pot constitui un nceput.

14.1. GENERAREA COMBINRILOR Fie o mulime oarecare de n elemente care poate fi pus ntr-o coresponden biunivoc cu mulimea A={1,...,n}. Ne propunem s determinm toate m-combinrile (m n) ale mulimii A (submulimi de m elemente ale mulimii A). Vom rezolva problema, nerecursiv, pornind de la m-combinarea V=(1,2,...,m) determinnd succesiv toate m-combinrile ordonate lexicografic cresctor. Fie V=(v1,v2,...,vm) o m-combinare oarecare, atunci m-combinarea urmtoare n ordine lexicografic cresctoare se obine astfel: se determin cel mai mare indice i satisfcnd relaiile: vi<n-m+i, vi+1=n-m+i+1,..., vm-1=n-1, vm=n. (1) se trece la vectorul urmtor: (v1,...,vi-1,vi+1,vi+2,...,vi+n-i+1); dac nu exist un astfel de indice i (care s satisfac relaiile (1)) nseamn c vectorul V conine ultima m-combinare i anume: (n-m+1,nm+2, ...,n). Dm n continuare o funcie C care genereaz o m-combinare cu n elemente avnd ca parametru cod o variabil boolean care pentru valoarea 0 genereaz prima m-combinare iar pentru valoarea 1 genereaz urmtoarea mcombinare. Funcia comb rentoarce valoarea 1 dac s-a generat o m-combinare oarecare i valoarea 0 dac s-a generat ultima m-combinare (n acest caz cod are valoarea 0). Se va folosi un vector global v n care se genereaz mcombinrile. #define dim 50 #include <stdio.h> int v[dim+1], n, m; // functia generatoare a m-combinarilor int comb(cod) int cod; { int i,j;

// generarea primei m-combinari 1,...,m if (cod == 0) { for (i=1; i<=m; v[i]=i++); return (1); } i=m+1; // cautarea indicelui i pentru satisfacerea rela iilor (1) do { i-- } while (v[i] >= n-m+i); if (i) { v[i]=v[i]+1; for (j=i+1; j<=m; v[j]=v[j-1]+1,j++); return (1); } else return (0); } void main(void) { int kod,i;

printf("\ndati n: "); scanf ("%d",&n); printf("\ndati m: "); scanf ("%d",&m); comb(0); kod=1; while (kod) { printf("\n"); for (i=1;i<=m;printf ("%3d",v[i++])); kod = comb(kod); } getche(); } 14.2. METODA GREEDY Se aplic problemelor n care se d o mulime A coninnd n date (de orice natur i structur) de intrare cerndu-se s se determine o submulime B (BA) care s ndeplineasc anumite condiii pentru a fi acceptat. Cum, n general, exist mai multe astfel de submulimi (numite soluii posibile) se mai d i un criteriu conform cruia dintre aceste submulimi s alegem una singur (numit soluie optim) ca rezultat final. Foarte multe probleme de cutare se nscriu n acest tip. Menionm c, n general, soluiile posibile au urmtoarele proprieti: - se presupune c este soluie posibil; - dac B este soluie posibili CB atunci i C este soluie posibil; Vom da n continuare o variant a tehnicii greedy (denumire care n traducere nseamnn lcomie, nghiire) n care se pornete de la mulimea vid. Apoi se alege pe rnd, ntr-un anumit fel, un element din A neales nc. Dac adugarea lui la soluia parial anterior construit conduce la o soluie posibil, atunci se adaug, altfel se alege un nou element. Tot acest procedeu se repet pentru toate elementele din A. Dm n continuare n pseudocod o procedur: procedure GREEDY (n,A,B) B:=; for i=1,n do call ALEGE (A,i,x); call POSIBIL (B,x,cod); if cod=1 then call ADAUG (B,x); endif; endfor; return; end procedure Despre procedurile apelate din GREEDY precizm urmtoarele: procedura ALEGE furnizeaz n x un element din A aj{ai,...,an} i interschimb ai cu aj; dac la fiecare pas se cerceteaz ai atunci procedura se simplific; procedura POSIBIL verific dac elementul x poate fi adugat sau nu mulimii pariale construit pn la pasul curent furniznd o valoare boolean cod cu semnificaia: cod = 1, dac B U {x}, este soluie posibil cod = 0, altfel procedura ADAUG nlocuiete pe B cu B{x}. Obs. Procedurile de mai sus nu sunt necesare ntotdeauna, acest fapt depinznd de complexitatea problemei. Oricum trebuie reinut cadrul de rezolvare al acestui tip de problem.

Problem rezolvat Se d o mulime de valori reale X={x1, submulimea YX astfel ca y /yY, s fie maxim. . . .,xn}. Se cere

Evident c problema este foarte simpl (n Y trebuie introduse elementele strict pozitive din X; evitm s mai construim procedurile ALEGE, POSIBIL, ADAUG) i vom da rezolvarea ei n pseudocod: procedure SUMA (n,X,Y,k) integer n,X(n),Y(n),k k:=0; for i=1,n do if x(i) > 0 then k:=k+1; y(k):=x(i) endif; endfor; return; end procedure Probleme propuse 14.2.1. Se dau n iruri S1,S2,...,Sn ordonate cresctor, de lungimi L1,L2, ...,Ln. S se obin un ir S de lungime L 1+L2+...+Ln cu toate elementele celor n iruri ordonate cresctor (problem de interclasare). Indicaie: Vom interclasa succesiv cte dou iruri n final obinnd irul ordonat cresctor. Complexitatea interclasrii a dou iruri A i B de lungimi a i b depinde direct proporional de a+b (pentru c se fac a+b deplasri de elemente). Se pune problema gsirii ordinii optime n care trebuie efectuate aceste interclasri astfel ca numrul total de deplasri s fie minim. Vom da un exemplu pentru a lmuri mai bine lucrurile: fie 3 iruri de lungimi (90,40,10). Interclasm irul S1 cu S2 apoi irul rezultat cu S3; putem nota acest lucru formal prin (S1+S2)+S3. Se obin (90+40) + (130+10)=270 deplasri. Dac vom interclasa irurile n ordinea (S2+S3)+S1 vom obine (40+10)+ (50+90)=190 de deplasri. De aici concluzia c ntotdeauna vom interclasa irurile de lungimi minime din irurile rmase. 14.2.2. Gsii tripletele de numere pitagorice din Nn x Nn x Nn (prin Nn notat mulimea {0,1,2,...,n}). ncercai optimizarea timpului de execuie a programului. 14.2.3. Fie mulimea m-combinrilor luate din n elemente i fie k<m un numr natural. S se dea un algoritm i apoi s se scrie un program C astfel nct s se determine o submulime de m-combinri cu proprietatea c oricare dou m-combinri au cel mult k elemente comune. 14.2.4. S se determine mulimile interior stabile (MIS) ale unui graf oarecare dat prin matricea sa de adiacen. 14.3. METODA BACKTRACKING (CUTARE CU REVENIRE) Aceast metod se aplic problemelor ce pot fi reprezentate sub forma unui arbore finit iar cutarea soluiei presupune parcurgerea arborelui n adncime (DF=Depth First). Problemele de acest tip au n general soluia de forma x=(x 1, . . . ,xn) S1x . . . xSn, fiecare Sk fiind o mulime finit. Mai facem cteva precizri preliminare:

a) pentru fiecare problem sunt date anumite relaii ntre componentele x1, . . . ,xn ale lui x numite condiii interne; b) produsul cartezian S=S1x...xSn se mai numete spaiul soluiilor posibile, iar soluiile posibile care satisfac condiiile interne se numesc soluii rezultat; c) n general se cer dou lucruri n astfel de probleme: - determinarea tuturor soluiilor rezultat; - determinarea doar a acelor soluii care optimizeaz o funcie obiectiv dat. Cum rezolvm astfel de probleme? Exist dou modaliti de rezolvare: 1) tehnica direct (numit i tehnica forei brute) prin care se genereaz toate elementele spaiului de soluii posibile i apoi se verific fiecare dac este sau nu o soluie rezultat; aceast tehnic necesit un timp prohibitiv (dac fiecare Si are doar 2 componente complexitatea este O(2n); totodat ar fi necesar imbricarea a n cicluri cu n aprioric necunoscut). 2) backtracking care evit generarea tuturor soluiilor posibile. S dm n continuare cteva repere ale rezolvrii: soluia este construit progresiv, component cu component; lui xk i se atribuie o valoare (evident c numai din Sk) dac i numai dac x1, . . . ,xk-1 au deja valori; se verific condiiile de continuare (strns legate de condiiile interne) dac are sens trecerea la xk+1; dac nu are sens (adic condiiile de continuare nu sunt ndeplinite atunci facem o nou alegere pentru xk sau dac am epuizat valorile din Sk atunci k:=k-1 i se face o nou alegere pentru xk-1; .a.m.d. 2. dac are sens atunci k:=k+1 i se testeaz dac k>n: 21) dac inegalitatea este adevrat atunci se afieaz soluia astfel determinat i k:=k-1 continund procesul de la punctul 1; 22) dac inegalitatea este fals se continu procesul de la punctul 1. Procedura rezolvrii unor astfel de probleme este: procedure BT(n,x) integer n; array x(n); k:=1; while k>0 do cod:=0; while ([mai exist o valoare din Sk netestat] and cod=0) x(k):=; if k(x(1),...,x(k)) then cod:=1; endif; endwhile; if cod=0 then k:=k-1 else if k=n then write (x(1),...,x(n)) else k:=k+1 endif endif; endwhile; return; end procedure Vom face cteva precizri: 1o. Predicatul k(x1, . . . , xk) reprezint condiiile de continuare pentru x1, . . . , xk; 2o. Cod este o valoare ce indic ndeplinirea/nendeplinirea condiiilor de continuare;

3o. Dac predicatul k(x1, . . . , xk) este adevrat k {1,...,n} atunci se vor afla toi vectorii din S; 4o. Backtracking poate fi uor reprezentat pe un arbore construit astfel: - nivelul 1 conine rdcina; - din orice nod de pe nivelul k pleac sk muchii spre nivelul k+1, etichetate cu cele sk elemente ale lui Sk; - nivelul n+1 va conine s1*s2*. . .* sn noduri frunz; - pentru fiecare nod de pe nivelul n+1 etichetele muchiilor coninute n drumul ce leag rdcina de acest nod reprezint o soluie posibil; Dac mulimile Sk reprezint algoritmul general se modific astfel: progresii aritmetice atunci

procedure BTA(n,a,b,h) integer n; array primul(n),ultimul(n),ratia(n),x(n); k:=1; x(1):=primul(1)-ratia(1); while k>0 do cod:=0; while ( x(k)+ratia(k) ultimul(k) and cod=0 ) x(k):=x(k)+ratia(k); if k(x(1),...,x(k)) then cod:=1 endif; endwhile; if cod=0 then k:=k-1 else if k=n then write (x(1),...,x(n)) else k:=k+1 x(k):=a(k)-h(k) endif endif; endwhile; return; end procedure unde: aritmetice; - primul(n) reprezint vectorul primilor termeni ai progresiilor

ultimul(n) reprezint vectorul ultimilor termeni ai progresiilor aritmetice; - ratia(n) reprezint vectorul raiilor progresiilor aritmetice; De evitarea (n algoritmul evitarea xSn. reinut cele dou avantaje ale acestui procedeu: imbricrii unui numr oarecare de cicluri aprioric variabil propus se imbric doar dou cicluri pretestate while); construirii spaiului tuturor soluiilor posibile S1xS2x . . .

Problem rezolvat n cte moduri se pot aranja 8 dame pe tabla de ah astfel nct s nu se "bat" reciproc. S se foloseasc al doilea algoritm dintre cei menionai anterior. Prima variant Acest program respect algoritmul anterior cu unele mici modificri. Facem precizarea c vectorul x conine n componenta x[i] numrul

coloanei de pe tabla de ah pe care se va afla dama de pe linia i. Tiprirea va reprezenta o permutare (din cele 8! soluii posibile). Se vor afla 92 de soluii. Lsm pe seama cititorului s deduc analogiile i diferenele ntre algoritm i program. #include <stdio.h> #include <math.h> void main (void) { int x[9],cod,k,i,nr; k=1; x[1]=0;nr=0; while (k>0) { cod=0; while (((x[k]+1)<=8)&&(cod= =0)) { x[k]++; if ((k= =1) && (x[k]= =1)) cod=1; else { i=1; cod=1; while ((cod==1)&&(i<k)) { if ((x[i]==x[k])||(abs(x[i]-x[k])==k-i)) cod=0; i++; } } } if (cod= =0) k--; else {if (k= =8) { printf("\n%3d. ",++nr); for (i=1;i<9;printf("%d ",x[i++])); }} else x[++k]=0; } } } A doua variant: #include <stdio.h> #include <math.h> #define n 100 int x[100],cod,k,nc,nsol; int Verifica(void) { int i,cod1; // cod1=1 conditiile de continuare cod1=1; // sunt verificate if (k>1) // cod1=0 in caz contrar; for(i=1; i<= (k-1); i++) { if (x[k]= =x[i]) cod1=0; if (abs(x[k]-x[i]) = = k-i) cod1=0; // (*) } return cod1; } void ScrieSolutie(void) { int i; printf("\n%3d. ",++nsol); for (i=1; i<=nc; printf("%3d",x[i++])); } void CitesteDate(void) { int i; nsol=0; clrscr(); nc=n+1; while ((nc>n) || (nc<0))

{ printf ("Dati n = "); scanf ("%d",&nc); }; } void main (void) { CitesteDate(); k=1; x[1]=0; while (k>0) { cod=0; while (((x[k]+1) <= nc) && (cod= =0)) {x[k]++; cod=Verifica(); } if (cod= =0) k--; else {if (k==nc) ScrieSolutie(); else x[++k]=0; } } } A doua variant este modular, mai uor de neles i generalizeaz problema damelor pn la tabla de ah de 100x100. Lsm pe seama cititorului modificarea funciei ScrieSolutie pentru a afla n mod grafic tabla de ah. Dac n funcia Verifica se terge instruciunea notat cu (*) atunci se obin toate permutrile de n obiecte. Probleme propuse 14.3.1 S se rezolve problema turelor de ah dup al doilea algoritm. n cte moduri se pot aranja n turnuri pe tabla de ah astfel nct s nu se "bat" reciproc. 14.3.2. S se afieze poziiile succesive ale unui cal pe tabla de ah, pornind dintr-o poziie dat, astfel nct s fie atinse toate csuele tablei de ah. 14.3.3. Avnd un fiier cu o mulime de cuvinte din limba romn de aceeai lungime k s se scrie un program C care afieaz toate careurile rebusiste fr puncte negre. ( Problema e fascinant implicnd i cunotine gramaticale dar i cunoscnd faptul c nu s-au construit careuri de 10x10 fr puncte negre manual i nici cu ajutorul calculatorului; se poate ncerca apoi i cu k:=11,12, . . .). 14.3.4. Un intreprinztor dispune de un capital C i are n variante de investiii. Pentru fiecare investiie i cunoate fondurile de investiie fi precum i beneficiile bi. Se cere un program care s deduc toate variantele posibile de investiii al intreprinztorului. Se mai dau i condiiile C ci i {1, . . . ,n}. 14.3.5. Avnd un graf neorientat caracterizat prin matricea costurilor s se determine prin bactraking circuitul de cost minim pornind dintr-un vrf dat. 14.3.6. Avnd un graf neorientat caracterizat prin matricea de inciden a vrfurilor s se determine prin bactraking mulimile interior stabile maximale.

14.3.7. S se determine toate cuvintele binare de lungime 10 care conin exact 5 cifre de 1. 14.3.8. S se determine toate cuvintele binare de lungime 10 care conin cel mult 5 cifre de 1. 14.3.9. S se determine toate cuvintele din {a,b,c}* (mulimea tuturor cuvintelor peste alfabetul se noteaz cu * ) de lungime 10 care conin exact 2 simboluri 'a'; 3 simboluri 'b' i 5 simboluri 'c'. 14.3.10. S se determine toate cuvintele din {a,b,c}* de lungime n care conin exact na simboluri 'a'; nb simboluri 'b' i nc simboluri 'c' (cu condiia n=na+nb+nc). 14.3.11. S se determine toate tripletele (x1,x2,x3) de numere astfel ca: x1+x2+x3 suma x1*x2*x3 produs cu valorile suma i produs date iar x1S1, x2S2 i x3S3 ; S1, S2 i S3 fiind trei progresii aritmetice date deasemenea. 14.3.12. S se determine toate variantele de pronosport cu 13 rezultate din {1,x,2} care conin exact n1 simboluri '1'; nx simboluri 'x' i n2 simboluri '2' (cu condiia n1+nx+n2=13). 14.3.13. S se determine toate variantele de pronosport cu 13 rezultate din {1,x,2} care conin cel mult n1 simboluri '1'; cel mult nx simboluri 'x' i simboluri '2' n rest (cu condiia n1+nx 13). 14.4. METODA DIVIDE ET IMPERA (DIVIDE I STPNETE) Aceast modalitate de elaborare a programelor const n mprirea repetat a unei probleme de dimensiune mai mare n dou sau mai multe subprobleme de acelai tip urmat de combinarea soluiilor subproblemelor rezolvate pentru a obine soluia problemei iniiale. Se d un vector A=(a1,...,an) i trebuie efectuat o prelucrare oarecare asupra elementelor sale. Presupunem c: p,q{1,...,n} cu 1 p < q m{p,p+1,...,q-1} a.. prelucrarea secvenei {ap,...,aq} se poate face prelucrnd subsecvenele: {ap,...,am} i {am+1,...,aq} i apoi combinnd rezultatele pentru a obine prelucrarea ntregii secvene {ap,...,aq}. Dac se reuete o astfel de formalizare a problemei atunci ea poate fi rezolvat cu ajutorul acestei metode. Vom da n continuare o procedur recursiv n pseudocod: procedure DI (p,q,) if q-p eps then call PREL (p,q,) else call IMPARTE (p,q,m) ; call DI (p,m,); call DI (m+1,q,); call COMB (,,); endif; return; end procedure

Cteva precizri se impun: procedura trebuie apelat prin call DI (1,n,) n obinndu-se rezultatul final; eps este lungimea maxim a unei secvene {ap,...,aq} notat prin (p,q) pentru care se face prelucrarea direct fr a mai fi necesar mprirea n subprobleme; procedura PREL realizeaz prelucrarea direct a secvenelor (p,q); procedura COMB realizeaz combinarea rezultatelor i ale prelucrrii a dou secvene vecine (p,m) i (m+1,q) obinnd rezultatul al prelucrrii ntregii secvene (p,q); prin procedura IMPARTE se obine valoarea lui m. Vom da ca exemplu problema sortrii cresctoare a unui ir de ntregi prin interclasare. deoarece secvenele (i,i+1) sunt uor de ordonat acestea vor constitui secvenele ce se vor prelucra, deci eps = 1; - m se va calcula ca (p+q)/2, deci nu mai e nevoie de procedura special IMPARTE; procedura COMB va interclasa ntotdeauna dou secvene (p,m) i (m+1,q) ordonate cresctor; vom folosi un vector x drept structur global i vom face toate prelucrrile pe elementele sale nemaiavnd nevoie de zonele ,; pentru zona vom folosi un vector local y n procedura COMB acesta coninnd elementele corespondente din x dar ordonate cresctor; tot n procedura COMB se vor copia apoi elementele lui y din poriunea (p,q) n x; evident c procedurile din schema general a algoritmului sunt funcii C cititorul fcnd analogiile necesare. #include <stdio.h> #include <conio.h> #define nrelem 100 int x[nrelem]; int n; void PREL (int p, int q) {int aux; if ((p<q) && (x[p] > x[q])) { aux=x[p]; x[p]=x[q]; x[q]=aux; } } void COMB (int inf, int mijloc, int sup) {int i,j,k,l; int y[nrelem]; i=k=inf; j=mijloc+1; while (i<=mijloc && j<=sup) { if (x[i] <= x[j]) y[k++]=x[i++]; else y[k++]=x[j++]; } for(l=i; l<=mijloc; y[k++]=x[l++]); for(l=j; l<=sup; y[k++]=x[l++]); for(k=inf; k<=sup; x[k++]=y[k]); } void DI (int p, int q) { int m;

if ((q-p) <= 1) PREL (p,q); else {m=(p+q)/2; DI (p,m); DI (m+1,q); COMB (p,m,q); } return; } void main(void) {int i; clrscr(); printf ("dati nr de elemente\n"); scanf ("%d",&n); for (i=1; i<=n; i++) {printf("x[%d]=",i); scanf("%d",&x[i]); } DI (1,n); printf("\nsirul sortat crescator este:\n"); for (i=1; i<=n; i++) printf("x[%d]=%d\n",i,x[i]); getch(); } 14.5. METODA PROGRAMRII DINAMICE Aceast metod este aplicabil problemelor de optim n care soluia poate fi privit ca rezultatul unui ir de decizii d 1, . . . ,dn. Fiecare decizie depinde de deciziile deja luate i nu este unic determinat (spre deosebire de tehnica greedy unde fiecare decizie care se ia trebuie s fie unic). Totodat este necesar s fie satisfcut una din variantele principiului optimalitii pentru a putea fi aplicat aceast metod. Vom formaliza acest principiu al optimalitii: fie d1,...,dn un ir optim de decizii (SOD) care transform starea s o n starea sn, trecnd prin strile intermediare s1, . . . ,sn-1; vom nota acest fapt prin (d1,dn) este SOD pentru perechea de stri (so,sn); grafic procesul este descris ca n figura urmtoare: d1 d2 dn *----->*---->*------>* . . . *------>* so s1 s2 sn-1 sn Vom da acum mai multe variante ale principiului optimalitii: dac (d1,dn) este SOD pentru (so,sn) atunci (d2,dn) este SOD pentru (s1,sn); 2) dac (d1,dn) este SOD pentru (so,sn) atunci i din {1, . . . ,n-1} avem a) (d1,di) este SOD pentru (so,si) SAU b) (di+1,dn) este SOD pentru (si,sn). 3) dac (d1,dn) este SOD pentru (so,sn) atunci i din {1, . . . ,n-1} avem a) (d1,di) este SOD pentru (so,si) I b) (di+1,dn) este SOD pentru (si,sn).

Ultima variant este cea mai general i mai complet. Odat verificat o form a principiului optimalitii, metoda programrii dinamice const n a scrie relaiile de recuren i apoi de a le rezolva. n general relaiile de recuren sunt de 2 tipuri :

fiecare decizie di depinde de di+1,...,dn - relaii de recuren nainte, deciziile vor fi luate n ordinea dn, dn-1, . . . ,d1; fiecare decizie di depinde de d1, . . . ,di-1 - relaii de recuren napoi, deciziile vor fi luate n ordinea d1, d2, . . . , dn. Problem rezolvat Fie G=(X,) un 1-graf orientat cruia i atam matricea costurilor, (fiecare arc (i,j) este etichetat cu o valoare real strict pozitiv). Se pune problema gsirii drumului de valoare minim (DVM) ntre oricare 2 vrfuri i i j. Rezolvare Verificm prima variant a principiului optimalitii: - fie i, i1, i2, . . . , im, j DVM ntre i i j atunci evident c i1, . . . , im, j este DVM ntre i1 i j; - notm prin L(i,k) lungimea DVM dintre i i k, kX; - notm deasemenea dkj valoarea arcului (k,j); - ecuaiile de recuren sunt: L(i,j) = min {L(i,k) + dkj)} (k,j) #include<stdio.h> #include<values.h> #define nn 10 int d[nn+1][nn+1],i,j,n; int L(int i,int j) { int m=MAXINT; int k,x=0; if (i= =j) m=0; else for (k=1;k<=n;k++) if (d[k][j] < MAXINT) { x=L(i,k)+d[k][j]; if (x<m) m=x; } return m; } void citestematrice(void) {int i,j; printf("\ndati dimensiunea matricii distantelor : "); scanf("%d",&n); for(i=1;i<=n;i++) for(j=1;j<=n;j++) { printf("d[%d,%d]= ",i,j); scanf ("%d",&d[i][j]); } for(i=1;i<=n;i++) for(j=1;j<=n;j++) if (d[i][j]= = -1) d[i][j]=MAXINT; } void citeste(void) { printf("\ndati varful initial : "); scanf("%d",&i); printf("\ndati varful final : "); scanf("%d",&j);

} void afiseaza(int val) { printf("\nvdlm intre varful %d si %d este %d",i,j,val); } void main(void) { int vdlm; clrscr(); citestematrice(); citeste(); vdlm=L(i,j); afiseaza(vdlm); getch(); } Probleme propuse 14.5.1. Se d o matrice A=(aij), i=1,...,n; j=1,...,m cu elementele din mulimea {0,1,2}. Dou elemente din A aij i akl se numesc 4-vecine dac i-k j-l = 1. + Notm cu So, S1 i S2 submulimile formate din elementele matricii egale cu 0, 1 respectiv 2. Submulimea S1 se mparte n grupuri astfel: aij i akl fac parte din acelai grup dac sunt 4-vecine sau dac apq S1 : aij i apq sunt 4-vecine iar apk i akl sunt din acelasi grup. Un element akl So l vom numi spion al grupului G S1 dac aij G a.. akl i aij s fie vecine. Un spion este perfect dac are toi vecinii din G. Se cere: a) cel mai numeros grup (S1) care are un singur spion (dac exist). b) toate grupurile care au cel puin doi spioni perfeci. 14.5.2. Se d o matrice cu elemente care au valori din {d,o}, care reprezint un teren cu drumuri {d} i obstacole {o}. n acest teren se afl un tanc i o int. Acest tanc poate primi comenzi pentru rotirea evii (cu 90o n ambele sensuri), pentru deplasare (n direcia evii cu o linie sau cu o coloan) sau pentru tragere (n direcia evii pentru a nimeri inta) n cazul n care ntre tanc i int nu exist nici un obstacol. Considernd c deplasarea nu se poate efectua prin obstacole, se cere cel mai scurt drum necesar tancului pentru a putea distruge inta i irul comenzilor efectuate n cazul n care exist un astfel de drum. 14.5.3. Se d o matrice cu elemente din {0,1,2,3}, reprezentnd o pdure cu capcane (0) i crri (1). n aceast pdure se afl o vulpe (2) i mai muli lupi (3). Fiecare lup ncearc s se apropie de vulpe fr a ti unde se afl capcanele, iar n cazul n care cade ntr-o capcan, moare. Vulpea ncearc s se ndeprteze de cel mai apropiat lup, avnd ns posibilitatea s descopere i capcanele i s le ocoleasc. Att vulpea ct i lupii i pot modifica poziia doar cu o linie sau cu o coloan n fiecare moment. S se spun dac vulpea reuete s scape de lupi. 14.5.4. Se consider un teren dreptunghiular sub forma unei matrici A de m linii i n coloane. Elementele aij ale matricii conin cotele (nlimile) diferitelor poriuni astfel obinute. Se presupune c o bil se gsete pe o poriune (io,jo) avnd cota a(io,jo). Se cere un program care s precizeze toate traseele (ncepnd cu (io,jo) ) posibile pe care le poate urma bila spre a iei din teren, tiind c bila se poate deplasa n orice poriune de teren 4-nvecinat cu o cot strict inferioar cotei terenului pe care se gsete bila. 14.5.5.

Se cere un program care rezolv problema labirintului (nerecursiv). O matrice de m linii i n coloane reprezint un labirint dac: a(i,j) = o - semnificnd culoar; a(i,j) = 1 - semnificnd zid. Un traseu de ieire pornind de la o poriune (i o,jo) trebuie s fie o succesiune de perechi (io, jo), (i1, j1) . . . (ik, jk) astfel nct 2 perechi nvecinate s fie 4-vecine i a(ip,jp)=0 p=0, . . . ,k.

ANEX UN MEMENTO AL SINTAXEI LIMBAJULUI C SINTAXA LIMBAJULUI C a) b) c) d) e) n descrierea sintaxei limbajului vom folosi urmtoarele notaii: ::= cu semnificaia "prin definiie este"; pentru a separa alternativele unei definiii sintactice; < > pentru definirea unor metavariabile; [ ] pentru delimitarea elementelor opionale; .. pentru a indica elementele care se pot repeta.

Exemplu: Pentru a defini un "ntreg_zecimal" ca o succesiune de cifre semnificative vom scrie: <ntreg_zecimal> ::= <Cifr_nenul> [<Cifr>] .. A. Atomi lexicali <Cuvinte_cheie> ::= auto break case char const continue default double| do else enum extern float for goto int|long if register | return short signed sizeof static|struct switch typedef union unsigned void volatile while <Nume> ::= <Liter>[<Liter> <Cifr>] .. <Liter> ::= a c e g i k m o q s u w y b d f h j l n p r t v x z C E G I K M O Q S U W Y _ B D F H J L N P R T V X Z <Cifr> ::= 0 2 4 6 8 1 3 5 7 9 <Constant> ::= <Constant_ntreag> <Constant_real> <Constant_enumerare> <Constant_caracter> <Constant_ntreag> ::= <Numr_ntreg>[<Sufix_ntreg> <Numr_ntreg> ::= <ntreg_zecimal> <ntreg_hexazecimal> <ntreg_octal> <ntreg_zecimal> ::= <Cifr_nenul>[<Cifr>] .. <Cifr_nenul> ::= 1 3 5 7 9 2 4 6 8 <ntreg_hexazecimal> ::= 0x<Cifr_hexa> .. <Cifr_hexa> ::= <Cifr> b d f B D F a c e A C E <ntreg_octal> ::= 0[<Cifr_octal>] .. <Cifr_octal_> ::= 0 2 4 6 1 3 5 7

<Sufix_ntreg> <Sufix_unsigned>[<Sufix_long>] <Sufix_long>[<Sufix_unsigned>] <Sufix_unsigned> ::= U u <Sufix_long> ::= L l <Constant_real> ::= <Constant_fract>[<Exponent>][<Sufix_real>] <Cifr> .. <Exponent>[<Sufix_real>] <Constant_fract> ::= [<Cifr>] .. . <Cifr>.. <Cifr>.. . <Exponent> ::= E [+ <Cifr> .. e -] <Sufix_real> ::= F L f l <Constant_enumerare> ::= <Nume> <Constant_caracter> ::= '<Caracter_C> ..' L'<Caracter_C>' <Caracter_C> ::= <Orice_caracter_tipribil_cu excepia: ' i \> <Secven_escape>

::=

<Secven_escape> ::= \" \? \a \f \r \v \' \\ \b \n \t \<Cifr_octal>[<Cifr_octal>[<Cifr_octal>]] \x<Cifr_hexa> .. <ir_de_caractere> ::= "[<Caracter_S> ..]" <Caracter_S> ::= i \> <Secvena_escape> <Orice_caracter_tipribil_cu excepia: '

<Operatori_i_semne_de_punctuaie >::= + * % & ! -> -= - / ^ ~ = += *= /= ^= != >> %= &= << <<= >>= = = <= >= > !! -- () { ; : < && ++ , [] } ? ... B. Declaraii <Unitate_de_compilare> ::= <O_declaraie> .. <O_declaraie> ::= <Def_funcie> <Declaraie>; <Declaraie> ::= [<Specificatori>][<List_declaratori>] <Specificatori> ::= <Specificator> .. <Specificator> ::= <Clas_memorare> <Tip> typedef <Clas_memorare> ::= auto register static extern <Tip> ::= <Tip_simplu> <Nume_typedef> <Calificator> <Descriere_tip_enumerare> <Descriere_tip_neomogen> <Tip_simplu> ::= char short int long signed unsigned float double void <Nume_typedef> ::= <Nume> <Descriere_tip_enumerare> ::= enum<Nume> enum[<Nume>]{<Lista_enum>}

<Lista_enum> ::= <Enumerator>[,<Enumerator>] .. <Enumerator> ::= <Nume>[=<Expr_constant_>{] <Descriere_tip_neomogen> ::= <Tip_neomogen><Nume> <Tip_neomogen>[<Nume>]{<List__cmpuri>} <Tip_neomogen> ::= struct union <List_cmpuri> ::= <Decl_cmpuri>;[<Decl_cmpuri>;] ..

<Decl_cmpuri> ::= [<Specificatori>][<Cmp>[,<Cmp>] ..] <Cmp> ::= <Declarator> [<Nume>]:<Expr_constant> <List_declaratori> ::= <Decl_init>[,<Decl_init>] .. <Decl_init> ::= <Declarator>[<Ini ializare>] <Declarator> ::= [<Indirect>..]<Denumire>[<Decl_F_T>] <Indirect> ::= *[<Calificator> ..]

<Denumire> ::= <Nume> (<Declarator>) <Decl_F_T> ::= ([<L_declar_par>]) <Decl_tablou> .. <Decl_tablou> ::= [] [<Expr_constant>] <L_declar_par> ::= <Decl_par>[,<Decl_par>] .. [, ..] <Decl_par> ::= <Tip>..<Declarator> <Decl_tip> <Decl_tip> ::= <Tip>..[<Indirect>..][(<Decl_tip>)][<Decl_F_T>] <Iniializare> ::= =<Expr> ={<List_init>[,]} <List_init>::=<Expr>[,<List_init> .. {<List_init>}[,<List_init>] .. <Def_funcie> [<Specif_funcie> ..]<Declarator>[<Declaraie>;] .. <Bloc> <Specif_funcie> ::= extern static <Tip> ::=

C. Expresii <Expr> ::= <Expresie>[,<Expresie>] .. <Expresie> ::= <Expr_unar><Oper_atribuire><Expresie> <Oper_atribuire> ::= + /= += <<= *= %= -= >>= ^= &= != <Expr_condiional> ::= <Expr_SAU_logic> <Expr_SAU_logic> ? <Expr> : <Expr_condi ional> <Expr_condiional>|

<Expr_SAU_logic> <Expr_I_logic> <Expr_I_logic> ::=

::=

<Expr_I_logic> <Expr_SAU_logic>

!!

<Expr_SAU> <Expr_I_logic> && <Expr_SAU>

<Expr_SAU> ::= <Expr_SAU_exclusiv> <Expr_SAU><Expr_SAU_exclusiv> <Expr_SAU_exclusiv> ::= <Expr_I> <Expr_SAU_exclusiv> ^ <Expr_I> <Expr_I> ::= <Expr_egalitate> <Expr_I> & <Expr_egalitate> <Expr_egalitate> <Expr_rela ional> <Expr_egalitate>==Expr_relaional> <Expr_egalitate> != <Expr_relaional> <Expr_relaional> <Expr_deplasare> <Expr_deplasare> <Expr_relaional> <Expr_deplasare> <Expr_deplasare> <Expr_deplasare> <Expr_aditiv> ::= <Expr_aditiv> <Expr_deplasare> << <Expr_relaional> <= >= ::= <Expr_deplasare> <Expr_relaional> ::=

< >

<Expr_relaional>

<Expr_deplasare> >> <Expr_aditiv> <Expr_aditiv> <Expr_multiplicativ> <Expr_multiplicativ> ::= <Expr_multiplicativ> <Expr_aditiv> <Expr_aditiv> + -

<Expr_multiplicativ>::= <Expr_prefixat>

<Expr_multiplicativ> <Expr_multiplicativ> / <Expr_prefixat> <Expr_multiplicativ> % <Expr_prefixat>

<Expr_prefixat> ::= <Expr_unar> (<Decl_tip>)<Expr_prefixat> <Expr_unar> ::= <Expr_postfixat> <Op_unar><Expr_prefixat> ++<Expr_unar> --<Expr_unar> sizeof<Expr_unar> sizeof(<Decl_tip>) <Op_unar> ::= & + ~ * - ! <Expr_postfixat> ::= <Termen> <Expr_postfixat>[<Expr>] <Expr_postfixat>(<List_Expresii>) <Expr_postfixat> . <Nume> <Expr_postfixat> -> <Nume> <Expr_postfixat> ++ <Expr_postfixat>-<Termen> ::= <Nume> <Constant> <ir_de_caractere> (<Expr>) <List_Expresii> ::= [<Expr>] .. <Expr_constant> ::= <Expr_condiional>

D. Instruciuni <Instr>::= <Instr_etichetat> <Instr_compus> <Instr_expresie> Instr_de_selecie> <Instr_de_ciclare> <Instr_de_salt> <Instr_etichetat> ::= <Nume>:<Instr> case <Expr_constant { :<Instr> default : <Instr> <Instr_compus> ::= <Bloc> <Bloc> ::= {[<Declaraie>;] .. [<Instr>] ..} <Instr_expresie> ::= [<Expr>]; <Instr_de_selecie> ::= if (<Expr>) <Instr> if (<Expr>) <Instr> else <Instr> switch (<Expr>) <Instr> <Instr_de_ciclare> ::= while (<Expr>)<Instr>; do <Instr> while (<Expr>); for ( [<Expr>];[<Expr>];[<Expr>] ) [<Instr>];

<Instr_de_salt> ::= goto <Nume>; continue; break; return [<Expr>];

CUPRINS

BIBLIOGRAFIE Brian W.Kerninghan, Dennis M. Ritchie, The C Programming Language, INC. Englewood Cliffs, New Jersey, 1978. Liviu Negrescu, Limbajul C, Editura Libris, Cluj-Napoca, 1997. G. Moldovan, M. Lupea, V.Cioban, Probleme pentru programare n limbajul C, Litografia Universitii Babe-Bolyai, Cluj-Napoca, 1995.

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