Sunteți pe pagina 1din 22

ALGORITMI I PROGRAMARE

Prof. univ dr. GRIGORE ALBEANU Lector univ. SILVIU BRZ I. INTRODUCERE
I.1. Ce este informatica? n anul 1642, matematicianul i fizicianul Blaise Pascal (1623-1662) a inventat prima masin mecanic, cu roi dinate, capabil s realizeze operaii de adunare i scdere a numerelor naturale. Totui, de-abia dup apariia mainilor electromecanice, n 1944, John von Neumann a formulat principiul programului nregistrat i a sugerat constructorilor de calculatoare trei principii care trebuie avute n vedere pentru realizarea unui calculator: programele i datele trebuie s fie codificate sub form binar; programele i datele trebuie stocate ntr-o memorie a mainii de calcul; trebuie s existe o component (unitate central de prelucrare, procesor) special care tie att s execute operaii de calcul, ct i s extrag, s decodifice i s execute instruciunile programului. Astfel, aproape toate tipurile de sisteme de calcul ce au aprut mai trziu sunt calculatoare de tip von Neumann. Apariia sistemelor de calcul a dus la apariia i dezvoltarea unei noi tiine: informatica (Informatique, Informatics, Informatik n Europa, respectiv Computer Science n SUA). Informatica reprezint un complex de discipline prin care se asigur prelucrarea informaiilor cu ajutorul sistemelor de calcul. Astzi, informatica este prezent n industrie, bnci, ferme, art, medicin, tiine sociale etc. i este structurat n mai multe domenii precum: arhitectura sistemelor de calcul: studiaz modul de organizare a sistemului fizic (hardware) pentru a se obine o mai mare eficient, siguran i utilitate. sisteme de operare: studiaz modalitile de gestiune eficient a resurselor fizice i a programelor. algoritmi si structuri de date: studiaz metodele de rezolvare a problemelor i modurile de organizare a datelor pentru a obine programe eficiente. limbaje de programare: studiaz modalitile prin care algoritmii i structurile de date sunt prezentate calculatorului pentru a fi prelucrate. ingineria programrii: studiaz metodele de automatizare a proceselor de proiectare, analiz, testare i reutilizare a programelor. calcule numerice i simbolice: studiaz modalitile de reprezentare a informaiei numerice pentru implementarea unor algoritmi numerici robuti, eficieni i de mare precizie. sisteme de gestiune a bazelor de date: studiaz modalitile de structurare i organizare eficient a coleciilor mari de date ce vor fi supuse diverselor prelucrri. inteligena artificial: studiaz modalitile de reprezentare i manipulare a cunotinelor n vederea obinerii de noi cunotine. animaia i robotica: studiaz modalitile de reprezentare, prelucrare i analiz a informaiei audiovizuale. n sfera sa de preocupri, informatica a atras i dezvoltat o mare varietate de discipline precum: logica matematic, teoria automatelor, limbaje formale, cercetri operaionale, teoria grafurilor i reelelor, calcul numeric, teoria fiabilitii, geometrie computaional, teoria calculabilitii, baze de date, baze de cunotine, sisteme expert . I.2. Ce este un program? Noiunea de algoritm Soluia unei probleme, din punct de vedere informatic, este dat printr-o mulime de comenzi (instruciuni) explicite i neambigue, exprimate ntr-un limbaj de programare. Aceast mulime de instruciuni prezentat conform anumitor reguli sintactice formeaz un program. Un program poate fi privit i ca un algoritm exprimat ntr-un limbaj de programare. Totui, un algoritm descrie soluia problemei independent de limbajul de programare n care este redactat programul. Exist mai multe definiii ale noiunii de algoritm. A. A. Markov, a considerat algoritmul ca fiind o noiune matematic primar i a descris-o astfel: Un algoritm este o reet care descrie precis i clar un proces de calcul. El a descris un mecanism formal pentru a specifica o clas larg de activiti i anume:
215

algoritmii normali. Alte modaliti de descriere a algoritmilor ce au mai fost propuse sunt: maina Turing, sistemele Post, funciile recursive etc. n aceast lucrare, prin algoritm vom nelege o secvena finit de comenzi explicite i neambigue care, executate pentru o mulime de date (ce satisfac anumite condiii iniiale), conduce n timp finit la rezultatul corespunztor. Observm c se face distincie ntre algoritm (care se termin n timp) i procedur (care poate continua nedefinit). Conform lui D. Knuth (The art of computer programming, Vol. I, 1997) un algoritm posed cinci caracteristici importante: Un algoritm are caracter finit. El este descris printr-o secvena finit de etape i trebuie ca, ori de cte ori sunt parcurse etapele algoritmului pentru anumite date, procesul s se ncheie n timp finit. Un algoritm are caracter determinist. Aciunile specificate de paii algoritmului sunt clare, fiind riguros prezentate. Un algoritm are date de intrare. Se poate admite c ntotdeauna un algoritm lucreaz asupra unor date de intrare. Acestea pot fi evideniate din contextul n care este formulat problema a crei soluie o reprezint algoritmul. Un algoritm furnizeaz cel puin o valoare de ieire ce se afl ntr-o relaie specific cu datele de intrare. Un algoritm este eficace. Trebuie spus c nu orice problem admite soluie descris algoritmic. Problemele pentru care exist un algoritm de rezolvare se numesc probleme decidabile. Problemele pentru care s-a demonstrat (matematic) c nu admit un algoritm de rezolvare se numesc probleme nedecidabile. Nu este suficient ca o anumit problem (clas de probleme) s admit soluii (chiar si o singur soluie). Din punctul de vedere al informaticii, intereseaz dac exist soluie ce poate fi descris algoritmic, deci dac soluia problemei poate fi construit efectiv. De asemenea, pentru rezolvarea anumitor probleme pot exista mai muli algoritmi. Stabilirea celui mai bun dintre acetia se realizeaz n urma unui proces de analiz prin care se determin performantele fiecruia dintre algoritmi. Am vzut c algoritmii accept date de intrare i furnizeaz rezultate (date de ieire). Introducerea unei valori sau a unui ir de valori se va descrie folosind verbul citete. Afiarea unei valori sau a unui ir de valori va fi descris prin verbul scrie. Vom conveni s numerotm paii (etapele) unui algoritm. Aceast numerotare va fi utilizat eventual n ali pai. Acest mod de prezentare a algoritmilor se numete descriere n limbaj convenional. n unele lucrri, limbajul convenional mai este numit i limbaj pseudocod. Exemplul 1. Fie a i b dou variabile ntregi. Dorim s schimbm ntre ele valorile celor dou variabile. Mai precis, dac a = 640 i b = 480, dorim s obinem a = 480 i b = 640. Un mod de a realiza aceast schimbare presupune utilizarea unei variabile suplimentare, cu rol de intermediar. Folosind trei operaii de atribuire, algoritmul are urmtorii pai: 1. citete a, b 2. t := a 3. a := b 4. b := t 5. scrie a,b I.3. Cum rezolvm probleme cu ajutorul calculatorului? Am vzut c exist probleme pentru care nu poate fi dat un algoritm de rezolvare. Totui cele mai multe probleme cu care se confrunt informatica sunt probleme decidabile. Toate temele tratate n aceast lucrare formuleaz probleme decidabile. Se tie c nu nvarea unui limbaj de programare este o sarcin dificil, ci rezolvarea algoritmic a problemelor decidabile. Este clar c, nainte de a ncepe scrierea unui program trebuie, mai nti, s gsim (sau s cunoatem deja) soluia algoritmic a problemei puse. Cum se gsete soluia? Etapele descrise de G. Plya (Cum rezolvm o problem? Editura tiinific, Bucureti, 1965), pentru rezolvarea unei probleme de matematic, sunt valabile i n informatic. nainte de a ncepe s vedem cum se face, trebuie s nelegem att ipotezele problemei, ct i ceea ce se cere. Deci, nelegerea problemei, ocup primul loc n procesul cutrii unei metode de rezolvare a acesteia cu ajutorul calculatorului. Trebuie evideniate datele de intrare si condiiile pe care acestea trebuie s le ndeplineasc. Este important s identificm esenialul. De multe ori un enun al unei probleme conine foarte multe elemente descriptive privind importanta problemei, consideraii de natur istoric, exemple, uneori chiar i etape distincte pentru obinerea soluiei. Cerina problemei furnizeaz, de multe ori, chiar idei privind modul de a ajunge la soluie. Considerarea unor configuraii particulare pentru datele de intrare i ncercarea de a lucra asupra lor, pentru a gsi soluia, va contribui ntotdeauna la o nelegere mai bun a enunului.
216

Dac n enun se sugereaz etapele rezolvrii, acestea implicnd rezolvarea unor subprobleme, atunci trebuie s aflm soluia algoritmic corespunztoare fiecrei etape. Dac nu se specific etape, dar putem descompune problema n subprobleme mai simple, atunci soluia algoritmic a problemei iniiale se va obine prin "compunerea" soluiilor subproblemelor. Deci, este foarte important s tim s rezolvm probleme simple, eventual probleme reprezentative pentru anumite clase de aplicaii i s tim s descompunem (respectiv, s reducem) problema iniiala n (la) subprobleme uor de rezolvat i apoi s construim soluia final. Abordarea prin descompuneri repetate, cu detaliere pas cu pas se numete abordare top-down sau rafinare iterativ. Abordarea prin care, pornind de la soluii algoritmice ale unor probleme cunoscute, construim soluii ale altor probleme care au ns legtur cu problema de rezolvat, iar n final, urmnd aceeai modalitate construim soluia problemei a crei soluie se cere, se numete abordare bottomup. Aceast metod permite reutilizarea programelor existente i este tot mai important odat cu apariia tehnicilor orientate obiect. De asemenea, pentru a obine o soluie a unei probleme este util s privim problema i comparativ cu alte probleme ntlnite. Numai dup investigarea acestor variante putem trece la stabilirea metodei de abordare. Pentru probleme de complexitate ridicat, oricare din metodele de abordare top-down sau bottom-up, conduc la o soluie modular, bazat pe subprograme sau, mai nou, la o soluie orientat obiect. Multe din problemele de programare pot fi rezolvate uor dac soluia verific anumite principii. Dac soluia problemei este o mulime de elemente care se poate obine pas cu pas, pornind de la mulimea vid, prin adugarea celui mai bun element neconsiderat nc (i ales conform unui anumit criteriu) spunem c avem de-a face cu o abordare de tip greedy. Pentru probleme n care se cere o soluie optim care satisface principiul optimalitii (principiul lui Bellman) se va aplica metoda programrii dinamice. Alte strategii pentru elaborarea algoritmilor sunt: metoda divide et impera, metoda backtracking, euristica etc.

II. ALGORITMI: DESCRIERE, COMPLEXITATE,


II.1. Noiuni de teoria grafurilor

CORECTITUDINE

Un graf neorientat G este definit prin perechea G = (V, E), unde V este o mulime nevid de elemente numite vrfuri (vertex), iar E este o mulime (posibil vid) de perechi neordonate cu componente distincte ale lui V care se numesc muchii (edges). Dac E este mulimea vid spunem c G este trivial. n cazul n care mulimea V este finit spunem c avem un graf finit. Numrul elementelor mulimii V determin ordinul grafului finit. O muchie cu vrfurile x i y (numite extremiti) se noteaz prin [x, y] sau [y, x]. Spunem c vrfurile x i y sunt incidente muchiei [x,y]. Un digraf este definit printr-o pereche G=(V, E), unde V este o mulime nevid de vrfuri, iar E este o parte a produsului cartezian V x V ale crei elemente le numim arce. Un arc este notat prin (x,y) cu x diferit de y, unde x se numete surs sau extremitate iniiala, iar y se numete destinaie sau extremitate final. Atributele : trivial, respectiv finit, pentru un digraf se definesc similar. Un graf (digraf) parial al unui graf (digraf) G = (V, E) este un graf (digraf) G1 = (V, E1), unde E este submulime a mulimii E, deci este graful (digraful) G nsui sau se obine din G prin suprimarea anumitor muchii (arce). Un subgraf (subdigraf) al unui graf (digraf) G este un graf (digraf) H = (U, E'), unde U este submulime a mulimii V, E' este submulime a mulimii E, iar muchiile (arcele) din E' sunt toate muchiile (arcele) din E, care au ambele extremiti n mulimea de vrfuri U. Uneori, arcele (muchiile) sunt etichetate. Dac etichetele sunt numere reale pozitive se spune c digraful (graful) este ponderat. n continuare vom considera numai grafuri (digrafuri) finite. De obicei un graf se reprezint prin indicarea unor puncte din plan (ce corespund vrfurilor) i a unor segmente de curb (sau de dreapt) pentru indicarea muchiilor. n cazul digrafurilor arcele sunt segmente de curb orientate, sensul fiind de la surs spre destinaie. Definiie. Fie G = (V,E) un digraf, iar x i y dou vrfuri. Numim x-y drum (de lungime n) o (vi, vi+1) aparine mulimii E pentru toi i, 1 secvena de vrfuri D: v0, v1, ..., vn dac v0 = x, vn = y , iar i n-1. Vrfurile x i y se numesc extremitile drumului D. Un drum fr nici un arc este un drum trivial. Un x-y drum se numete circuit (sau drum nchis) dac x=y; dac x diferit de y, se spune c drumul este unul deschis. Circuitul este elementar dac toate vrfurile circuitului, cu excepia primului i a ultimului vrf care coincid, sunt distincte dou cte dou. Definiie. Fie G = (V, E) un graf neorientat, iar x i y dou vrfuri. Secvena de vrfuri L: v0, v1, ..., vn este un x-y lan (de lungime n) dac [vi, vi+1] aparine mulimii E, pentru toi i, 0 i n-1, iar x=v0 si y=vn. L este ciclu dac x=y. Atributul elementar, pentru un lan, poate fi introdus similar.
217

Uneori, chiar pentru un digraf, putem folosi noiunea de lan, dac se verific proprietatea c oricare dou arce vecine au o extremitate comun. Definiie. Numim lan (drum) hamiltonian un lan (drum) elementar al unui graf care conine toate vrfurile grafului. Definiie. Fie G = (V, E) un graf netrivial. Spunem c x, y din V sunt conectate n G dac exist un x-y lan n G. Graful G este un graf conex dac oricare dou vrfuri din V sunt conectate n G. Dac G este un graf trivial (E mulime vid) se accept c este graf conex. Dac G nu este conex, atunci exist cel puin dou componente conexe (subgrafuri conexe maximale, disjuncte dou cte dou relativ la vrfuri). Un digraf G cu proprietatea c pentru oricare dou vrfuri x i y exist att un x-y drum, ct si un y-x drum n G se numete graf tare conex. n seciunea urmtoare prezentm o metod de reprezentare a algoritmilor folosind schemele logice. Acestea sunt introduse folosind terminologia teoriei grafurilor. II.2. Scheme logice structurate O transcriere grafic a etapelor (pailor) unui algoritm este numit organigram. De cele mai multe ori este folosit denumirea de "schem logic". Fiecrui pas, al algoritmului, i se asociaz un bloc ce constituie eticheta unui arc. Blocurile folosite ntr-o schem logic descriu instruciuni (comenzi) sau predicate (expresii logice). Predicatele apar n cadrul instruciunii de ramificare. Celelalte instruciuni sunt: 1. Instruciunea START: eticheteaz arcul iniial (acel arc n a crui extremitate iniiala nu pot sosi alte arce). Orice schem logic are un unic arc iniial. Acesta va indica punctul de unde ncepe execuia unui program. 2. Instruciunea STOP: eticheteaz un arc final (acel arc din a crui extremitate final nu pot pleca alte arce). O schem logic poate avea mai multe arce finale. Acestea indic oprirea execuiei programului descris prin intermediul schemei logice. 3. Instruciunea CITESTE: eticheteaz un arc ce indic introducerea de la mediul de intrare (tastatur, disc etc.) a unei secvene de valori (numite date de intrare). Cuvntul CITESTE este nsoit de variabilele ce descriu datele de intrare. 4. Instruciunea SCRIE: eticheteaz un arc ce indic nregistrarea la mediul de ieire (display, disc etc.) a rezultatelor (datelor de ieire). Cuvntul SCRIE este nsoit de variabilele sau constantele ce desemneaz datele de ieire. 5. Instruciunea de atribuire: eticheteaz un arc cu eticheta v := e, unde v este o variabil, iar e este o expresie de acelai tip (numeric sau logic) cu variabila v. 6. Instruciunea de ramificare: este caracterizat prin n arce ce pleac din acelai punct, arce etichetate cu predicatele p1, p2, ..., pn definite astfel nct p1 or p 2 or ... or pn = TRUE (adevrat) i pi and pj = FALSE (fals) pentru oricare i i j diferii. Definiie. Se numete schem logic (sau program sub form de schem logic) un graf orientat, n care:
exist o unic instruciune START i cel puin o instruciune STOP; orice vrf (diferit de extremitatea final a unei instruciuni STOP) este extremitatea iniiala a unei

unice instruciuni; orice arc este etichetat cu una dintre instruciunile: START, STOP, CITESTE, SCRIE, atribuire sau cu un predicat. n ultimul caz, extremitatea iniiala a arcului trebuie s coincid cu extremitatea iniiala a unei instruciuni de ramificare; pentru orice arc exist cel puin un drum care ncepe cu instruciunea START, se termin cu o instruciune STOP i conine arcul considerat. Schemele logice sunt folosite pentru descrierea algoritmilor. Se pot pune n evident structuri fundamentale precum: 1. structura secvenial - format din arce conectate etichetate cu instruciuni distincte de cea de ramificare. O structur secvenial format din dou arce etichetate prin a, respectiv b se va nota prin SEQ(a,b) i are semnificaia " execut a urmat de b". 2. structuri decizionale (alternative) - conin, obligatoriu, cel puin un predicat i cel puin un bloc funcional (instruciune alta dect START sau ramificativ). Structurile decizionale sunt de urmtoarele forme: IF(p; a, b) - Dac p este adevrat, atunci execut a altfel execut b. IF0(p; a) - Dac p este verificat, atunci a.
218

CASE(p1,p2,...,pn; a1, a2, ..., an) - Dac p1 atunci a1, dac p2 atunci a2, ..., dac pn atunci an. 3. Structuri repetitive (structuri de tip ciclu, structuri iterative) - conin obligatoriu un bloc predicativ i un bloc funcional care se execut de un numr finit de ori pn cnd predicatul i schimb valoarea. Sunt posibile trei situaii: WHILE(p; a) - Ct timp condiia p este adevrata se execut a, apoi se continu cu urmtoarea instruciune. REPEAT(p; a) - Repet a pn cnd condiia p este adevrata, apoi execut instruciunea urmtoare. FOR(p; a, b, c) - Este echivalent cu SEQ(a, WHILE(p; SEQ(b, c))). Blocul a este un bloc funcional de iniializare. Blocul b descrie instruciunea ce se va executa cnd condiia p este adevrata. Blocul c (prezentat explicit n aceast structur) va descrie actualizarea strilor variabilelor programului cu rol deosebit n evaluarea condiiei p. Eticheta secvenei de mai sus este ET1, iar componentele secvenei sunt instruciuni Pascal. Secvena descrie citirea a trei numere, calculul mediei aritmetice i afiarea rezultatului. Definiie. Un algoritm exprimat n funcie de structurile SEQ, IF i WHILE se numete structurat, schema logic asociat se numete schem logic structurat, iar programul corespunztor se numete program structurat. Evident, familia algoritmilor structurai este nevid. Un rezultat foarte important afirm: Orice algoritm poate fi transformat ntr-un algoritm structurat. Aceast afirmaie are la baz teorema BohmJacopini. Pentru a transforma o schem logic (nestructurat) ntr-o schem logic structurat se poate folosi regula variabilei booleene (ce rezult din demonstraia teoremei Bohm-Jacopini). Cititorul interesat de detalii poate consulta lucrarea: Corrado Bohm, Giuseppe Jacopini - Flow-diagrams, Turing Machines and languages with only two formation rules. Comm. ACM. 9 (May, 1966), 366-371. Una din consecinele importante ale programrii structurate este eliminarea instruciunii de salt necondiionat (goto) din programe. Totui, exist situaii cnd instruciunea goto este util. Lungimea programelor nu va creste, iar claritatea algoritmului nu va avea de suferit. Important este ca numrul instruciunilor goto folosite s fie foarte mic, iar salturile s fie locale.

II.3. Noiuni introductive privind organizarea datelor Organizarea datelor, n vederea prelucrrii acestora, este un proces complex, dar de o deosebit important. De modul n care sunt structurate datele, depinde eficienta algoritmilor de prelucrare. Unele date sunt de tip simplu: data apare ca o entitate indivizibil att din punct de vedere a informaiei pe care o reprezint, ct i n raport cu unitatea central de prelucrare. Alte date sunt descrise prin componente, cu acelai tip sau cu tipuri diferite. O colecie de date pe care s-a evideniat un anumit mod de structurare i s-au stabilit procedeele de nregistrare/identificare a componentelor se va numi structur de date. Componentele unei structuri de date pot fi de tip elementar sau pot fi, la rndul lor, structuri. Identificarea unei componente se poate face prin poziia pe care o ocup n structur sau prin nume. Pentru o dat elementar trebuie specificate: un identificator, atribute (domeniul de valori, modul de reprezentare n sistemul de calcul, precizia reprezentrii) i valorile datei (pot fi enumerate sau indicate printr-o proprietate comun). Din punct de vedere al domeniului de valori asociat unei date se disting urmtoarele clase: date de tip integer (numere ntregi); date de tip real sau float (cu elemente din mulimea numerelor raionale); date de tip boolean (se refer la valorile de adevr true (adevrat), false (fals)); date de tip char (cu elemente ale mulimilor ASCII sau Unicode); date de tip string (obinute prin concatenarea datelor de tip caracter); date de tip array (structur cu componente de acelai tip ce ocup locaii succesive din memoria sistemului de calcul, identificate prin poziie); date de tip record (structur cu componente oarecare, identificate prin nume). Un mod special de organizare a datelor este ntlnit cnd avem de prelucrat liste. O list liniar este o structur de date omogen, secvenial, format din elemente aparinnd unei mulimi date. O list poate fi vid (nu are nici un element) sau plin (nu mai exist spaiu pentru stocarea unor componente suplimentare). Este foarte important s putem accesa un element al listei, s inserm sau s tergem un element etc. Listele pot fi stocate n memoria unui sistem de calcul n dou moduri: secvenial i nlnuit. Modul secvenial presupune stocarea elementelor listei n locaii succesive de memorie conform ordinii elementelor din list i reinerea adresei primului element al listei (adresa de baz). Modul nlnuit presupune c fiecare element al listei este nlocuit cu o celul format dintr-o parte de informaie (corespunztoare elementului listei) i o parte de legtura ce conine adresa celulei urmtorului element din list. Se va retine adresa de
219

baz a listei, iar ultima celul va indica, n partea de legtura, o valoare special (ce nu poate desemna o legtura). Structura de date elementar adecvat reprezentrii secveniale a listelor este tabloul unidimensional. Orice list liniar are un nceput i un sfrit pe care le numim baz, respectiv vrf. O list liniar la care inserarea i extragerea elementelor se face prin vrful listei se numete stiv (stack). O list liniar n care inserrile se efectueaz la baza listei, iar extragerile prin vrful listei se numete coad (queue). Listele liniare pot fi transformate n liste circulare considernd c legtura ultimului element indic adresa bazei listei. II.4. Limbaj algoritmic O alt modalitate de reprezentare a algoritmilor o constituie utilizarea limbajului algoritmic. Limbajul algoritmic folosete o scriere similar limbajelor de programare moderne. El permite att descrierea instruciunilor algoritmului, ct i descrierea exact a tipului datelor cu care lucreaz algoritmul. Un algoritm descris folosind limbajul algoritmic este o succesiune finit de declarri si instruciuni. Declarrile precizeaz tipul i organizarea datelor. Ele apar naintea instruciunilor ce descriu paii algoritmului. Din punct de vedere al scrierii instruciunilor, o instruciune poate ocupa mai multe rnduri sau pe un rnd pot fi scrise mai multe instruciuni. Instruciunile vor fi separate, ntre ele, folosind caracterul ';'. Cuvintele care identific un tip de date sau o instruciune, numite n continuare cuvinte cheie, apar evideniate pentru a fi deosebite de numele variabilelor. O declarare utilizeaz unul dintre cuvintele cheie integer, real, boolean, char, string, array, record, stack, queue, urmat de o list de nume de variabile. Declarrile sunt separate, ntre ele, folosind caracterul ';'. Variabilele prezente ntr-o list au tipul i organizarea precizat prin cuvntul cheie respectiv. O important deosebit o au declarrile de subprograme. n rezolvarea multor probleme apare necesitatea executrii repetate a acelorai calcule pentru date diferite. Subprogramele permit descrierea acestor calcule o singur dat. Subprogramul poate fi apelat ori de cte ori este necesar efectuarea acestor operaii. Un subprogram este identificat printr-un nume i o list de parametri. Subprogramele se mpart n proceduri i funcii. Declararea unei proceduri const n specificarea cuvntului procedure, a unui identificator al procedurii i a unei liste de declarri (ntre paranteze rotunde) ce indic informaiile ce fac obiectul transferului ntre apelant i apelat. Pentru declararea unei funcii se folosete cuvntul cheie function. Spre deosebire de proceduri, funciile ntorc obligatoriu un rezultat. De aceea, n declaraii, declararea unei funcii ncepe cu specificarea mulimii de valori ce corespunde rezultatului, a cuvntului function, a identificatorului funciei i a listei parametrilor (similar ca la o procedur). Un algoritm este complet cunoscut dac este descris i definiia subprogramelor folosite. Definiia unui subprogram presupune descrierea (prin instruciuni) modului n care se efectueaz calculele i se transmit rezultatele. Mai multe detalii prezentm n finalul acestei seciuni. Instruciunile limbajului algoritmic sunt urmtoarele: 1. Instruciunea de atribuire. Aceast instruciune are forma: v := E (atribuire simpl) sau (v1, v2, ..., vn) vi :=Ei, pentru oricare i = 1, 2, ..., n. Operaia de := (E1, E2, ..., En) ce realizeaz simultan atribuirile atribuire este permis cnd variabila v (variabilele v1, v2, ..., vn) din membru stng i expresia E (expresiile E1, E2, ..., En) sunt compatibile (se refer la aceeai clas de obiecte). O expresie conine paranteze (opional), operanzi (inclusiv apeluri de funcii) i operatori adecvai. 2. Instruciuni de intrare/ieire. Vom presupune c citirea datelor (de intrare) se face de la un mediu de intrare (de exemplu: tastatura sistemului de calcul), iar scrierea rezultatelor (de ieire) se face la un mediu de ieire (de exemplu: ecranul, imprimanta, plotterul etc.). Forma instruciunilor de intrare/ieire este: read v1, v2, ..., vn write v1, v2, ..., vn unde v1, v2, ..., vn sunt variabile de tip elementar. 3. Instruciunea repetitiv While. Aceast instruciune are forma: while p do S, unde p este un predicat, iar S este o secvena de instructiui. Deoarece instruciunile sunt separate ntre ele, folosind ';' va trebui s delimitm secvena S. Pentru aceasta se utilizeaz construcia SEQ..END prezentat mai sus. Semnificaia acestei instruciuni este aceeai ca pentru subschema logic While(p; S). 4. Instruciunea If_then_else. Aceast instruciune are forma: if p then S1 [elseS2 ], unde p este un predicat, iar S1 i S2 sunt secvene de instruciuni. Dac nendeplinirea predicatului p nu indic vreo aciune, poriunea else S2 poate lipsi, fapt reprezentat prin includerea ntre paranteze drepte, exprimarea fiind echivalent cu IF0(p; S1). Atunci cnd att S1, ct i S2 sunt aciuni prevzute, instruciunea este echivalent cu subschema logic IF(p; S1, S2). 5. Instruciunile insert i extract. Aceste instruciuni sunt necesare pentru lucrul cu liste. Acestea sunt o prelungire a instruciunii de atribuire. Dac se specific o list L, atunci insert i, L (sau L:=i) exprim
220

introducerea elementului specificat prin i n lista L, iar instruciunea extract i, L (sau i:=L) specific extragerea elementului curent din lista L i depunerea acestuia n i. 6. Instruciunea apel de procedur. Apelarea unei proceduri se face prin instruciunea apel de procedur care are una din formele: identificator_procedura sau identificator_procedura(lista de argumente) unde identificator_procedura specific o procedur declarat, iar argumentele sunt expresii separate prin virgul. Se presupune c, atunci cnd se ajunge la un apel de procedur, se stabilete corespondenta ntre argumente i parametri, i se execut toate instruciunile specificate n definiia procedurii. Dup ultima instruciune a procedurii se continu cu instruciunea urmtoare apelului de procedur. Un apel de procedur este corect atunci cnd, ntre argumente i parametri exist o concordant ca numr, tip i mod de organizare. Convenim ca atunci cnd referim o variabil (ntr-o procedur) de fapt, facem o referire la locaia de memorie corespunztoare argumentului respectiv. Spunem c se realizeaz un transfer prin referin. Sunt posibile i alte moduri de transfer, dar acestea nu sunt considerate momentan. 7. Instruciunea return. Aceast instruciune provoac prsirea corpului unui subprogram. n cazul n care cuvntul return este urmat de o expresie, valoarea expresiei este folosit ca valoare de retur a subprogramului. Instruciunea return fr valoare de retur este folosit pentru a prsi execuia unei proceduri i a reveni n unitatea de program din care a avut loc apelul; i anume la instruciunea ce urmeaz imediat acestui apel. 8. Pentru a uura descrierea algoritmilor admitem prezena, ca instruciuni ale limbajului algoritmic, a instruciunilor Repeat i For. Instruciunea Repeat este modelat de structura repetitiv REPEAT (p; S). Ea are forma: Repeat S until p; unde S este o secvena (eventual vid) de instruciuni, iar p modeleaz o expresie logic. Instruciunea For este modelat de structura iterativ FOR(p; a,b,c) i are forma simplificat For v := e1, e2, e3 do S unde: S este o secvena de instruciuni, iar expresiile e1, e2 i e3 au acelai domeniu de valori ca variabila v. n forma prezentat, instruciunea determin executarea secvenei S pentru v lund succesiv valorile e1, e1+e3, e1+2e3, ..., fr a trece dincolo de e2. Formei de mai sus i corespunde structura iterativ: FOR((v-v2)*v30; SEQ v:= e1; v1 :=e2; v3:=e3 END, S, v := v +v3). II.5. Analiza complexitii Existena unui algoritm de rezolvare a unei probleme genereaz, imediat, ntrebri ca: Mai exist un alt algoritm de rezolvare? Este algoritmul gsit "cel mai rapid"? Acest paragraf introduce noiunile fundamentale privind complexitatea algoritmilor i prezint exemple simple de algoritmi pentru care se determin complexitatea. Analiza complexitii unui algoritm presupune determinarea resurselor de care acesta are nevoie pentru a produce datele de ieire. Prin resurs nelegem timpul de executare, dar uneori este necesar s analizm i alte resurse precum: memoria intern, memoria extern etc. Modelul mainii pe care va fi executat algoritmul nu presupune existena operaiilor paralele; operaiile se execut secvenial. Timpul de executare al unui algoritm reprezint numrul de operaii primitive executate. Trebuie, pentru fiecare algoritm, s definim noiunea de operaie primitiv, independent de maina secveniala pe care se va execut algoritmul. n analiza complexitii unui algoritm avem n vedere cazul cel mai defavorabil din mai multe motive: 1. Timpul de executare n cazul cel mai defavorabil ofer o limit superioar a timpului de executare (avem certitudinea c executarea algoritmului nu va dura mai mult). 2. Situaia cea mai defavorabil este ntlnit des. 3. Timpul mediu de executare este, uneori, apropiat de timpul de executare n cazul cel mai defavorabil, dar dificil de estimat. Notaia theta este utilizat pentru a specifica faptul c o funcie este mrginita (inferior si superior). Semnificaia notaiei O este de limit superioar, n timp ce semnificaia notaiei Omega este de limit inferioar. Definiie. Fie A un algoritm, n dimensiunea datelor de intrare i T(n) timpul de executare estimat pentru algoritmul A. Se spune c algoritmul A are comportare polinomial (aparine clasei P) dac exist p>0, astfel nct T(n) = O(np).
221

Definiie. O funcie care crete mai rapid dect funcia putere xp, dar mai lent dect funcia exponeniala ax cu a>1 se spune c este cu cretere exponeniala moderat. Mai precis: f este cu cretere exponeniala moderat dac pentru oricare p>0 avem f(x) = Omega(xp) i oricare M>0 avem f(x) = o((1+ M)x). Definiie. O funcie f are cretere exponeniala dac exist a>1 astfel nct f(x) = Omega(ax) i exist x b>1 astfel nct f(x) = O(b ). Printre funciile n -> f(n), nemrginite, funciile ce cresc cel mai lent sunt, de exemplu, de forma log log n sau (log log n)1,02. Pentru n = 1000000, log log n ~ 2,6. Deci un algoritm a crui complexitate este log log n este preferabil unui algoritm (elaborat pentru rezolvarea aceleiai probleme) de complexitate log n. Algoritmii de tip polinomial sunt mai leni (creterea funciei T(n) este mai rapid) dect algoritmii de tip logaritmic. Urmeaz apoi algoritmii moderai (cu T(n) de forma nlogn etc.) i cei cu cretere exponeniala (2n, n33n etc.). Algoritmii cei mai leni sunt cei pentru care funcia T(n) este foarte rapid cresctoare (de exemplu: T(n) = n!). n informatic sunt interesani numai algoritmii care conduc la un timp de calcul cel mult moderat. Dac un algoritm necesit timp de calcul exponenial sau factorial, acesta va fi utilizat numai n cazuri excepionale. Tabelul urmtor ilustreaz cele de mai sus. n 10 100 1000 10000 n2 n
3

100 1000 1

10000 1000000 2 0.30103

1000000 3 0.47712

100000000 4 0.60206

1000000000 1000000000000

log n

log log n 0

Propoziie. Fie n i m dou numere ntregi pozitive. Algoritmul lui Euclid pentru a determina cmmdc(m, n) efectueaz cel mult [2log2M]+1 operaii de mprire ntreag, unde M = max (m, n). II.6. Elemente privind corectitudinea algoritmilor A verifica corectitudinea unui algoritm nseamn a verifica dac algoritmul conduce ntr-un interval finit de timp la obinerea soluiei corecte a problemei pentru care a fost elaborat. Vom vedea n capitolul 5 cteva metode de rezolvare a problemelor, deci de a elabora algoritmi. Metodele descrise n acest capitol se vor exemplifica pentru algoritmi simpli. Pentru aspecte suplimentare legate de corectitudinea algoritmilor se poate folosi lucrarea: Tudor Blnescu. Corectitudinea algoritmilor. Editura Tehnic, Bucureti, 1995. Notaie. Construcia {P}A{Q}, numit i formul de corectitudine total conine urmtoarele elemente: P - comentariu care descrie proprietile datelor de intrare (precondiia); A - algoritmul (secvena de instruciuni) supus analizei; Q - comentariu care descrie proprietile datelor de ieire (postcondiia). Definiie. Un algoritm {P}A{Q} este corect cnd propoziia urmtoare este valid: Dac datele de intrare satisfac precondiia P Atunci 1) executarea lui A se termin (ntr-un interval finit de timp) i 2) datele de ieire satisfac postcondiia Q. Folosind elementele fundamentale ale logicii matematice rezult c urmtoarele observaii sunt adevrate: 1. Algoritmul {false}A {Q} este corect, oricare ar fi A i Q. 2. Algoritmul {P}A{true} este corect dac i numai dac executarea lui A se termin, atunci cnd datele iniiale satisfac proprietatea P. 3. Dac {P}A{Q} i {R}A{Q} sunt formule corecte, atunci {P v R}A {Q} este formul corect. Pentru a stabili corectitudinea algoritmilor compleci se procedeaz la descompunerea acestora n elemente simple a cror corectitudine se analizeaz. n continuare vor fi prezentate reguli pentru a analiza corectitudinea unor astfel de algoritmi. Pentru o formalizarea avansat a acestor reguli, cititorul interesat poate parcurge lucrarea: C. A. R. Hoare et al. Laws of Programming. Comm. ACM. 30(8), 1987, 672-687. Regula compunerii secveniale (CS): Dac A este de forma SEQ B; C END, atunci a verifica formula {P}A{Q}, revine la a verifica formulele {P}B{R} i {R}C{Q}, unde R este un predicat asupra datelor intermediare.
222

Este evident c regula CS poate fi aplicat iterativ. Mai precis, dac A este de forma SEQ A1; A2; ..., An END, atunci obinem regula compunerii secveniale generale: &t9;CSG: Dac {P0} A1 {P1}, {P1} A2 {P2}, ...., {Pn-2}An-1{Pn-1} si {Pn-1}An{Pn} sunt algoritmi coreci, atunci {P0}A{Pn} este algoritm corect. Regula implicaiei (I): Aceast regul este util n cazul algoritmilor ce urmeaz a fi aplicai n condiii mai tari dect pentru cele care au fost deja verificai. O proprietate P este mai tare dect proprietatea Q dac este adevrat propoziia compus: P -> Q. Regula implicaiei are urmtoarea form: I: Dac{P} A {Q} este algoritm corect, P1 -> P si Q -> Q1, atunci {P1} A {Q1} este algoritm corect. Regula instruciunii de atribuire (A): Fie notaiile: x e Def(e) Variabil simpl Expresie; Proprietatea satisfcut de acele elemente pentru care evaluarea expresiei e este corect (Exemplu: pentru integer array b(10), Def(b(i)):= (i=1, 2, ..., 10)); Formul n care apare variabila x; Formula obinut din P(x) prin substituirea variabilei simple x cu expresia e, ori de cte ori x este variabil liber n P, iar dac e conine o variabil y care apare legat n P, nainte de substituie variabila y se nlocuiete printr-o nou variabil (care nu mai apare n e).

P(x) P(x/e)

Valoarea de adevr a propoziiei P -> Q(x/e) nu se schimb dac n Q(x/e) se efectueaz substituia descris de atribuirea x := e. Regula atribuirii este deci: A: Dac P -> (Def(e) and Q(x/e)) atunci algoritmul {P} x:=e {Q} este corect. Regula instruciunii if (IF si IFR): Dac c este o expresie boolean i A i B sunt algoritmi, pentru cele dou forme ale instruciunii if sunt valabile regulile de corectitudine: IF: Dac {P and c} A {Q}, {P and not c} B {Q} sunt corecte, iar P -> Def(c) este adevrata Atunci formula {P} if c then A else B {Q} este corect. IFR: Dac {P and c} A {Q} este corect, iar P and (not c) -> Q si P -> Def(c) sunt adevrate Atunci {P} if c then {Q} este formul corect. Se poate observa c aplicarea regulilor instruciunii de decizie nu este n sine dificil corectitudinea acestei instruciuni se reduce la corectitudinea instruciunilor componente. Regula instruciunii while (W): Regula instruciunii while trebuie s precizeze dac nu apare fenomenul de ciclare, iar prelucrrile sunt corecte (n paranteze rotunde apare descrierea formal). Fie algoritmul {P} A; while c do S {Q}. Presupunem c exist o proprietate invariant I (vezi mai jos) i o funcie de terminare t cu valori numere ntregi care satisfac urmtoarele condiii: Cnd I este adevrata atunci expresia boolean c este bine definit (adic I -> Def(c)). Proprietatea I rezult prin executarea secvenei A (adic, {P} A {I} este algoritm corect). La terminarea instruciunii while, proprietatea final Q poate fi dedus (adic, I and (not C) -> Q). I este proprietate invariant la executarea unei iteraii: dac I este adevrat nainte de executarea secvenei S i expresia boolean c este adevrat, atunci executarea secvenei S se termin ntr-un interval finit de timp i I este adevrata la sfrit (adic, {I and c} S {I} este algoritm corect).
223

Dac rezultatul evalurii expresiei c este true i proprietatea I este adevrata, atunci exist cel puin o iteraie de efectuat (adic, I and c -> (t > =1)). Valoarea lui t descrete dup executarea unei iteraii (adic, {(I and c) and (t=a)} S {t < a}). n aceste condiii, algoritmul considerat este corect.

III. LIMBAJE DE PROGRAMARE


III.1. Vocabularul i sintaxa limbajelor de programare Vocabularul unui limbaj de programare este format din cele mai simple elemente cu semnificaie lingvistic numite entiti lexicale sau tokens. Elementele vocabularului sunt alctuite din caractere Unicode (care constituie alfabetul limbajului). Standardul Unicode conine ca subset codul ASCII, dar reprezentarea intern a caracterelor Unicode folosete 16 bii. Cele mai utilizate simboluri sunt: literele mari i mici ale alfabetului englez, cifrele sistemului zecimal, diferite semne speciale. Unitile lexicale sunt separate, ntre ele, prin comentarii i spatii. Pentru aproape toate limbajele de programare se pot evidenia uniti lexicale precum: cuvinte cheie, identificatori, literali, separatori i operatori. Cuvintele cheie sunt secvene de caractere ASCII rezervate (nu pot avea alt semnificaie) utilizate pentru definirea unitilor sintactice fundamentale. Pentru exemplificare ne referim la limbajele de programare Pascal si Java: 1. Cuvinte cheie Pascal: absolute, and, array, begin, case, const, div, do, downto, else, end, external, file, for, forward, function, goto, if, implementation, in, inline, interface, interrupt, label, mod, nil, not, of, or, packed, procedure, program, record, repeat, set, shl, shr, string, then, to, type, unit, until, uses, var, while, with, xor. 2. Cuvinte cheie Java: abstract, boolean, break, byte, case, cast, catch, char, class, const, continue, default, do, double, else, extends, final, finally, float, for, future, generic, goto, if, implements, import, inner, instanceof, int, interface, long, native, new, null, operator, outer, package, private, protected, public, rest, return, short, static, super, switch, synchronized, this, throw, throws, transient, try, var, void, volatile, while, byvalue. Cuvintele cheie subliniate sunt prezente i n limbajul C alturi de altele. Identificatorii sunt secvene, teoretic nelimitate, de litere i cifre Unicode, ncepnd cu o liter sau liniua de subliniere (n limbajul C). Identificatorii nu pot fi identici cu cuvintele rezervate. Literalii ne permit introducerea valorilor pe care le pot lua tipurile de date primitive i tipul ir de caractere. Mai precis, lieralii sunt constante ntregi, flotante, booleene, caracter i, iruri de caractere. Literalii ntregi, n general, pot fi descrii prin reprezentri n una din bazele de numeraie: 10, 16 sau 8. Lungimea reprezentrii interne depinde de implementarea limbajului. De exemplu, n limbajul Pascal, un literal ntreg, cu semn, este reprezentat pe 16 bii, descrierea sa n baza 16 fiind o secvena a simbolurilor asociate reprezentrii numrului ntreg n baza 16 avnd prefixul $. Literalii flotani reprezint numere raionale. Ei sunt formai din urmtoarele elemente: partea ntreag, partea fracionar i exponent. Exponentul, dac exist, este introdus de litera E sau e urmat opional de un semn al exponentului. Literalii booleeni sunt TRUE si FALSE, primul reprezentnd valoarea boolean de adevr, iar cellalt valoarea boolean de fals. Dei TRUE si FALSE nu sunt cuvinte rezervate, acestea nu pot fi folosite ca identificatori (n Pascal, Java). Literalii caracter sunt folosii pentru a desemna caracterele codului Unicode (sau ASCII, acolo unde este cazul). Descrierea unui literal caracter se fie folosind o liter, fie o secvena special. Secvenele speciale (numite secvene escape n C, C++ i Java) permit specificarea caracterelor fr reprezentare grafic precum i a unor caractere speciale. Caracterele ce au reprezentare grafic pot fi descrise ntre apostrofuri, ca n exemplele: 'P', 'A', '3', '!'. Secvenele speciale se descriu diferit de la un limbaj la altul. Vom exemplifica folosind limbajele Pascal i Java. Secvene speciale n limbajul Pascal: 1) Un ntreg din domeniul 0, ..., 255 precedat de simbolul # desemneaz un caracter dat prin codul su ASCII. 2) Un caracter imprimabil precedat de semnul ^ desemneaz un "caracter de control" avnd codul ASCII n domeniul 0, ..., 31. Secvenele escape n limbajul Java se descriu folosind apostrofuri, semnul \, litere i cifre. Vom exemplifica indicnd cteva secvene predefinite: '\b' (backspace = #8), '\n' (linefeed), '\r' (carriage return) etc. Un literal ir de caractere este constituit din zero sau mai multe caractere ntre delimitatori. Secvena de caractere ce formeaz irul poate conine att caractere imprimabile, ct i secvene speciale. n limbajul Pascal se utilizeaz apostroful ca delimitator, iar n limbajul C( C++, Java) ghilimelele.
224

Separatorii sunt caractere ce indic sfritul unui token i nceputul altuia. Acetia particip i la construcia sintaxei limbajelor de programare. Ei nu trebuie confundai cu spatiile care, i acestea, separ uniti lexicale distincte. Separatorii sunt necesari cnd unitile lexicale diferite sunt scrise fr spatii ntre ele. Cei mai ntlnii separatori sunt: ( ) { } [ ] ; , . Exemple: x[10], f(x,y), carte.autor etc. Operatorii sunt simboluri grafice ce desemneaz operaiile definite de un limbaj de programare. n unele limbaje de programare este posibil redefinirea operatorilor, acelai simbol fiind utilizat pentru operaii diferite ce rezult din contextul n care apar. Lista minimal a operatorilor aritmetici include: +(adunare), -(scdere), /(mprire), *(nmulire). Mai sunt admise i operaii precum: % (C, Java) sau mod (Pascal, Modula), div (mprire ntreag n limbajul Pascal). Ali operatori sunt: operatori logici, operatori relaionali, operatori asupra irurilor de caractere etc. Toi operatorii pot fi privii i ca separatori. O construcie aparte utilizat n programe pentru explicarea sau documentarea textului programului este comentariul. Comentariile sunt delimitate de textul programului folosind anumii delimitatori. n limbajul Pascal, un comentariu este scris ntre acoladele { } sau ntre secvenele (*, *). Programele C++, Java pot conine comentarii pe o singur linie ;i ncep cu //, sau pe mai multe linii ;i sunt cuprinse ntre /* si */. Alte elemente lexicale ce pot fi prezente ntr-un program sunt etichetele ;i clauzele. Etichetele sunt iruri de cifre zecimale/hexazecimale sau identificatori folosite n legtura cu o instruciune de salt (goto) pentru marcarea unor instruciuni. Clauzele (numite ;i directive) sunt cuvinte cheie ce desemneaz instruciuni cu efect n timpul compilrii. Prin sintaxa unui limbaj de programare se nelege, n general, un ansamblu de reguli privind agregarea unitilor lexicale pentru a forma structuri mai complexe (declaraii, instruciuni, module, programe etc.). Prezentarea acestor reguli se poate folosind limbajul natural sau mecanisme formalizate. Descrierea sintaxei n limbaj natural poate conduce la neclariti sau specificaii incomplete. Cu ajutorul mecanismelor formale sintaxa unui limbaj este complet specificat. Cea mai folosit notaie este cunoscut sub numele de notaie BNF (Backus-Naum-Form) ;i a fost folosit pentru prima dat, n anul 1959, la specificarea sintaxei limbajului Algol-60. Aceast notaie are aceeai putere generativ cu gramaticile independente de context introduse de N. Chomsky. Totui, limbajele de programare nu sunt independente de context, ci numai poriuni ale acestora pot fi modelate cu ajutorul limbajelor independente de context. Pentru a putea specifica un ntreg limbaj de programare se poate folosi notaia BNF extins. n prezentarea din acest capitol vom utiliza opt metasimboluri: ::= < > { } [ ] | pentru a defini unitile sintactice ale limbajului Pascal. Metasimbolurile < i > sunt folosite pentru delimitarea numelui unei uniti sintactice. Presupunem, de asemenea, existenta unei operaii de concatenare pe mulimea unitilor sintactice. Metasimbolul ::= apare dup numele unei uniti sintactice i are semnificaia "se definete prin". Metasimbolul | este utilizat pentru a delimita mai multe variante de definire ale unei uniti sintactice, aceasta fiind obinuta prin reuniunea variantelor. Metasimbolurile { i } indic repetarea posibil (de zero sau mai multe ori) a simbolurilor pe care le delimiteaz. Pentru a desemna prezenta opional a unor simboluri se utilizeaz, ca delimitatori, metasimbolurile [ i ]. Vom admite, pentru prescurtare metasimbolul ... care indic continuarea unui ir de valori conform contextului n care apare. Iat cteva exemple: 1. <liter> ::= A ... Z descrie mulimea literelor mari; 2. <cifr> ::= 0 ... 9 descrie mulimea cifrelor zecimale; 3. <identificator> ::= <liter> { <liter> | <cifr>} descrie modul de formare a identificatorilor: un ir de litere i/sau cifre, primul semn fiind o liter. 4. <secvena cifre> ::= <cifr> { <cifr>} descrie modul de formare a unei secvene de cifre; 5. <ntreg fr semn> ::= <secvena cifre> definete un numr ntreg fr semn; 6. <semn> ::= + | 7. <ntreg cu semn>::= [ <semn><ntreg fr semn> spune c un ntreg cu semn este un ntreg fr semn precedat de un semn: + sau -. Prin diagrame sintactice se realizeaz o reprezentare grafic a modului de agregare a unitilor sintactice. n cele ce urmeaz vom prefera limbajul natural (n anumite cazuri) i notaia BNF extins (n alte cazuri), cititorul interesat asupra diagramelor sintactice poate consulta, de exemplu: N. Wirth: Systematic Programming: An introduction, Prentice Hall, 1972. III.2. Tipuri de date. Constante. Variabile. Expresii Un tip de date este o structur compus din: 1) o mulime X de valori numite date i 2) o mulime de legi de compoziie pe X (operaii ce se pot efectua cu valori din X). O dat are un singur tip (aparine unei singure mulimi). Exist limbaje de programare puternic tipizate (n sensul verificrii cu regularitate a apartenei unei date la mulimea de valori a tipului su, nc din faza de compilare). Astfel de limbaje de programare sunt: Pascal, Modula, Ada etc.
225

Tipurile de date sunt standard sau definite de utilizator. Tipurile definite de utilizator se introduc prin intermediul unei definiii folosind un cuvnt cheie precum type (n Pascal), typedef (n C) sau class (n limbajele C++ i Java). De asemenea, se vor utiliza diverse cuvinte cheie pentru a specifica structura tipului. Dac pentru o anumit structur a unui tip nu este stabilit un identificator, spunem c avem de-a face cu un tip anonim. Valorile unui tip de date (elementele mulimii X sunt referite fie prin variabile, fie prin constante (literali sau constante simbolice). O locaie de memorie care poate stoca o valoare a unui anumit tip de date se numete, prin abuz de limbaj, variabil. Orice variabil trebuie s fie declarat pentru a putea fi folosit. O declaraie conine un tip de valori - ce indic: ce se stocheaz, cum se stocheaz i n ce operaii intervin valorile stocate - i un identificator pentru a ne referi la variabila ca obiectul declaraiei. Practic o variabil este un obiect caracterizat de tip, adres i valoare, pentru care atributul valoare poate fi modificat. Operaiile cu elemente ale unui tip sunt fie predefinite, fie sunt introduse prin declaraii function sau procedure (n Pascal) sau operator (n C++). Agregarea variabilelor, constantelor i a operatorilor conduce la construcii numite expresii. Expresiile sunt evaluate n cursul executrii unui program. Rezultatul unei expresii depinde de valorile variabilelor n momentul evalurii. Tipurile de date ntlnite n limbajele de programare actuale sunt clasificate n: tipuri de date simple; tipuri de date structurate, tipuri referin (pointer), tipuri procedurale. n limbajele C, C++ i Java exist tipul void. Aceast mulime notat prin void nseamn fie mulimea vid, fie o mulime neprecizat. Tipurile de date simple numite i tipuri primitive (sau tipuri standard) se refer la mulimi de elemente precum: numere ntregi, numere raionale, valori de adevr (logice sau booleene), caractere, valori aparinnd unei enumerri sau unui interval (subdomeniu). O parte dintre tipurile simple sunt tipuri ordinale, adic tipuri caracterizate printr-o mulime finit de valori, pe care este definit o ordine liniar i, prin urmare, pentru orice element al unei asemenea mulimi se stabilete numrul de ordine ord(.), elementul predecesor pred(.) i cel succesor succ(.). Tipurile ordinale sunt cele care se refer la mulimi precum: mulimea numerelor ntregi, mulimea valorilor de adevr, mulimea caracterelor, mulimea valorilor unei enumerri, mulimea valorilor dintr-un subdomeniu al uneia dintre mulimile anterioare. Tipurile raionale (simpl precizie, dubl precizie, precizie extins etc.) nu sunt considerate tipuri ordinale, dei sunt tot mulimi finite de elemente. Trebuie observat c metoda de reprezentare n memoria calculatorului a numerelor raionale ar permite considerarea unei ordini liniare i, elementele unei astfel de mulimi ar avea un numr de ordine. Operatorii sunt simboluri care specific operaii efectuate asupra unor variabile sau constante denumite operanzi. Combinaiile valide de operanzi i operatorii reprezint expresii. Limbajele de programare ofer o multitudine de operatori: operator de atribuire (simbolizat prin := sau =), operatori aritmetici unari (utilizarea semnului, incrementare, decrementare), operatori aritmetici binari (adunare, scdere, nmulire, mprire, obinerea ctului i restului mpririi a dou numere ntregi), operatori logici (i, sau, negare), operatori relaionali (egal, diferit, mai mic, mai mic sau egal, mai mare, mai mare sau egal, aparine), operatori la nivel de bit (i, sau, negare, sau exclusiv, deplasare stnga, deplasare dreapta), operatori combinai (n limbajele C, C++ i Java), operatori asupra mulimilor (reuniune, intersecie, diferen), operatori asupra irurilor de caractere (concatenare) precum i ali operatori. Evaluarea expresiilor trebuie s tin seama de poziia parantezelor i de proprietile operatorilor (precedent, asociativitate, conversii implicite n cazul tipurilor compatibile, conversii explicite). Precedenta stabilete ordinea de evaluare a operaiilor n cazul expresiilor care conin mai muli operatori diferii. Dac ntr-o expresie se ntlnesc operaii cu aceeai precedent, atunci ordinea de evaluare este dat de tipul asociativitii (de la stnga la dreapta sau de la dreapta la stnga). Cnd ntr-o expresie apar operaii cu operanzi de tipuri diferite, nainte de efectuarea operaiei are loc un proces de conversie implicit (cnd nu se semnaleaz explicit i nu apare fenomenul de incompatibiltate) prin care tipul cu cardinalul mai mic este promovat ctre tipul mai bogat (se presupune aici c natura elementelor celor dou tipuri este aceeai). III.3. Programare n Turbo Pascal Limbajul Pascal a fost definit de profesorul Niklaus Wirth (Universitatea Tehnic din Zrich, Elvetia) n anul 1971. Exist foarte multe implementri ale acestui limbaj, cea mai popular aparine firmei Borland International i este destinat calculatoarelor compatibile IBM-PC. Structura unui program Turbo Pascal conine o parte declarativ i o parte principal. Partea declarativ conine o descriere a datelor ce vor fi prelucrate pe calculator, ct si o descriere a operaiilor ce se pot efectua asupra datelor. Partea principal (numit i corpul programului) descrie secvena de instruciuni ce se execut n momentul lansrii programului pentru executare. Aciunile (reprezentate de instruciuni) asupra datelor efective sunt descrise n partea principal. Partea declarativ indic un antet, o linie uses, una
226

sau mai multe declaraii (etichete (label), tipuri de date (type), constante (const) i variabile (var)) precum i una sau mai multe declaraii i definiii de subprograme. III.3.1. Instruciuni Pascal Instruciunile limbajului Turbo Pascal sunt grupate n dou clase: instruciuni simple i instruciuni structurate. Aciunile simple sunt: instruciunea de atribuire, instruciunea apel de procedur, instruciunea de transfer necondiionat (goto) i instruciunea vid (cu efect nul). Aciunile structurate sunt reprezentate de: instruciunea compus, instruciunile iterative (for, while, repeat), instruciuni condiionale (if, case) i instruciunea with (n contextul prelucrrii datelor de tip record). Din punct de vedere sintactic, o instruciune este alctuita dintr-o etichet (opional) pentru a putea fi referit de alte instruciuni urmat de instruciunea propriu-zis (care descrie aciunea realizat n momentul executrii sale). Instruciunea de atribuire, n notaia BNF, are forma descris prin: <instruciune de atribuire> ::= <variabil> := <expresie> unde <variabil> i rezultatul dat de <expresie> trebuie s fie de tipuri identice, sau tipul uneia s fie un subdomeniu al celeilalte, sau ambele trebuie s fie subdomenii ale aceluiai tip. Se accept, ca o excepie, cazul n care <variabil> are tipul real, iar <expresie> conduce la un rezultat de tip ntreg. Instruciunea apel de procedur se insereaz n program n locul n care se dorete executarea instruciunilor specificate de o declaraie de procedur asupra unor date efective transmise procedurii n locul de apel. Instruciunea write('Apel!'); apeleaz procedura write pentru irul 'Apel !'. O procedur cu antetul procedure PROC(a, b, c:real; var s:real) se poate apela n mai multe moduri. Urmtoarele apeluri sunt corecte: PROC(3.0, 4.0, 5.0, S); PROC(L1,L2,L3,ARIA); PROC(2.0, Y, Z, A); Apelul PROC(X,Y,Z,45.0); nu este un apel corect deoarece al patrulea parametru nu poate fi o constant. Instruciunea de transfer necondiionat, transfer controlul execuiei programului n alt loc din textul surs implementnd mecanismul "Mergi la pasul ". Instruciunea ctre care se realizeaz transferul trebuie s fie una etichetat Exist anumite restricii privind utilizarea instruciunii goto (se transfer controlul n acelai bloc de instruciuni redat prin begin ... end sau repeat ... until; transferul ctre o instruciune din interiorul unei instruciuni compuse este permis, dar nu este recomandabil). Instruciunea de efect nul (instruciunea vid), nu are efect asupra variabilelor programului (nu schimb starea programului). Ea nu este redat printr-un cuvnt cheie, dar deoarece instruciunile Pascal sunt separate prin delimitatorul ";", prezenta acesteia este marcat de apariia acestui delimitator. n exemplul: begin i:=i+1; ; write(i); end instruciunea cu efect nul apare ntre cei doi delimitatori "; ;" i ntre "; end". Instruciunea compus. Cnd ntr-o instruciune este necesar specificarea unei alte instruciuni, se poate utiliza instruciunea compus indicat prin secvena begin ... end. Instruciunile dintre begin i end se execut n ordinea n care apar. n secvena de mai jos, i:=1; while <=n do begin read(x); s:=s+x; i:=i+1 end; pentru fiecare i pentru care este adevrat condiia i<=N se vor executa instruciunile dintre begin i end. Instruciuni iterative (repetitive). De foarte multe ori, un algoritm exprim execuia de zero sau mai multe ori a unui grup de operaii. Pentru specificarea acestui aspect se utilizeaz iteraia (ciclul sau bucla) concretizat ntr-o instruciune iterativ. Conform teoremei fundamentale a programrii structurate, instruciunea repetitiv cu test iniial (while) este suficient pentru exprimarea oricrui algoritm, deci i a oricrui program pentru calculator. Totui, pentru uzul programatorilor, limbajele de programare includ i alte construcii. Instruciunile repetitive Pascal sunt: instruciunea while, instruciunea repeat (ciclul cu test final) i instruciunea for (ciclul cu contor). Instruciunea while are forma While C do S; unde C este o expresie logic, iar S este o instruciune. Dac S exprim mai multe operaii, atunci reprezint o instruciune compus. Instruciunea S se execut ct timp expresia logic C este adevrat. Dac expresia logic C este fals execuia continu cu urmtoarea instruciune (imediat dup while). Dac de la nceput expresia C are valoarea false, atunci instruciunea S nu se execut. Trebuie observat c blocul S trebuie s asigure o schimbare, n timp, a valorii de adevr a condiiei C; altfel se obine o bucl infinit. Astfel, execuia secvenei: i:=1; While i<10 do read(x); va continua indefinit. Pentru a asigura terminarea ciclului trebuie s modificm valoarea variabilei i. Secvena corect este:
227

i:=1; while < 10 do begin read(x); i:=i+1 end; Alte construcii interesante sunt: While true do I; (o bucl infinit) i While false do I; (instruciune de efect nul - secvena I nu se execut niciodat). Instruciunea repeat are forma repeat S until C; unde S este o secvena de instruciuni (nu neaprat incluse ntr-o instruciune compus), iar C este o expresie logic. Secvena S se execut cel puin o dat, execuia ei continund pn cnd condiia C este ndeplinit (valoarea de adevr a expresiei C este true). Practic, n loc de repeat S until C; putem scrie S; while (not C) do S; Instruciunea for are una din formele: for v:=i to f do S sau for v:=i downto f do S. Variabila v, numit contor, trebuie s fie de tip ordinal (pe mulimea de definiie trebuie s aib sens funciile succ(esor) i pred(ecesor)), iar i i f trebuie s fie expresii compatibile cu tipul variabile v. Forma for v:=i to f do S este echivalent cu secvena: v:=i; while v<= f do begin S; v := succ(v) end; iar forma for v:=i downto f do S este echivalent cu secvena: v:=i; while v>=f do begin S; v:=pred(v) end; Valoarea final a variabilei v se consider a fi nedefinit dup terminarea normal a instruciunii for. Instruciuni condiionale (instruciuni de decizie). O instruciune condiional selecteaz o singur instruciune dintre mai multe alternative posibile. Limbajul Pascal, alturi de instruciunea if, permite i utilizarea unei instruciuni de selecie multipl: instruciunea case. Instruciunea if are una din forma: if C then S1 sau forma if C then S1 else S2. Pentru oricare dintre forme, iniial se evalueaz expresia logic C. Dac valoarea de adevr a acestei condiii este true se va executa secvena de pe ramura then (S1), altfel n primul caz se execut instruciunea de efect nul, iar n al doilea caz se execut secvena de pe ramura else (S2). Instruciunea case utilizeaz o expresie numit selector i o list de instruciuni, fiecare element din list fiind precedat de o constant cu un subdomeniu de valori din mulimea de baz a selectorului. Conform notaiei BNF, forma instruciunii case este: <instruciunea case> ::= case <selector> of [ <element case> {; <element case>}] [else <instructine>][;] end <element case> ::= <constant> {<constant>} <constant1> .. <constant2>: <instruciune> Expresia ce definete selectorul are valori ordinale cuprinse ntre 32768 i 32767. Instruciunea with are forma general: with <variabil record>{ ,<variabil record>} do <instruciune> unde: lista dintre cuvintele rezervate with i do conine identificatorii variabilelor de tip nregistrare (record) crora li se aplic instruciunea ce apare dup cuvntul do. Acest instruciune simplific modul de referire la componentele nregistrrilor. n <instruciune> se folosesc numai identificatorii cmpurilor nregistrrilor specificate n lista dintre with i do. III.3.2. Subprograme Pascal Proceduri Pascal. Elementele introduse anterior: tip de date, variabil, expresie, instruciune permit codificarea riguroas a prelucrrilor de date sub form de programe. Noiunile de expresie i instruciune pot fi generalizate prin intermediul subprogramelor (funcii i proceduri). Se poate introduce tipul subprogram (numit i tip procedural). Declaraia unui subprogram Pascal asociaz un identificator unui bloc de instruciuni precedat, eventual, de o secvena declarativ. Identificatorul subprogramului poate fi urmat de o list de parametrii formali. Identificatorul unei proceduri (urmat, eventual, de o list a parametrilor efectivi) poate fi folosit prin intermediul instruciunii apel de procedur. Identificatorul unei funcii urmat, eventual, de o list a argumentelor efective, poate fi folosit n expresii sau n orice loc unde poate fi prezent o expresie. Declararea unei proceduri Pascal se realizeaz conform urm-toarelor reguli: <procedur> ::= <antet_procedur>;<corp> <antet_procedur> ::= procedure <identificator> [ (<list_parametrii>)]; <corp> ::= [ interrupt ; | near ; | far ;] (bloc | forward; | external;| directiv INLINE | directiv ASM )
228

Instruciunile ce se execut la activarea unei proceduri (n general, a unui subprogram) sunt descrise n blocul subprogramului, n liniile INLINE sau n blocul ASM. Funcii Pascal. Declararea unei funcii Pascal se supune regulilor urmtoare: <funcie> ::= <antet>; <corp> <antet> ::= = function <identificator> [ (list_parametrii_formali) ] : <tip_rezultat>; <corp>::= [ near ;| far ;] (bloc | forward ;| external ;| directiv INLINE | directiv ASM) Tipul rezultatului returnat este fie un nume de tip simplu, fie string. Un subprogram function este activat printr-un apel de funcie. Apelul de funcie const din identificatorul funciei urmat, eventual, de lista argumentelor funciei, ntre paranteze. Un apel de funcie apare ca un operand ntr-o expresie. La evaluarea expresiei, se apeleaz i se execut subprogramul function, iar valoarea operandului devine valoarea returnat de funcie. Zona de instruciuni a corpului specific instruciunile ce urmeaz a-tilde; a fi executate la apelul funciei. Acest bloc trebuie s conin cel puin o instruciune de atribuire care asociaz o valoare identificatorului funciei. Rezultatul ntors este ultima valoare asociat. Cnd o astfel de instruciune lipsete, valoarea returnat de funciei nu este precizat. Comunicarea ntre uniti de program. Comunicarea ntre programul principal i subprograme, precum si comunicarea ntre subprograme se poate face prin entiti globale i prin parametri. Comunicarea prin intermediul parametrilor permite tratarea subprogramelor ca pe nite blocuri specializate avnd un anumit numr de linii de intrare i un anumit numr de linii de ieire. Aceast comunicarea este posibil prin specificarea listei parametrilor la declararea i definirea unui subprogram. La execuia apelului subprogramului valorile parametrilor de apel sunt substituite parametrilor formali (blocul primete semnale concrete) astfel c aciunile instruciunilor subprogramului au loc asupra acestor valori. Exist trei categorii de parametri formali: parametri valoare, parametri variabil, parametri fr tip. Din punct de vedere sintactic, declararea parametrilor formali urmeaz regulile: <list_parametri_formali>::= (<declaratie_parametru>{ ; <declaratie_parametru>} ) <declaratie_parametru> ::= <list_identificatori> : <tip> | var <list_identificatori> : <tip> | var <list_identificatori> <tip> ::= <tip_simplu> | string | file Un grup de identificatori neprecedai de var, dar urmai de un tip reprezint parametri valoare. Un parametru valoare, pentru subprogram, se comport ca o variabil local, cu excepia faptului c la execuie va fi iniializat cu valoarea parametrului actual corespunztor. Orice schimbare asupra unui parametru valoare nu va afecta valoarea parametrului actual. Un parametru actual corespunztor unui parametru valoare, ntr-un apel de subprogram, trebuie s fie o expresie, nu de tip file i nici de tip structurat altul dect string. Parametrul actual i cel formal trebuie s aib tipuri compatibile. Un grup de parametri precedai de var i urmai de un identificator de tip reprezint parametri variabil. Un parametru variabil este utilizat atunci cnd subprogramul trebuie s furnizeze valori apelantului. Parametrul actual, ntr-un apel de subprogram, trebuie s fie declarat ca variabil n unitatea apelant sau mai sus, cnd este vorba de un identificator global. Parametrul formal de tip variabil, n timpul unui apel, reprezint chiar parametrul actual, deci orice modificare asupra parametrului formal se va reflecta asupra parametrului actual. Parametrul formal i parametrul actual corespunztor trebuie s fie de acelai tip. Fiierele pot fi transmise numai ca parametrii variabil. Un grup de parametri precedai de var, dar neurmai de tip reprezint parametrii fr tip. Cu ajutorul acestora se pot realiza prelucrri generice. Parametrul actual corespunztor unui parametru fr tip trebuie s fie declarat n unitatea apelant sau mai sus ca variabil. Utilizarea unui parametru fr tip, n subprogram, presupune o conversie de tip pentru a suporta corespondenta cu parametrul actual. Tipuri procedurale. Extinznd limbajul Pascal standard, Borland Pascal permite ca subprogramele s fie tratate ca elemente ale unor mulimi de subprograme, deci se pot definii tipuri de date asociate subprogramelor. Un astfel de tip de date se numete tip procedural. Odat definit un tip procedural, putem declara variabile de acest tip - numite variabile procedurale, iar aceste variabile pot fi folosite n atribuiri, cu operatorul =, cu operatorul <> i ca parametri n apelurile de subprograme. Declaraia unui tip procedural include parametri, iar pentru funcii, i tipul rezultatului. Regulile avute n vedere la o astfel de declarare sunt descrise prin: <tip_procedural> ::= <tip_procedure>| <tip_function> <tip_procedure> ::= procedure;| procedure (<list_parametri_formal>); <tip_function> := function : <tip_rezultat>;|
229

function (<list_parametri_formali>):<tip_rezultat>; Identificatorii parametrilor n <list_parametri_formali> sunt pur decorativi, ei nu au o semnificaie deosebit n partea declarativ. Exemple: type proc = procedure; proc2int = procedure(var a, b:integer); procstr = procedure(var s:string); funcmat = function(x: real):real; funcmat2 = function(x, y: real):real; Eqfunc = function (a, b: real; f:funcmat): real; Nu putem scrie subprograme de tip funcie care s returneze variabile procedurale. Funciile pot returna valori din urmtoarele categorii: string, real i variantele, integer si variantele, char, boolean, pointer (referin) i enumerare (definit de utilizator). O variabil procedural se declar ca orice alt variabil. III.4. Programare n C Limbajul C a fost creat la AT & T Bell Laboratories n anul 1972 de Dennis Ritchie. Versiunea standard a limbajului C pn n anul 1988 a fost cea furnizata odat cu sistemul de operare UNIX i descris n [14]. n anul 1983 a nceput redactarea standardului ANSI pentru limbajul C. Standardul ANSI a fost finalizat n anul 1990. III.4.1. Structura programelor C n limbajul C programul este o colecie de module distincte numite funcii, organizate n una sau mai multe uniti de translatare. Fiecare unitate de translatare poate fi compilat (analizat lexical i sintactic) separat. O unitate de translatare trebuie s conin cel puin o declaraie sau o definiie de funcie. Ea const din fiierul surs mpreuna cu oricare fiier antet i fiiere surs incluse prin directiva #include. O unitate de translatare, prin compilare, conduce la un fiier obiect (.obj) relocabil. Directivele precedate de delimitatorul # se numesc directive preprocesor, acestea specificnd operaii anterioare procesului de compilare ce sunt efectuate de o componenta a mediului de programare numit preprocesor. O declaraie C specific atributele unui identificator sau mulime de identificatori. Regulile sintactice ce stau la baza scrierii declaraiilor sunt redate prin: <declaraie> ::= <specificator declaraie> [ <lista declaratori de iniializare> ] ; <specificator declaraie> ::= <clasa de memorare> [ <specificator declaraie> ] | <specificator tip> [ <specificator declaraie> ] | <calificator tip> [ <specificator declaraie> ] <lista declaratori de iniializate> ::= < declarator iniializare> | <lista declaratori iniializare>, <declarator iniializare> <declarator iniializare> ::= <declarator> | <declarator> = <iniializare> A face o declaraie nu presupune i alocarea memoriei pentru identificatorul declarat. Exista situaii cnd alocarea se realizeaz n alt unitate de translatare (cazul datelor externe). Declaraia unui identificator asociaz numelui n mod explicit sau implicit o serie de atribute din mulimea urmtoare: Clasa de memorare - localizeaz zona de memorie n care este plasat elementul declarat (zona de date, un registru al procesorului, stiva mediului de programare, zona de alocare dinamic) i delimiteaz durata alocrii (ntreg timpul de executare a programului, executarea unei funcii sau a unui bloc etc.). Domeniul numelui - reprezint poriunea din program n care poate fi utilizat identificatorul pentru accesarea informaiei asociate i este determinat de poziia declaraiei. Durata de stocare - reprezint perioada ct elementul asociat exist efectiv n memorie. Legtura - indic modul de asociere a unui identificator cu un anumit obiect sau funcie, n procesul de editare a legturilor. Tipul datei (standard sau definit de utilizator) - descrie informaia coninut de elementul definit de identificator. Clasa de memorare este specificat prin unul dintre cuvintele cheie: typedef, extern, static, auto, register. Declaraia auto se poate utiliza pentru variabile temporare - alocate folosind stiva, cu domeniul
230

local. Variabilele declarate n interiorul unui bloc sunt implicit locale, deci auto este rar utilizat. n limbajul C clasic, o declaraie register reprezint un apel la compilator pentru a stoca o variabil int sau char ntr-un registru al procesorului pentru a creste viteza de executare. Versiunile actuale permit specificarea register pentru orice tip, semnificaia apelului fiind de optimizare a timpului de acces. Specificatorul typedef indic faptul c nu se declar o variabil sau funcie de un anumit tip, ci se asociaz un nume tipului de date. Sintaxa este: typedef <definiie tip> <identificator>; Specificatorul static poate s apar n declaraii locale de variabile pentru a indica durata static sau n declaraii globale de funcii i de variabile pentru a indica legtura intern. Specificatorul extern este utilizat pentru declaraii de funcii sau variabile locale sau globale pentru a indica legtura extern i durata static. n C (precum i n Pascal), declaraia unei variabile trebuie s precead orice referire a ei. Ea poate aprea n exteriorul oricrei funcii, n lista de parametri formali ai unei funcii sau la nceputul unui bloc. Domeniul numelui este regiunea dintr-un program C n care identificatorul este "vizibil". Poziia declaraiei determina urmtoarele domenii: Domeniul bloc - caracterizeaz identificatorii locali (identificatorii declarai n interiorul unui bloc i au domeniul cuprins ntre declaraie i sfritul blocului; parametrii formali din definiia unei funcii au ca domeniu blocul funciei). Domeniul fiier - caracterizeaz identificatorii declarai n exteriorul oricrei funcii - numii identificatori globali - i care au domeniul cuprins ntre declaraie i sfritul fiierului. Domeniul funcie - aplicabil pentru etichetele instruciunilor i este blocul funciei. Domeniul prototip - definit pentru identificatorii specificai n lista de parametrii din prototipul unei funcii - i care au domeniul limitat la acel prototip. Partea din domeniu n care informaia asociata este accesibila se numete zona de vizibilitate. O declaraie a unui identificator este vizibila n tot domeniul sau mai puin blocurile sau funciile n care identificatorul este redeclarat. Pentru identificatorii globali se poate repeta declaraia, dar iniializarea trebuie s se fac o singur dat. Un identificator declarat n diferite domenii, de mai multe ori, sau redeclarat n acelai domeniu se poate referi la acelai obiect sau funcie prin procesul numit legare. Legarea poate fi intern, extern sau unic. Dac un identificator are domeniul fiier i clasa de memorare static, el se supune legrii interne. Daca un identificator are clasa de memorare extern, el se supune aceluiai tip de legare precum orice declaraie vizibila a identificatorului cu domeniu fiier; dac nu exist declaraii vizibile cu domeniul fiier, se supune implicit legrii externe. Pentru identificatorii cu legtura extern sunt permise mai multe declaraii de referin, dar trebuie s existe o singur definiie. Funciile au implicit legtura extern i durata static. Specificatorii de tip indic modul de alocare asociat unei variabile sau tipul rezultatului unei funcii. n C, exist urmtoarele categorii de tipuri: tipuri de funcii, tipuri de variabile i tipul void. Variabilele pot fi de tip scalar, de tip structurat sau de tip uniune. Tipurile scalare sunt tipuri aritmetice i tipul pointer. Tipurile structurate cuprind tablourile i nregistrrile (numite n C, structuri). n categoria tipurilor aritmetice intr mulimile de elemente specificate prin cuvintele cheie: char, int, float, double; extinse cu ajutorul modificatorilor de tip: signed, unsigned, short, long. Tot tip aritmetic este considerat a fi i tipul obinut prin enumerare. Tipul void indic absena oricrei valori i este utilizat n urmtoarele situaii: declaraia unei funcii fr parametrii sau fr rezultat, tipul pointer generic i conversii de tip pentru pointeri. Literalii sunt i ei afectai de existenta modificatorilor de tip prin indicarea unui sufix (U, u, L, l, f, F). Efectul sufixului asociat unui literal ntreg este ilustrat prin situaiile: U sau u - unsigned int sau unsigned long int (n funcie de valoare); L sau l - long int sau unsigned long int (n funcie de valoare); UL, ul, Ul, uL - unsigned long int. Un literal de tip numr zecimal, este automat de tip double; dac se utilizeaz sufixul F sau f va fi considerat de tip float, iar dac se utilizeaz sufixul L sau l, va fi considerat de tip long double. Tabloul este o list de elemente de acelai tip plasate succesiv ntr-o zona contigu de memorie. Nu exist limit pentru numrul dimensiunilor tabloului. Structura este o colecie de date eterogene (corespunde tipului record din limbajul Pascal). O declaraie de structura precizeaz identificatorii i tipurile elementelor componente i constituie o definiie a unui tip de date nou. Acestui tip i se poate asocia un nume. n cazul general, sintaxa declaraiei unei structuri este: <declaraie structura> ::= struct < id _tip> { <tip _camp _1> <id _camp _1>;
231

<tip _camp _2> <id _camp _2>; ... <tip _camp _i> <id _camp _i>; ... <tip _camp _n> <id _camp _n>; } <lista identificatori de tip struct>; in care: struct - este cuvnt cheie pentru construirea unui tip nregistrare; <id_tip> - este un identificator ce desemneaz numele tipului structur ce este declarat; <tip_camp_i> - tipul cmpului i; <id_camp_i> - identificatorul cmpului i (cmpurile structurii se mai numesc i membrii structurii); <lista identificatori de tip struct> - lista identificatorilor declarai. Referirea unui membru al unei variabile de tip structur se face folosind operatorul de selecie (.) ntr-o expresie care precizeaz identificatorul variabilei i al cmpului. Alocarea cmpurilor poate ridica probleme de portabilitate, deoarece organizarea memoriei depinde de sistemul de calcul. Uniunile sunt entiti care pot conine (la momente de timp diferite) obiecte de tipuri diferite. Practic, mai multe variabile sunt suprapuse n acelai spaiu de memorie. Sintaxa declaraiei este similar cu cea a structurii, dar identificatorii declarai ca membrii reprezint numele cu care sunt referite diferitele tipuri de variabile ce ocup aceeai zona de memorie: <declaraie uniune> ::= union <id_tip> { <tip_var_ 1> <id_var_1>; <tip_ var_2> <id_ var_2>; ... <tip_ var_i> <id _var_i>; ... <tip_ var_ n> <id_ var_n>; } <lista identificatori de tip union>; Spaiul alocat n memorie corespunde tipului cu dimensiune maxima. Tipurile uniune sunt utile pentru conversii de date, n implementarea programelor de comunicaie etc. Tipul enumerare const dintr-un ansamblu de constante ntregi (cel puin un element), fiecare fiind asociat cte unui identificator. Constanta unui element al enumerrii este fie asociat implicit, fie explicit. Implicit, primul element are asociat valoarea 0, iar pentru restul este valoarea _precedenta+1. Cel mai simplu program C este constituit din directive preprocesor, declaraii globale i funcii. Printre funcii trebuie s existe una cu numele "main " cu care va ncepe executarea programului. Chiar dac programul este organizat pe mai multe fiiere surs, numai ntr-un singur fiier, numai o singura funcie poate purta numele "main". Celelalte funcii sunt subprograme definite de programator sau funcii din biblioteca de subprograme. Limbajul C nu conine funcii predefinite cum sunt cele din unitatea System a mediului Borland Pascal. Funciile din bibliotecile C sunt declarate mpreuna cu constantele, tipurile i variabilele globale asociate, n fiiere antet, cu extensia ".h", situate n subarborele include al arborelui asociat mediului de programare. Operaiile de intrare-ieire necesit specificarea fiierului stdio.h, ncadrat de delimitatorii < i >, ntr-o directiv{ # include. Fiierele antet ale programatorului vor fi ncadrate folosind delimitatorul ". O funcie C are structura: <tip_ rezultat> <id_functie> (<lista _parametri_ formali>){ declaratii_locale secventa_instructiuni } unde <tip_ rezultat> indica tipul rezultatului returnat de funcie, <id _funcie> reprezint numele (identificatorul) funciei, iar <lista_parametri_ formali> const n enumerarea declaraiilor parametrilor funciei sub forma: <tip_ parametru> <id_ parametru> [ ,<tip_parametru> <id _parametru>] Acoladele { } sunt delimitatori ce ncadreaz o instruciune compus (bloc) alctuita din declaraii i instruciuni. Secvena de instruciuni a funciilor pentru care <tip _rezultat> este diferit de tipul void, trebuie s conin o instruciune return, cu sintaxa general:
232

return <expresie> Rezultatul funciei este valoarea expresiei. Funcia main poate avea parametri i poate ntoarce un rezultat. III.4.2. Funciile de intrare-ieire pentru consol Consola sau dispozitivul standard de intrare-ieire reprezentate de tastatur (zona de date - stdin) i ecran (zonele de date - stdout i stderr) permit utilizatorului interaciunea cu programul aflat n executare. Sunt posibile operaii de citire/scriere fr formatare i operaii de citire/scriere cu formatare. Operaii de citire/scriere fr formatare. Acestea permit lucrul cu caractere (char ) sau cu iruri de caractere (* char). Pentru citirea unui caracter din stdin pot fi utilizate funciile: int getchar(void), int getche(void ) i int getch( void), ultimele dou variante nefiind prevzute de standardul ANSI, dar sunt prezente n versiunile Borland (fiierul antet conio.h). Funcia getchar ntoarce primul caracter din stdin, care corespunde primei taste apsate, dar numai dup apsarea tastei Enter. Caracterul este transformat n ntreg fr semn. n cazul unei erori sau la ntlnirea combinaiei EOF (sfrit de fiier) funcia ntoarce valoarea -1 (codificat prin EOF). Funcia getche ateapt apsarea unei taste i ntoarce caracterul corespunztor pe care l afieaz pe ecran (nu e nevoie de Enter ). Funcia getch este similara cu getche(), dar nu afieaz ecoul pe ecran. Pentru scrierea unui caracter la stdout se utilizeaz funcia int putchar (int c) care afieaz pe ecran caracterul cu codul ASCII c. Dac operaia reuete, ntoarce caracterul afiat, iar n caz de eec valoarea EOF (-1). Pentru citirea (resp. scrierea) irurilor de caractere se lucreaz cu funcia gets (respectiv puts). Funcia cu prototipul char *gets (char *s) citete caractere din stdin i le depune n zona de date de la adresa s, pn la apsarea tastei Enter. n ir, tastei Enter i va corespunde caracterul '\0'. Dac operaia reuete, funcia ntoarce adresa irului, altfel valoarea NULL ( = 0 ). Funcia cu prototipul int puts( const char *s) afiaz pe ecran irul de la adresa s sau o constant ir de caractere (secvena de caractere ntre ghilimele) i apoi trece la linie noua. La succes, funcia ntoarce ultimul caracter, altfel valoarea EOF. Operaii de citire/scriere cu formatare. La citire, formatarea specific conversia datelor de la reprezentarea extern n reprezentarea binar. Pentru operaia de scriere se efectueaz conversia invers. Pentru citirea datelor se utilizeaz funcia scanf cu prototipul: int scanf( const char * format [ , lista_adrese_ variabile] ); iar pentru scrierea datelor se utilizeaz funcia printf cu prototipul: int printf( const char *format, lista_valori); irul de caractere format poate conine n general: 1. specificatori de format: iruri precedate de caracterul '%' care descriu fiecare cmp ateptat; 2. caractere de spaiere: spaiu (' '), tab ('\t'), linie noua ('\n'); 3. orice alt caracter Unicode. Fiecrei variabile din lista i corespunde o specificaie de format (tipul I.). Funcia scanf ntoarce numrul de cmpuri citite i depuse la adresele din list. Dac nu s-a stocat nici o valoare, funcia ntoarce valoarea 0. Funcia printf ntoarce numrul de octei transferai sau EOF n caz de eec. Funcia scanf citete succesiv caractere din stdin pe care le interpreteaz prin compararea succesiv a caracterului citit cu informaia curent din irul format. Prezena unui caracter de tip II determin citirea fr memorare a secvenei pn la ntlnirea unui caracter de tip I sau III. Prezena unui caracter de tip III determin citirea fr stocare a caracterului curent de la tastatur, dac este identic. La scriere, caracterele de tip II i III se afieaz pe ecran aa cum apar n irul format. Forma general a unui descriptor pentru scriere este: % [ flags] [ width] [ .prec] [ lmod] type specificaiile dintre [ i ] fiind opionale. Elementele de mai sus au urmtoarea semnificaie: flags - poate fi unul dintre semnele: +, -, 0, spaiu, #. Semnele au urmtoarea semnificaie: : aliniere la stnga a argumentului n cadrul cmpului; + : numerele vor fi obligatoriu tiprite cu semn; 0 : indic completarea la stnga cu zerouri (la numere); spaiu: daca primul caracter nu e semnul , se va afia un spaiu; width: este un numr care specifica limea minima a cmpului. Argumentul corespunztor va fi afiat pe un cmp cu latine cel puin width. Dac sunt mai puine caractere de scris, se va completa cmpul cu spatii la stnga (implicit) sau la dreapta, dac s-a specificat flag-ul -. Dac s-a specificat flagul 0, se va
233

completa la stnga cu zero. Dac width este caracterul *, atunci limea este dat de urmtorul argument din list (trebuie s fie neaprat un int). prec: este un numr care specifica precizia de scriere; pentru %s prec indic numrul maxim de caractere ce se va scrie; pentru %e, %E si %f prec indic numrul de zecimale; pentru %g si %G prec indic numrul de cifre semnificative, iar la descriptorii pentru ntregi indic numrul minim de cifre. Daca prec este *, atunci se consider c limea de scriere este dat de urmtorul argument din list, care trebuie sa fie de tip int. lmod: este un specificator de lungime care corespunde unui argument short sau unsigned short (h), long sau unsigned long (l), respectiv long double (L). type: este descriptorul propriu-zis. Se utilizeaz urmtoarele caractere: d, i (int ) - notaie zecimal cu semn; 0 (int ) - notaie n baza 16 fr semn; x, X (int ) - notaie n baza 16 fr semn cu abcdef pentru x i ABCDEF pentru X; u (int ) - notaie zecimal fr semn; c (int ) - un caracter; s (char *) - ir de caractere terminat cu '\0' ; f (double ) - numrul n virgul mobil cu format standard; e, E (double ) - numrul n virgul mobil cu format exponenial; g, G (double ) - n loc de f, e, E; p (void *) - se tiprete argumentul ca adres; % - se tiprete %. Forma general a unui descriptor pentru citire este: % [ *] [ width] [ lmod] type unde: * - suprim atribuirea urmtorului cmp din stdin la urmtoarea variabila; width, lmod - ca mai sus; type - descrie tipul de conversie. Cele mai importante specificaii de conversie sunt: d (int *) - ntreg zecimal; i (int *) - ntreg oarecare (zecimal, octal sau hexa); o (int *) - ntreg octal; u (unsigned int *) ntreg zecimal fr semn; x (int *) - ntreg hexa, c (char *) - caractere; s (char *) - ir de caractere (se va ncheia cu '\0 '); e, f, g (float *) - numere n virgul mobil; p (void *) - valoarea unei adrese aa cum e tiprit de printf. n descrierea de mai sus, ntre paranteze se indica tipul argumentului supus operaiei de intrareieire. Notaia tip * nseamn adresa unei locaii de tipul tip. III.4.3. Operatori i expresii Operatorii sunt simboluri care descriu operaii efectuate asupra unor variabile sau constante (numite generic operanzi). O combinaie corect de operatori, variabile, constante, apeluri de funcii reprezint o expresie. Pentru construcia expresiilor, limbajul C ofer o gam foarte larg de operatori. Operatorul de atribuire. Operatorul de atribuire (=) permite crearea unei expresii de forma: <variabila> = <expresie> ce se evalueaz de la dreapta la stnga. Dup evaluarea membrului drept, valoarea rezultata este nscrisa n <variabila>, iar ntreaga construcie are valoarea variabilei dup nscriere. Operatori aritmetici. Operatorii aritmetici sunt: + (adunare), - (scdere), * (nmulire), / (mprire), % (mprire modulo mpritor). Ordinea operaiilor este cea binecunoscut, dar se pot utiliza paranteze pentru schimbarea ordinii operaiilor. Pentru scrierea instruciunii de atribuire <variabila> = <variabila> + 1; se poate utiliza forma prescurtat <variabila>++, operatorul ++ fiind numit operator de incrementare. Exist, de asemenea, i un operator de decrementare (--): <variabila>--; ce este echivalentul instruciunii: <variabila> = <variabila> - 1; Operatori logici i relaionali. Pentru scrierea expresilor booleene se utilizeaz operatorii logici i operatorii relaionali. Exist trei operatori logici: || (SAU logic - SAU INCLUSIV), && (SI logic), ! (NU logic). Operatorii relaionali ntlnii n limbajul C sunt urmtorii: < (mai mic strict), > (mai mare strict), <= (mai mic sau egal), >= (mai mare sau egal), == (egal cu), != (diferit de). Ori de cte ori relaia este fals se genereaz valoarea 0, valoarea 1 fiind generat atunci cnd relaia este adevrat. Trebuie evideniat c operatorii aritmetici au prioritate fa de operatorii relaionali. Operatori la nivel de bit. Operatorii la nivel de bit se pot aplica operanzilor de tip ntreg (char, int, short, long , cu sau fr semn): & (SI logic la nivel de bit), (SAU logic la nivel de bit), ^ (SAU exclusiv la nivel de bit), << (deplasare stnga), >> (deplasare dreapta) i (negare la nivel de bit). Operatori de atribuire combinai. Pentru realizarea atribuirii <variabila> = <variabila> <operator> <var _sau_const>; se pot utiliza operatorii de atribuire combinai: += (atribuire cu adunare), -= (atribuire cu scdere), *= (atribuire cu nmulire), /= (atribuire cu mprire), %= (atribuire cu mprire modulo); expresia fiind scris prescurtat: <variabila> <operator>= <var _sau_const>;
234

Operatorul virgul. n limbajul C, virgula (,) este un operator binar, care leag expresii oarecare. Construcia <expresie_ 1>, <expresie_2> este una corect, constnd din evaluarea celor dou expresii, n ordinea n care apar, valoarea ntregii construcii fiind dat de valoarea lui <expresie_2>. Asocierea operatorului virgul se face de la stnga la dreapta, astfel nct o expresie de forma e1,e2,e3 este echivalent cu: (e1, e2), e3. Operatorul condiional (?:) Operatorul condiional este o construcie decizional a limbajului C care are urmtoarea forma general: <Expresie-booleana> ? <expresie_ 1> : <expresie _2>; avnd urmtorul neles: Dac <Expresie-booleana> este adevrat. atunci ntreaga expresie condiional are valoarea <expresie_1>, n caz contrar, valoarea expresiei condiionale fiind valoarea <expresie_2>. Ali operatori: n aceast categorie includem operatorii specifici tipului referin, operatorii pentru selectarea elementelor unui tip de date structurat, precum i operatorii introdui de extensia C++. III.4.4. Instruciuni C Cea mai simpl instruciune C este instruciunea <expresie>; ce ofer un echivalent n C pentru urmtoarele instruciuni Pascal: atribuire, apel de procedur, instruciunea vid. O secven de instruciuni ncadrat de acolade este considerat ca o singur instruciune i este numit instruciune compus sau bloc. Spre deosebire de instruciunea compus a limbajului Pascal, instruciunea compus din limbajul C poate conine att declaraii, ct i instruciuni, declaraiile fiind poziionate la nceputul blocului. n mod implicit, identificatorii declarai n blocul delimitat de acolade, au ca domeniu de vizibilitate blocul, iar timpul de via este limitat la timpul de executare a blocului. Programarea unei structuri decizionale, n C, poate fi realizat folosind: instruciunea if...else ; operatorul condiional (?:) i instruciunea switch. Sintaxa instruciunii if...else este: <instructiune_if> ::= if ( <Expresie>) <Instruciune _T>; if ( <Expresie>) <Instruciune_ T> else <Instruciune_ F> unde: <Expresie> are o valoare de tip scalar reprezentnd o constructe de tip expresie, <Instruciune_ T> reprezint o instruciune C care se va executa cnd <Expresie> are o valoare nenul (adevrat), iar <Instruciune_ F> reprezint acea instruciune C ce se va executa pentru valoare 0 (false) a lui <Expresie>. Conform celor de mai sus, construcia: e1 ? e2 : e3; poate nlocui instruciunea if...else: if (e1) e2; else e3; Atunci cnd o selecie multipl este controlat de valoarea unei singure expresii, se poate utiliza instruciunea switch , un pseudo-echivalent C a construciei Pascal case . Sintaxa instruciunii switch este: <instructiune_switch> ::= switch ( <expresie>) { case <const_ 1> : <lista_ instruciuni> [ break;] case <const_2> : <lista _instruciuni> [ break;] ... [ default:] <lista_instructiuni> [ break ;] } unde: <expresie> este o expresie cu valoare ntreag (tip ntreg sau enumerare); <const_1>, <const _2>, ... sunt constante de selecie, cu valori distincte, convertibile la tipul expresiei <expresie>, iar <lista _instructiuni> este o secven de instruciuni C. Fiecare etichet case indic o singur constant, dar se pot asocia mai multe etichete case , scrise consecutiv, pentru aceeai secvena de instruciuni. Instruciunea break ntrerupe lista de instruciuni i duce la ncheierea instruciunii switch . Dac valoarea expresie nu apare n lista constantelor de selecie, se execut instruciunile asociate etichetei default , dac exist. Programarea ciclurilor poate fi realizat folosind instruciunile de ciclare: ciclul cu test iniial (instruciunea while ), ciclul cu test final (instruciunea do...while ) i ciclul cu test iniial i contor (instruciunea for ). Forma instruciunii while este: while (<expresie>) <instruc#iune>. n particular, <instruciune> poate fi chiar instruciunea vid. Sintaxa instruciunii do..while este: do <instruciune> while (<expresie>); Instruciunea dintre do i while se execut cel puin o dat i se repet ct timp <expresie> este compatibil cu valoarea logic adevrat.
235

Instruciunea for, ofer cea mai compact metod pentru scrierea ciclurilor cu test iniial i are o definiie care i extinde domeniul de aplicare fa de alte limbaje de programare. Forma instruciunii for este: for ( <expresie_1> ; <expresie_3> ; <expresie_3> ) <instruciune> i are efectul similar cu al secvenei: <expresie_1>; while (<expresie_2>) { <instruciune> <expresie_3>;} Cele trei expresii dintre paranteze pot fi toate vide, caz n care avem de-a face cu un ciclu infinit. ntreruperea necondiionat a unei secvene de instruciuni i continuarea dintr-un alt punct al programului este posibil prin utilizarea instruciunilor de salt: goto (salt la o instruciune etichetat), break (n contextul instruciunii switch ct i n instruciunile de ciclare pentru a determina ieirea forat din ciclu, indiferent de valoarea condiiei de ciclare) i continue (n cadrul blocului instruciunilor de ciclare pentru a ntrerupe execuia iteraiei curente). n cazul instruciunilor while i do..while, instruciunea continue determin activarea testului condiiei de ciclare, iar pentru instruciunea for se va continua cu evaluarea, n aceast ordine, expresiilor <expresie_3>, <expresie_2>. BIBLIOGRAFIE 1. Albeanu G., Algoritmi si limbaje de programare, Editura Fundaiei Romnia de Mine, Bucureti, 2000. 2. Albeanu G., Luminita Radu, Algoritmica i programare n Pascal, Editura Fundaiei Romnia de Mine, Bucureti, 2001. 3. Knuth D., Arta programrii calculatoarelor, vol. I, Algoritmi fundamentali, Editura Teora, 2000. 4. Livovschi L., Georgescu H., Analiza i sinteza algoritmilor, Editura tiinific i Enciclopedic, 1974. 5. Albeanu G., Tehnici de programare, Lucrri practice de programarea calculatoarelor, Editura Fundaia Romnia de Mine, 2003. 6. Brz S., Culegere de probleme de algoritmic i programare, vol I, Programare static, Editura Universitii Bucureti, 2001. 7. Brz S., Algoritmic i programare. Note de curs, vol. I, Programare static, Editura Universitii Bucureti, 2001.

236

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