Sunteți pe pagina 1din 88

PROGRAMARE PROCEDURAL

PROCEDURAL PROGRAMMING Disciplin obligatorie; sem 1, ore sptmnal nvmnt de zi: 2 curs, 2 laborator, total ore semestru 56; 6 credite; colocviu

I. CONINUTUL TEMATIC AL DISCIPLINEI


Algoritmi: Caracteristici. Descriere. Complexitate. Corectitudine. Limbaje de programare. Caracteristici. Limbajul de programare C: Entiti sintactice. Operatori.. Expresii. Instruciuni. Funcii. Directive de preprocesare. Tablouri i Pointeri. Comunicare intermodular. Funcia main cu argumente. Pachetele: stdio.h, math.h, string.h Alocare static Alocare dinamic. Structuri de date dinamice (liste i arbori). Aplicaii ale utilizrii tipurilor de date structurate (struct, union, typedef) cu ajutorul pointerilor: crearea i explorarea structurilor de date. Pachetele: stdlib.h, alloc.h Operaii de intrare-ieire. Fiiere n C i aplicaii. Corectitudinea programelor C. Metoda aseriunilor (assert.h). Complexitatea programelor (time.h). Metrici software. Testarea programelor C. Utilizarea bibliotecilor statice (.LIB) i dinamice (.DLL). Metode de proiectare a programelor.

II. BIBLIOGRAFIE MINIMAL OBLIGATORIE


1. G. Albeanu, Algoritmi i limbaje de programare, Editura Fundaiei Romnia de mine, Bucureti, 2000. 2. G. Albeanu, Programare procedural, Note de curs (Avizierul virtual / Blackboard) 3. G. Albeanu (coord.), Tehnici de programare. Lucrri practice de programarea calculatoarelor, Editura Fundaiei Romnia de mine, Bucureti, 2003. 4. Popa M., Popa M., Programare procedural (Aplicaii C i C++ n structuri de date i grafic), Editura Fundaiei Romnia de mine, Bucureti, 2006.

UNIVERSITATEA SPIRU HARET

file:///C:/ACTIV/Proc/FINAL/IDD-USH.htm

UNIVERSITATEA SPIRU HARET FACULTATEA DE MATEMATICA-INFORMATICA

Suport de curs elaborat de

Prof. Univ. Dr. Grigore ALBEANU


pentru disciplina: PROGRAMARE PROCEDURALA 2C + 2L; Ciclul I; semestrul I Evaluare: Examen CUPRINS INTRODUCERE Ce este informatica ? Ce este un program ? Notiunea de algoritm. Cum rezolvam probleme cu ajutorul calculatorului ? ALGORITMI: DESCRIERE, COMPLEXITATE, CORECTITUDINE Notiuni de teoria grafurilor Scheme logice structurate Notiuni introductive privind organizarea datelor Limbaj algoritmic Analiza complexitatii Elemente privind corectitudinea algoritmilor LIMBAJE DE PROGRAMARE Vocabularul si sintaxa limbajelor de programare Tipuri de date. Constante. Variabile. Expresii Programare in C Bibliografie

1 of 1

12/9/2007 7:18 PM

G. Albeanu - PROGRAMARE PROCEDURALA - Introducere

file:///C:/ACTIV/Proc/FINAL/C1.HTM

1. Introducere
1.1. Ce este informatica? 1.2. Ce este un program? Notiunea de algoritm 1.3. Cum rezolvam probleme cu ajutorul calculatorului?

1.1. Ce este informatica?

n anul 1642, matematicianul si fizicianul Blaise Pascal (1623-1662) a inventat prima masina mecanic, cu roti dintate, capabil s realizeze operatii de adunare si scdere a numerelor naturale. Totusi, de abia dup aparitia masinilor electromecanice, n 1944, John von Neumann a formulat principiul programului nregistrat si a sugerat constructorilor de calculatoare trei principii care trebuie avute n vedere pentru realizarea unui calculator: programele si datele trebuie s fie codificate sub form binar; programele si datele trebuie stocate ntr-o memorie a masinii de calcul; trebuie s existe o component (unitate central de prelucrare, procesor) special care stie att s execute operatii de calcul, ct si s extrag, s decodifice si s execute instructiunile programului. Astfel, aproape toate tipurile de sisteme de calcul ce au aprut mai trziu sunt calculatoare de tip von Neumann. Aparitia sistemelor de calcul a dus la aparitia si dezvoltarea unei noi stiinte: informatica (Informatique, Informatics, Informatik n Europa, respectiv Computer Science n SUA). Informatica reprezint un complex de discipline prin care se asigur prelucrarea informatiilor cu ajutorul sistemelor de calcul. Astzi, informatica este prezent n industrie, bnci, ferme, art, medicin, stiinte sociale etc. si este structurat n mai multe domenii precum: arhitectura sistemelor de calcul: studiaz modul de organizare a sistemului fizic (hardware) pentru a se obtine o mai mare eficient, sigurant si utilitate. sisteme de operare: studiaz modalittile de gestiune eficient a resurselor fizice si a programelor. algoritmi si structuri de date: studiaz metodele de rezolvare a problemelor si modurile de organizare a datelor pentru a obtine programe eficiente. limbaje de programare: studiaz modalittile prin care algoritmii si structurile de date sunt prezentate calculatorului pentru a fi prelucrate. ingineria programrii: studiaz metodele de automatizare a proceselor de proiectare, analiz, testare si reutilizare a programelor. calcule numerice si simbolice: studiaz modalittile de reprezentare a informatiei numerice pentru implementarea unor algoritmi numerici robusti, eficienti si de mare precizie. sisteme de gestiune a bazelor de date: studiaz modalittile de structurare si organizare eficient a

1 of 6

12/9/2007 7:19 PM

G. Albeanu - PROGRAMARE PROCEDURALA - Introducere

file:///C:/ACTIV/Proc/FINAL/C1.HTM

colectiilor mari de date ce vor fi supuse diverselor prelucrri. inteligenta artificial: studiaz modalittile de reprezentare si manipulare a cunostintelor n vederea obtinerii de noi cunostinte. animatia si robotica: studiaz modalittile de reprezentare, prelucrare si analiz a informatiei audio-vizuale. n sfera sa de preocupri, Informatica a atras si dezvoltat o mare varietate de discipline precum: logica matematic, teoria automatelor, limbaje formale, cercetri operationale, teoria grafurilor si retelelor, calcul numeric, teoria fiabilittii, geometrie computational, teoria calculabilittii, baze de date, baze de cunostinte, sisteme expert etc.

1.2. Ce este un program? Notiunea de algoritm

Solutia unei probleme, din punct de vedere informatic, este dat printr-o multime de comenzi (instructiuni) explicite si neambigue, exprimate ntr-un limbaj de programare. Aceast multime de instructiuni prezentat conform anumitor reguli sintactice formeaz un program. Un program poate fi privit si ca un algoritm exprimat ntr-un limbaj de programare. Totusi, un algoritm descrie solutia problemei independent de limbajul de programare n care este redactat programul. Exist mai multe definitii ale notiunii de algoritm. A. A. Markov, a considerat algoritmul ca fiind o notiune matematic primar si a descris-o astfel: Un algoritm este o retet care descrie precis si clar un proces de calcul. El a descris un mecanism formal pentru a specifica o clas larg de activitti si anume: algoritmii normali. Alte modalitti de descriere a algoritmilor ce au mai fost propuse sunt: masina Turing, sistemele Post, functiile recursive etc. n aceast lucrare, prin algoritm vom ntelege o secvent finit de comenzi explicite si neambigue care, executate pentru o multime de date (ce satisfac anumite conditii initiale), conduce n timp finit la rezultatul corespunztor. Observm c se face distinctie ntre algoritm (care se termin n timp) si 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 secvent finit de etape si 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. Actiunile specificate de pasii 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 evidentiate din contextul n care este formulat problema a crei solutie o reprezint algoritmul. Un algoritm furnizeaz cel putin o valoare de iesire ce se afl ntr-o relatie specific cu datele de intrare. Un algoritm este eficace. Trebuie spus c nu orice problem admite solutie 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 solutii (chiar si o singur solutie). Din punctul de vedere al informaticii, intereseaz dac exist solutie ce poate fi descris algoritmic, deci dac solutia problemei poate fi construit efectiv. De asemenea, pentru rezolvarea anumitor probleme pot exista mai multi

2 of 6

12/9/2007 7:19 PM

G. Albeanu - PROGRAMARE PROCEDURALA - Introducere

file:///C:/ACTIV/Proc/FINAL/C1.HTM

algoritmi. Stabilirea celui mai bun dintre acestia se realizeaz n urma unui proces de analiz prin care se determin performantele fiecruia dintre algoritmi. n finalul acestei sectiuni vom descrie ctiva algoritmi elementari. Acestia vor contribui la realizarea unui prim contact cu solutiile algoritmice. Mai nti prezentm cteva conventii. Datele supuse prelucrrilor sunt manipulate cu ajutorul variabilelor. Acestea sunt entitti ce pot referi elemente ale unei anumite multimi. Putem vorbi de variabile ntregi (respectiv reale, caracter etc.) atunci cnd acestea se refer la valori ntregi (respectiv reale, caracter etc.). Actiunile realizate n cadrul unui algoritm vor fi descrise prin verbe: execut, citeste, scrie etc. Notatiile matematice consacrate vor fi utilizate fr explicatii suplimentare. Vom utiliza constructii precum: dac, atunci, altfel, ct timp, repet, pn cnd, pentru, de la, pn la etc., a cror semnificatie este cea uzual. O operatie important este aceea prin intermediul creia o variabil "va primi" o anumit valoare. Aceast actiune se mai numeste operatie de atribuire si, n continuare, se va nota prin ":=" Valoarea ce se atribuie unei variabile este rezultatul evalurii unei expresii. Expresia poate fi de tip numeric sau de tip logic. ntr-o expresie numeric apar numere (ntregi, reale etc.) si operatori aritmetici precum: + (adunare), - (scdere), * (nmultire), / (mprtire) etc. Expresiile logice (numite si expresii booleene) contin operatori logici: and (si logic), or (sau logic), not (negatie) si operatori relationali (<, >, <= (mai mic sau egal), >= (mai mare sau egal), != sau <> (diferit), = (egalitate)) folositi ntre elemente pentru care aceste operatii au sens. Rezultatul evalurii unei expresii logice poate fi: true (adevrat) sau false (fals). n descrierea unei expresii pot fi folosite si alte simboluri dac acestea sunt cunoscute sau explicate. Utilizarea parantezelor: "(" si ")" pentru indicarea modului de evaluare este, de asemenea, permis. Am vzut c algoritmii accept date de intrare si furnizeaz rezultate (date de iesire). Introducerea un valori sau a unui sir de valori se va descrie folosind verbul citeste. Afisarea unei valori sau a unui sir de valori va fi descris prin verbul scrie. Vom conveni s numerotm pasii (etapele) unui algoritm. numerotare va fi utilizat eventual n alti pasi. Acest mod de prezentare a algoritmilor se numeste descriere n limbaj conventional. n unele lucrri, limbajul conventional mai este numit si limbaj pseudocod. Exemplul 1.1. Fie a si b dou variabile ntregi. Dorim s schimbm ntre ele valorile celor dou variabile. Mai precis, dac a = 640 si b = 480, dorim s obtinem a = 480 si b = 640. Un mod de a realiza aceast schimbare presupune utilizarea unei variabile suplimentare, cu rol de intermediar. Folosind trei operatii de atribuire, algoritmul are urmtorii pasi: 1. citeste a, b 2. t := a 3. a := b 4. b := t 5. scrie a,b Lsm ca exercitiu realizarea urmtoarelor transformri: i) a --> b --> c --> a ii) a --> b --> c --> d --> a Atunci cnd va fi necesar, ne vom referii la pasii 2, 3 si 4 prin constructia interschimb(a,b). Exemplul 1.2. Se cere generarea primilor n termeni (n>1) ai sirului lui Fibonacci. Primii ctiva termeni sunt: 0, 1, 1, 2, 3, 5, 8, 13, ... Mai precis, termenul curent este suma celor doi termeni anteriori. Vom prezenta dou metode. Prima metod descrie schema de generare c := a + b, unde a, b desemneaz

3 of 6

12/9/2007 7:19 PM

G. Albeanu - PROGRAMARE PROCEDURALA - Introducere

file:///C:/ACTIV/Proc/FINAL/C1.HTM

ultimii doi termeni generati, iar c va fi termenul curent. Deoarece se cer n termeni, este necesar un mecanism de numrare (incrementarea unei variabile de contorizare). Valorile contorului vor fi referite prin variabila k. Algoritmul de generare, n prima variant, este: Varianta 1: 1. citeste n 2. k := 2 3. a := 0; scrie a 4. b := 1; scrie b 5. repet 5.i) c := a + b; scrie c 5.ii) a := b 5.iii) b := c 5.iv) k := k+1 pn cnd k = n. Prin a doua metod se vor genera termenii doi cte doi. Aceasta nseamn ca uneori este generat unul n plus. Descrierea ce urmeaz foloseste numai variabilele a si b pentru a indica termenii sirului. Varianta 2: 1. citeste n 2. a := 0 3. b :=1 4. k :=2 5. ct timp k < n execut 5.i) scrie a, b 5.ii) a := a + b 5.iii) b := a + b 5.iv) k := k+2 6. dac k = n atunci scrie a, b altfel scrie a. Exemplul 1.3. Se consider o secvent cu n numere ntregi. Se cere afisarea numerelor n ordine cresctoare. Vom descrie sirul de numere prin x1, x2, ..., xn. Cea mai simpl metod presupune interschimbarea a cte dou elemente, pn cnd nu mai sunt necesare interschimbri. Pasii algoritmului sunt: 1. citeste n 2. citeste x1, x2, ..., xn 3. k:=1; 4. repet 4.i) ordonat := true 4.ii) pentru i de la 1 pn la n-k execut dac xi > xi+1 atunci a) interschimb(xi, xi+1) b) ordonat := false 4. iii) k:=k+1; pn cnd (ordonat = true) or (k = n) 5. scrie x1, x2, ..., xn. Justificarea metodei este simpl. Trebuie s observm c la prima parcurgere, elementul maxim va ajunge pe ultima pozitie. La a doua parcurgere, elementul imediat mai mic (dect maximul recent obtinut), va ocupa penultima pozitie. Cnd nu sunt necesare interschimbri sirul este deja ordonat.

4 of 6

12/9/2007 7:19 PM

G. Albeanu - PROGRAMARE PROCEDURALA - Introducere

file:///C:/ACTIV/Proc/FINAL/C1.HTM

1.3. Cum rezolvm probleme cu ajutorul calculatorului?


Am vzut c exist probleme pentru care nu poate fi dat un algoritm de rezolvare. Totusi cele mai multe probleme cu care se confrunt informatica sunt probleme decidabile. Toate temele tratate n aceast lucrare formuleaz probleme decidabile. Se stie c nu nvtarea 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 cunoastem deja) solutia algoritmic a problemei puse. Cum se gseste solutia? Etapele descrise de G. Plya (Cum rezolvm o problem? Editura Stiintific, Bucuresti, 1965), pentru rezolvarea unei probleme de matematic, sunt valabile si n informatic. nainte de a ncepe s vedem cum se face, trebuie s ntelegem att ipotezele problemei ct si ceea ce se cere. Deci, ntelegerea problemei, ocup primul loc n procesul cutrii unei metode de rezolvare a acesteia cu ajutorul calculatorului. Trebuie evidentiate datele de intrare si conditiile pe care acestea trebuie s le ndeplineasc. Este important s identificm esentialul. De multe ori un enunt al unei probleme contine foarte multe elemente descriptive privind importanta problemei, consideratii de natur istoric, exemple, uneori chiar si etape distincte pentru obtinerea solutiei. Cerinta problemei furnizeaz, de multe ori, chiar idei privind modul de a ajunge la solutie. Considerarea unor configuratii particulare pentru datele de intrare si ncercarea de a lucra asupra lor, pentru a gsi solutia, va contribui ntotdeauna la o ntelegere mai bun a enuntului. Dac n enunt se sugereaz etapele rezolvrii, acestea implicnd rezolvarea unor subprobleme, atunci trebuie s aflm solutia algoritmic corespunztoare fiecrei etape. Dac nu se specific etape, dar putem descompune problema n subprobleme mai simple atunci solutia algoritmic a problemei initiale se va obtine prin "compunerea" solutiilor subproblemelor. Deci este foarte important s stim s rezolvm probleme simple, eventual probleme reprezentative pentru anumite clase de aplicatii si s stim s descompunem (respectiv, s reducem) problema initial n (la) subprobleme usor de rezolvat si apoi s construim solutia final. Abordarea prin descompuneri repetate, cu detaliere pas cu pas se numeste abordare top-down sau rafinare iterativ. Abordarea prin care pornind de la solutii algoritmice ale unor probleme cunoscute, construim solutii ale altor probleme care au ns legtur cu problema de rezolvat, iar n final, urmnd aceeasi modalitate construim solutia problemei a crei solutie se cere, se numeste abordare bottom-up. Aceast metod permite reutilizarea programelor existente si este tot mai important odat cu aparitia tehnicilor orientate obiect. De asemenea, pentru a obtine o solutie a unei probleme este util s privim problema si 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 solutie modular, bazat pe subprograme sau, mai nou, la o solutie orientat obiect. Multe din problemele de programare pot fi rezolvate usor dac solutia verific anumite principii. Dac solutia problemei este o multime de elemente care se poate obtine pas cu pas, pornind de la multimea vid, prin adugarea celui mai bun element neconsiderat nc (si ales conform anui anumit criteriu) spunem c avem de-a face cu o abordare de tip greedy. Pentru probleme n care se cere o solutie optim care satisface principiul optimalittii (principiul lui Bellman) se va aplica metoda programrii dinamice. Alte strategii pentru elaborarea algoritmilor sunt: metoda divide et impera, metoda backtracking, euristica etc. Vreau sa vad nceputul capitolului Vreau sa vad Cuprinsul documentului.

5 of 6

12/9/2007 7:19 PM

G. Albeanu - PROGRAMARE PROCEDURALA - Introducere

file:///C:/ACTIV/Proc/FINAL/C1.HTM

Versiune prescurtat a capitolului 1 din: G. Albeanu. Algoritmi si limbaje de programare. Editura Fundatiei "Romnia de Mine", 2000.

6 of 6

12/9/2007 7:19 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

2. Algoritmi: Descriere, Complexitate, Corectitudine


2.1. Notiuni de teoria grafurilor 2.2. Scheme logice structurate 2.3. Notiuni introductive privind organizarea datelor 2.4. Limbaj algoritmic 2.5. Analiza complexitatii 2.6. Elemente privind corectitudinea algoritmilor

2.1. Notiuni de teoria grafurilor

Un graf neorientat G este definit prin perechea G = (V, E), unde V este o multime nevid de elemente numite vrfuri (vertex), iar E este o multime (posibil vid) de perechi neordonate cu componente distincte ale lui V care se numesc muchii (edges). Dac E este multimea vid spunem c G este trivial. n cazul n care multimea V este finit spunem c avem un graf finit. Numrul elementelor multimii V determin ordinul grafului finit. O muchie cu vrfurile x si y (numite extremitti) se noteaz prin [x, y] sau [y, x]. Spunem c vrfurile x si y sunt incidente muchiei [x,y]. Un digraf este definit printr-o pereche G=(V, E), unde V este o multime 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 numeste surs sau extremitate initial, iar y se numeste destinatie sau extremitate final. Atributele : trivial, respectiv finit, pentru un digraf se definesc similar. Un graf (digraf) partial al unui graf (digraf) G = (V, E) este un graf (digraf) G1 = (V, E1), unde E este submultime a multimii E, deci este graful (digraful) G nsusi sau se obtine 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 submultime a multimii V, E' este submultime a multimii E, iar muchiile (arcele) din E' sunt toate muchiile (arcele) din E, care au ambele extremitti n multimea 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) si a unor segmente de curb (sau de dreapt) pentru indicarea muchiilor (vezi figura 2.1a). n cazul digrafurilor arcele sunt segmente de curb orientate, sensul fiind de la surs spre destinatie (vezi figura 2.1b). Definitia 2.1.1.

1 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

Fie G = (V,E) un digraf, iar x si y dou vrfuri. Numim x-y drum (de lungime n) o secvent de vrfuri D: v0, v1, ..., vn dac v0 = x, vn = y , iar (vi, vi+1) apartine multimii E pentru toti i, i n-1. Vrfurile x si y se numesc extremittile drumului D. Un drum fr nici un arc este un drum trivial. Un x-y drum se numeste 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 exceptia primului si a ultimului vrf care coincid, sunt distincte dou cte dou. Definitia 2.1.2. Fie G = (V, E) un graf neorientat, iar x si y dou vrfuri. Secventa de vrfuri L: v0, v1, ..., vn este un x-y lant (de lungime n) dac [ vi, vi+1] apartine multimii E, pentru toti i, 0 i n-1, iar x=v0 si y=vn. L este ciclu dac x=y. Atributul elementar, pentru un lant, poate fi introdus similar. Uneori, chiar pentru un digraf, putem folosi notiunea de lant, dac se verific proprietatea c oricare dou arce vecine au o extremitate comun. Definitia 2.1.3. Numim lant (drum) hamiltonian un lant (drum) elementar al unui graf care contine toate vrfurile grafului. Definitia 2.1.4. Fie G = (V, E) un graf netrivial. Spunem c x, y din V sunt conectate n G dac exist un x-y lant 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 multime vid) se accept c este graf conex. Dac G nu este conex atunci exist cel putin dou componente conexe (subgrafuri conexe maximale, disjuncte dou cte dou relativ la vrfuri). Un digraf G cu proprietatea c pentru oricare dou vrfuri x si y exist att un x-y drum ct si un y-x drum n G se numeste graf tare conex. n sectiunea urmtoare prezentm o metod de reprezentare a algoritmilor folosind schemele logice. Acestea sunt introduse folosind terminologia teoriei grafurilor.

2.2. Scheme logice structurate


O transcriere grafic a etapelor (pasilor) 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 instructiuni (comenzi) sau predicate (expresii logice). Predicatele apar n cadrul instructiunii de ramificare. Celelalte instructiuni sunt: 1. Instructiunea START: eticheteaz arcul initial (acel arc n a crui extremitate initial nu pot sosi alte arce). Orice schem logic are un unic arc initial. Acesta va indica punctul de unde ncepe executia unui program. 2. Instructiunea 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 executiei programului descris prin intermediul schemei logice. 3. Instructiunea CITESTE: eticheteaz un arc ce indic introducerea de la mediul de intrare (tastatur, disc etc.) a unei secvente de valori (numite date de intrare). Cuvntul CITESTE este nsotit de variabilele ce descriu datele de intrare. 4. Instructiunea SCRIE: eticheteaz un arc ce indic nregistrarea la mediul de iesire (display, disc etc.) a rezultatelor (datelor de iesire). Cuvntul SCRIE este nsotit de variabilele sau constantele ce desemneaz datele de iesire. 5. Instructiunea de atribuire: eticheteaz un arc cu eticheta v := e, unde v este o variabil, iar e este o expresie de acelasi tip (numeric sau logic) cu variabila v. 6. Instructiunea de ramificare: este caracterizat prin n arce ce pleac din acelasi punct, arce
2 of 16 12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

etichetate cu predicatele p1, p2, ..., pn definite astfel nct: p1 or p 2 or ... or pn = TRUE (adevrat) si pi and pj = FALSE (fals) pentru oricare i si j diferiti. Definitia 2.2.1. Se numeste schem logic (sau program sub form de schem logic) un graf orientat, n care: exist o unic instructiune START si cel putin o instructiune STOP; Orice vrf (diferit de extremitatea final a unei instructiuni STOP) este extremitatea initial a unei unice instructiuni; Orice arc este etichetat cu una dintre instructiunile: START, STOP, CITESTE, SCRIE, atribuire sau cu un predicat. In ultimul caz, extremitatea initial a arcului trebuie s coincid cu extremitatea initial a unei instructiuni de ramificare. pentru orice arc exist cel putin un drum care ncepe cu instructiunea START, se termin cu o instructiune STOP si contine arcul considerat. Schemele logice sunt folosite pentru descrierea algoritmilor. Se pot pune n evident structuri fundamentale precum: 1. structura secvential - format din arce conectate etichetate cu instructiuni distincte de cea de ramificare. O structur secvential format din dou arce etichetate prin a, respectiv b se va nota prin SEQ(a,b) si are semnificatia " execut a urmat de b". 2. structuri decizionale (alternative) - contin, obligatoriu, cel putin un predicat si cel putin un bloc functional (instructiune 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 (vezi figura 2.2a). IF0(p; a) - Dac p este verificat, atunci a (vezi figura 2.2b). CASE(p1,p2,...,pn; a1, a2, ..., an) - Dac p1 atunci a1, dac p2 atunci a2, ..., dac pn atunci an (vezi figura 2.2c). 3. Structuri repetitive (structuri de tip ciclu, structuri iterative) - contin obligatoriu un bloc predicativ si un bloc functional care se execut de un numr finit de ori pn cnd predicatul si schimb valoarea. Sunt posibile trei situatii: WHILE(p; a) - Ct timp conditia p este adevrat se execut a, apoi se continu cu urmtoarea instructiune ( vezi figura 2.3a: ciclu cu test initial). REPEAT(p; a) - Repet a pn cnd conditia p este adevrat, apoi execut instructiunea urmtoare ( vezi figura 2.3b: ciclu cu test final). FOR(p; a, b, c) - Este echivalent cu SEQ(a, WHILE(p; SEQ(b, c))). Blocul a este un bloc functional de initializare. Blocul b descrie instructiunea ce se va executa cnd conditia p este adevrat. Blocul c (prezentat explicit n aceast structur) va descrie actualizarea strilor variabilelor programului cu rol deosebit n evaluarea conditiei p ( vezi figura 2.3c: ciclu cu contor). Exemplul 2.2.1. Urmtoarea descriere reprezint o secvent. Uneori secventele se pot eticheta (* vezi figura 2.4).
ET1 SEQ write('Introduceti 3 numere:'); read(x,y,z); media:=(x+y+z)/3;

3 of 16

12/9/2007 7:20 PM

ALGORITMI I
writeln('Media = ',media); END

file:///C:/ACTIV/Proc/FINAL/C3.HTM

ET1

Eticheta secventei de mai sus este ET1, iar componentele secventei sunt instructiuni Pascal. Secventa descrie citirea a trei numere, calculul mediei aritmetice si afisarea rezultatului. Exemplul 2.2.2. Urmtoarele secvente ilustreaz structurile decizionale. a)(* vezi figura 2.5a *)
ET2 SEQ read(x);> if (x>0) or (x=0) then write('Numar nenegativ') else write('Numar negativ'); END

ET2

b)(* vezi figura 2.5b *)


ET3 SEQ read(titlu); cod:=0; if nume='algebra' then cod:=1; write(cod); END

ET3

c)(* exercitiu *)
ET4 SEQ zar:=1+random(6); case zar of 1, 3, 5 : impar:=impar+1; 2, 4, 6 : par:=par+1; end END

ET4

Secventa ET2 descrie citirea unei valori (se presupune c x este un numr) si afisarea unui mesaj n functie de semnul numrului x. Secventa ET3, citeste un titlu, iar dac se introduce 'algebra' atunci codul asociat este 1, n rest codul este 0. n final se scrie codul. Prin secventa ET4 se genereaz un numr aleator ntre 1 si 6. Apoi se analizeaz paritatea numrului. Dac secventa ET4 s-ar repeta, atunci variabila impar ar contine numrul de aparitii ale numerelor impare. Similar, variabila par va reprezenta numrul numerelor pare generate. Exemplul 2.2.3. Prin urmtoarele secvente exemplificm structurile repetitive: While, Repeat si For (Elaborarea schemelor logice este lsat ca exercitiu).
a)ET5 SEQ read(x);i:=1; cod:=0; while ((i<n) or (i=n)) and (cod=0) do if x = a[i]; then cod:=1 else i:=i+1; write(cod,i); END SEQ repeat zar:=1+random(6) until zar=6; writeln('A aparut Numarul 6!'); END SEQ read(n); suma:=0;

ET5 b)ET6

ET6 c)ET7

4 of 16

12/9/2007 7:20 PM

ALGORITMI I
for i:=1 to n do begin read(x); suma:=suma+x end; writeln('Suma =', suma); END

file:///C:/ACTIV/Proc/FINAL/C3.HTM

ET7

Secventa ET5 descrie citirea unui element x si verificarea ipotezei: "Elementul x se afl printre cele n elemente din tabloul a". n secventa ET6 se descrie generarea repetat de numere pn la aparitia valorii 6. Secventa ET7, folosind variabila de contorizare i, citeste n numere, le calculeaz suma si apoi o afiseaz. Observati prezenta predicatului la iteratiile de tip While si Repeat. Definitia 2.2.2. Un algoritm exprimat n functie de structurile SEQ, IF si WHILE se numeste structurat, schema logic asociat se numeste schem logic structurat, iar programul corespunztor se numeste program structurat. Evident, familia algoritmilor structurati este nevid. Un rezultat foarte important afirm: Orice algoritm poate fi transformat ntr-un algoritm structurat. Aceast afirmatie are la baz teorema Bohm-Jacopini. Pentru a transforma o schem logic (nestructurat) ntr-o schem logic structurat se poate folosi regula variabilei booleene (ce rezult din demonstratia 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 consecintele importante ale programrii structurate este eliminarea instructiunii de salt neconditionat (goto) din programe. Totusi, exist situatii cnd instructiunea goto este util. Lungimea programelor nu va creste, iar claritatea algoritmului nu va avea de suferit. Important este ca numrul instructiunilor goto folosite s fie foarte mic, iar salturile s fie locale.

2.3. Notiuni 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 informatiei pe care o reprezint ct si n raport cu unitatea central de prelucrare. Alte date sunt descrise prin componente, cu acelasi tip sau cu tipuri diferite. O colectie de date pe care s-a evidentiat un anumit mod de structurare si 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 pozitia 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) si 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 multimea numerelor rationale); date de tip boolean (se refer la valorile de adevr true (adevrat), false (fals)); date de tip char (cu elemente ale multimilor ASCII sau Unicode); date de tip string (obtinute prin concatenarea datelor de tip caracter); date de tip array (structur cu componente de acelasi tip ce ocup locatii succesive din memoria sistemului de

5 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

calcul, identificate prin pozitie); date de tip record (structur cu componente oarecari, 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, secvential, format din elemente apartinnd unei multimi date. O list poate fi vid (nu are nici un element) sau plin (nu mai exist spatiu pentru stocarea unor componente suplimentare). Este foarte important s putem accesa un element al listei, s inserm sau s stergem un element etc. Listele pot fi stocate n memoria unui sistem de calcul n dou moduri: secvential si nlntuit. Modul secvential presupune stocarea elementelor listei n locatii succesive de memorie conform ordinii elementelor din list si retinerea adresei primului element al listei (adresa de baz). Modul nlntuit presupune c fiecare element al listei este nlocuit cu o celul format dintr-o parte de informatie (corespunztoare elementului listei) si o parte de legtur ce contine adresa celulei urmtorului element din list. Se va retine adresa de baz a listei, iar ultima celul va indica, n partea de legtur, o valoare special (ce nu poate desemna o legtur). Structura de date elementar adecvat reprezentrii secventiale a listelor este tabloul unidimensional. Orice list liniar are un nceput si un sfrsit pe care le numim baz, respectiv vrf. O list liniar la care inserarea si extragerea elementelor se face prin vrful listei se numeste stiv (stack). O list liniar n care inserrile se efectueaz la baza listei, iar extragerile prin vrful listei se numeste coad (queue). Listele liniare pot fi transformate n liste circulare considernd c legtura ultimului element indic adresa bazei listei.

2.4. Limbaj algoritmic


O alt modalitate de reprezentare a algoritmilor o constituie utilizarea limbajului algoritmic. Limbajul algoritmic foloseste o scriere similar limbajelor de programare moderne. El permite att descrierea instructiunilor algoritmului ct si descrierea exact a tipului datelor cu care lucreaz algoritmul. Un algoritm descris folosind limbajul algoritmic este o succesiune finit de declarri si instructiuni. Declarrile precizeaz tipul si organizarea datelor. Ele apar naintea instructiunilor ce descriu pasii algoritmului. Din punct de vedere al scrierii instructiunilor, o instructiune poate ocupa mai multe rnduri sau pe un rnd pot fi scrise mai multe instructiuni. Instructiunile vor fi separate, ntre ele, folosind caracterul ';'. Cuvintele care identific un tip de date sau o instructiune, numite n continuare cuvinte cheie, apar evidentiate 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 si organizarea precizat prin cuvntul cheie respectiv. De exemplu, declarrile: integer array a(10); record (integer cod; string nume; real media) x, y; stack S; queue Q; char ch; boolean ind; indic faptul c a este un tablou cu maxim 10 elemente ntregi; x si y sunt nregistrri (cu trei componente: cod - o valoare ntreag, nume - un sir de caractere, media - un numr real), S este o stiv, Q este o coad, ch este un caracter, iar ind este o variabil logic. O important deosebit o au declarrile de subprograme. n rezolvarea multor probleme apare necesitatea executrii repetate a acelorasi calcule pentru date diferite. Subprogramele permit descrierea acestor calcule o singur dat. Subprogramul poate fi apelat ori de cte ori este necesar efectuarea

6 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

acestor operatii. Un subprogram este identificat printr-un nume si o list de parametri. Subprogramele se mpart n proceduri si functii. Delararea unei proceduri const n specificarea cuvntului procedure, a unui identificator al procedurii si a unei liste de declarri (ntre paranteze rotunde) ce indic informatiile ce fac obiectul transferului ntre apelant si apelat. Pentru declararea unei functii se foloseste cuvntul cheie function. Spre deosebire de proceduri, functiile ntorc obligatoriu un rezultat. De aceea, n declaratii, declararea unei functii ncepe cu specificarea multimii de valori ce corespunde rezultatului, a cuvntului function, a identificatorului functiei si a listei parametrilor (similar ca la o procedur). Un algoritm este complet cunoscut dac este descris si definitia subprogramelor folosite. Definitia unui subprogram presupune descrierea (prin instructiuni) modului n care se efectueaz calculele si se transmit rezultatele. Mai multe detalii prezentm n finalul acestei sectiuni. Instructiunile limbajului algoritmic sunt urmtoarele: 1. Instructiunea de atribuire. Aceast instructiune are forma: v := E (atribuire simpl) sau (v 1, v2, ..., vn) := (E1, E2, ..., En) ce realizeaz simultan atribuirile vi :=Ei, pentru oricare i = 1, 2, ..., n. Operatia de atribuire este permis cnd variabila v (variabilele v1, v2, ..., vn) din membru stng si expresia E (expresiile E1, E2, ..., En) sunt compatibile (se refer la aceeasi aclas de obiecte). O expresie contine paranteze (optional), operanzi (inclusiv apeluri de functii) si operatori adecvati. 2. Instructiuni de intrare/iesire. 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 iesire) se face la un mediu de iesire (de exemplu: ecranul, imprimanta, plotterul etc.). Forma instructiunilor de intrare/iesire este: read v1, v2, ..., vn write v1, v2, ..., vn unde v1, v2, ..., vn sunt variabile de tip elementar. 3. Instructiunea repetitiv While. Aceast instructiune are forma: while p do S, unde p este un predicat, iar S este o secvent de instructiui. Deoarece instructiunile sunt separate ntre ele, folosind ';' va trebui s delimitm secventa S. Pentru aceasta se utilizeaz constructia SEQ..END prezentat mai sus. Semnificatia acestei instructiuni este aceeasi ca pentru sub-schema logic While(p; S). 4. Instructiunea If_then_else. Aceast instructiune are forma: if p then S1 [elseS2 ], unde p este un predicat, iar S1 si S2 sunt secvente de instructiuni. Dac nendeplinirea predicatului p nu indic vreo actiune, portiunea else S2 poate lipsi, fapt reprezentat prin includerea ntre paranteze drepte, exprimarea fiind echivalent cu IF0(p; S1). Atunci cnd att S1 ct si S2 sunt actiuni prevzute, instructiunea este echivalent cu sub-schema logic IF(p; S1, S2). 5. Instructiunile insert si extract. Aceste instructiuni sunt necesare pentru lucrul cu liste. Acestea sunt o prelungire a instructiunii de atribuire. Dac se specific o list L atunci insert i, L (sau L:=i) exprim introducerea elementului specificat prin i n lista L, iar instructiunea extract i, L (sau i:=L) specific extragerea elementului curent din lista L si depunerea acestuia n i. 6. Instructiunea apel de procedur. Apelarea unei proceduri se face prin instructiunea 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 stabileste corespondenta ntre argumente si parametri, si se execut toate instructiunile specificate n definitia procedurii. Dup ultima instructiune a procedurii se continu cu instructiunea urmtoare apelului de procedur.
7 of 16 12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

Un apel de procedur este corect atunci cnd ntre argumente si parametri exist o concordant ca numr, tip si mod de organizare. Convenim ca atunci cnd referim o variabil (ntr-o procedur) de fapt, facem o referire la locatia de memorie corespunztoare argumentului respectiv. Spunem c se realizeaz un transfer prin referint. Sunt posibile si alte moduri de transfer, dar acestea nu sunt considerate momentan. 7. Instructiunea return. Aceast instructiune 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. Instructiunea return fr valoare de retur este folosit pentru a prsi executia unei proceduri si a reveni n unitatea de program din care a avut loc apelul; si anume la instructiunea ce urmeaz imediat acestui apel. 8. Pentru a usura descrierea algoritmilor admitem prezenta, ca instructiuni ale limbajului algoritmic, a instructiunilor Repeat si For. Instructiunea Repeat este modelat de structura repetitiv REPEAT (p; S). Ea are forma: Repeat S until p; unde S este o secvent (eventual vid) de instructiuni, iar p modeleaz o expresie logic. Instructiunea For este modelat de structura iterativ FOR(p; a,b,c) si are forma simplificat For v := e1, e2, e3 do S unde: S este o secvent de instructiuni, iar expresiile e1, e2 si e3 au acelasi domeniu de valori ca variabila v. n forma prezentat, instructiunea determin executarea secventei 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).

2.5. Analiza complexittii


Existenta 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 notiunile fundamentale privind complexitatea algoritmilor si prezint exemple simple de algoritmi pentru care se determin complexitatea. Analiza complexittii unui algoritm presupune determinarea resurselor de care acesta are nevoie pentru a produce datele de iesire. Prin resurs ntelegem timpul de executare, dar uneori este necesar s analizm si alte resurse precum: memoria intern, memoria extern etc. Modelul masinii pe care va fi executat algoritmul nu presupune existenta operatiilor paralele; operatiile se execut secvential. Timpul de executare al unui algoritm reprezint numrul de operatii primitive executate. Trebuie, pentru fiecare algoritm, s definim notiunea de operatie primitiv, independent de masina secvential pe care se va executa algoritmul. n analiza complexittii 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. Situatia 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. Analiza exact a complexittii unui algoritm conduce la formule extrem de complicate. De aceea se folosesc notatii asimptotice, care evidentiaz doar termenul preponderent al formulei.
8 of 16 12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

Fie f si g dou functii reale. Atunci: f(x) = o(g(x)) dac exist si este egal cu 0 (zero) limita: f(x) = O(g(x)) dac exist c si x0 astfel nct f(x) = theta(g(x)) dac exist constantele C 1 si C2 pozitive, si x0 astfel nct pentru orice x x0: f(x) ~ g(x) dac f(x) = Omega(g(x)) dac exist M>0 si un sir x 1, x2, ..., xn, ... Inf astfel nct pentru oricare j>0 are loc inegalitatea

n general, se consider c un algoritm este mai rapid dect altul dac are un ordin de mrime pentru timpul de executare mai mic. Pentru o dimensiune mic a datelor de prelucrat, aceste comparatii pot fi (ns) eronate. Notatia theta este utilizat pentru a specifica faptul c o functie este mrginit (inferior si superior). Semnificatia notatiei O este de limit superioar, n timp ce semnificatia notatiei Omega este de limit inferioar. Exemple: x3 = o (x5); sin(x) = o (x); (x+1)2 = theta (2x2); x2+3x ~ x2. Definitia 2.5.1. Fie A un algoritm, n dimensiunea datelor de intrare si T(n) timpul de executare estimat pentru algoritmul A. Se spune c algoritmul A are comportare polinomial (apartine clasei P) dac exist p>0 astfel nct T(n) = O(np). Definitia 2.5.2. O functie care creste mai rapid dect functia putere xp, dar mai lent dect functia exponential ax cu a>1 se spune c este cu crestere exponential moderat. Mai precis: f este cu crestere exponential moderat dac pentru oricare p>0 avem f(x) = Omega(xp) si oricare M>0 avem f(x) = o((1+ M)x). Definitia 2.5.3. O functie f are crestere exponential dac exist a>1 astfel nct f(x) = Omega(ax) si exist b>1 astfel nct f(x) = O(b x). Printre functiile n -> f(n), nemrginite, functiile 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 aceleiasi probleme) de complexitate log n. Algoritmii de tip polinomial sunt mai lenti (cresterea functiei T(n) este mai rapid) dect algoritmii de tip logaritmic. Urmeaz apoi algoritmii moderati (cu T(n) de forma nlogn etc.) si cei cu crestere exponential (2n, n33n etc.). Algoritmii cei mai lenti sunt cei pentru care functia T(n) este foarte rapid cresctoare (de exemplu: T(n) = n!). n informatic sunt interesanti numai algoritmii care conduc la un timp de calcul cel mult moderat. Dac un algoritm necesit timp de calcul exponential sau factorial, acesta va fi utilizat numai n cazuri

9 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

exceptionale. Tabelul urmtor ilustreaz cele de mai sus. n n2 n3 log n log log n 10 100 1000 1 0 100 10000 1000000 2 0.30103 1000 1000000 1000000000 3 0.47712 10000 100000000 1000000000000 4 0.60206

Exemplul 2.5.1. (Produsul a dou numere complexe). Considerm numerele complexe a+bi si c+di (i2 = -1; a, b, c, d numere reale). Un algoritm pentru calculul produsului (a+bi)(c+di) este urmtorul:
real a,b,c,d,p,q; real t1,t2; SEQ Read a, b, c, d; t1:=a*c; t2:=b*d; p:=t1-t2; t1:=a*d; t2:=b*c; q:=t1+t2; write p,q; END

P1

P1

Acest algoritm (notat P1) necesit 4 nmultiri, o adunare si o scdere. n final p reprezint partea real, iar q furnizeaz partea imaginar a produsului celor 2 numere complexe. Urmtorul algoritm (notat P2) calculeaz acelasi produs folosind 3 nmultiri, 3 adunri si 2 scderi:
real a,b,c,d,p,q; real t1,t2,t3,t4; SEQ Read a, b, c, d; t1:=a+b; t2:=t1*c; t1:=d-c; t3:=a*t1; q:=t2+t3; t1:=d+c; t4:=b*t1; p:=t2-t4; write p,q; END

P2

P2

Algoritmul P1 necesit 2 locatii temporare, n timp ce algoritmul P2 necesit 4 locatii suplimentare. n plus, necesarul de memorie depinde de metoda de reprezentare a numerelor reale ntr-un sistem de calcul. (Determinarea valorii maxime si a indicelui corespunzator, dintr-un sir (tablou)). Fie X un tablou cu n elemente apartinnd unei multimi total ordonate Q: X = (x 1, x2, ..., xn) cu xi din Q pentru i = 1, 2, ..., n. Fie y o variabil de tipul Q. Se caut un indice k, ntre 1 si n astfel ca y := max{xi : i=1, 2, ..., n | = xk, iar k este cel mai mic numr cu aceast proprietate. Pentru rezolvarea acestei probleme putem utiliza urmtorul algoritm, denumit n continuare MAXIM, pentru Q = real.
procedure maxim(x,n,y,k) integer n;real array x(n); real y; integer i,k; SEQ k:=1; y:=x[1]; for i = 2, n, 1 do if y < x[i] then SEQ y := x[i]; k:=i END; return END

10 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

Observm c T(n) = n-1. Necesarul de memorie pentru stocarea datelor de prelucrat se exprim n functie de metoda de reprezentare a informatiei n calculator. Propozitia 2.5.1. Pentru determinarea maximului dintre n elemente ale unei multimi total ordonate sunt necesare cel putin n-1 comparri (T(n) = n-1). Exemplul 2.5.3. (Determinarea celui mai mare divizor comun). Fie m si n dou numere ntregi pozitive si, q si r ctul, respectiv restul mprtirii lui n la m, adic n = qm+r (0 r < m). Spunem c m divide n dac restul mprtirii lui n la m este zero. Pentru determinarea celui mai mare divizor comun (cmmdc) a dou numere se poate utiliza algoritmul lui Euclid. Dac d este un divizor oarecare al numerelor n si m, atunci d divide restul r. Reciproc, dac d este un divizor al numerelor m si r, relatia n = mq + r arat c d este divizor al numrului n. Deci: cmmdc(n, m) = cmmdc(m, r). Dac r = 0 atunci n = qm. Deci cmmdc(n, m) = m. Folosind notatia n mod m pentru r, putem scrie: cmmdc(n, m) = cmmdc(m, n mod m). Necesarul de memorie pentru stocarea numerelor n si m se poate exprima prin theta(log m) + theta(log n) ~ theta(log (mn)) biti. Timpul necesar executrii algoritmului este dat de urmtorul rezultat. Propozitia 2.5.2. Fie n si m dou numere ntregi pozitive. Algoritmul lui Euclid pentru a determina cmmdc(m, n) efectueaz cel mult [2log2M]+1 operatii de mprtire ntreag, unde M = max (m, n). Exemplul 2.5.4. (Sortare prin insertie). Fiind dat o secvent de elemente caracterizate de valorile x1, x2, ..., xn apartinnd unei multimi total ordonate T, s se determine o permutare xi(1), xi(2), .., xi(n) a xi(k) secventei date, astfel nct xi(j) pentru i(j) i(k), unde " " este relatia de ordine pe multimea T. Metoda ce va fi prezentat n continuare se mai numeste si "metoda juctorului de crti", si este una dintre cele mai cunoscute metode de sortare. Sortarea prin insertie se bazeaz pe urmtoarea procedur: Fie un tablou x cu n elemente (x[i] este al i-lea element din secventa de intrare). Pornim cu subtabloul x[1] si la primul pas cutm pozitia n care ar trebui s se gseasc elementul x[2]. Dac x[2] < x[1], atunci x[2] trebuie s fie mutat n locul elementului x[1]. La un pas i (pentru i ntre 1 si n), avem subtabloul x[1..i-1] ordonat si ncercm s-l plasm pe x[i] astfel nct s obtinem un tablou sortat ntre pozitiile 1 si i. Pentru aceasta, se compar succesiv x[i] cu elementele tabloului x[1..i-1] pentru a se determina acea pozitie j pentru care x[i] x[j], indexul de plasare fiind j+1. Algoritmul prezentat n continuare utilizeaz insertia direct:
procedure insert_sort(n,x); integer n; integer array x(n); integer i,j,temp; SEQ for i = 2, n, 1 do SEQ

11 of 16

12/9/2007 7:20 PM

ALGORITMI I
temp :=x[i]; j:=i-1; while (j>=1) and (x[j] > temp) do SEQ x[j+1] :=x[j]; j:=j-1 END x[j+1]:=temp END;

file:///C:/ACTIV/Proc/FINAL/C3.HTM

return END

Este clar c, timpul de executie nu depinde doar de n, numrul de elemente de sortat, ci si de pozitia initial a elementelor din secvent. Fie F(n) - numrul de comparri necesare, iar G(n) numrul de mutri necesare algoritmului insert_sort pentru sortarea unui tablou cu n elemente. Propozitia
2

2.5.3.

Complexitatea metodei de sortare prin insertie direct este caracterizat prin: F(n) = O(n ), G(n) = O(n2).

2.6. Elemente privind corectitudinea algoritmilor


A verifica corectitudinea unui algoritm nseamn a verifica dac algoritmul conduce ntr-un interval finit de timp la obtinerea solutiei 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, Bucuresti, 1995. Notatie. Constructia {P}A{Q}, numit si formul de corectitudine total contine urmtoarele elemente: P - comentariu care descrie propriettile datelor de intrare (preconditia); A - algoritmul (secventa de instructiuni) supus analizei; Q - comentariu care descrie propriet{tile datelor de iesire (postconditia). Definitia 2.6.1. Un algoritm {P}A{Q} este corect cnd propozitia urmtoare este valid: Dac datele de intrare satisfac preconditia P Atunci 1) executarea lui A se termin (ntr-un interval finit de timp) si 2) datele de iesire satisfac postconditia Q. Folosind elementele fundamentale ale logicii matematice rezult c urmtoarele observatii sunt adevrate: 1. Algoritmul {false}A {Q} este corect, oricare ar fi A si Q.
12 of 16 12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

2. Algoritmul {P}A{true} este corect dac si numai dac executarea lui A se termin atunci cnd datele initiale satisfac proprietatea P. 3. Dac{P}A{Q} si {R}A{Q} sunt formule corecte, atunci {P v R}A {Q} este formul corect. Pentru a stabili corectitudinea algoritmilor complecsi se procedeaz la descompunerea acestora 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 secventiale (CS): Dac A este de forma SEQ B; C END atunci a verifica formula {P}A{Q}, revine la a verifica form {P}B{R} si {R}C{Q}, unde R este un predicat asupra datelor intermediare. Este evident c regula CS poate fi aplicat iterativ. Mai precis, dac A este de forma SEQ A1; A2; ..., An END atunci obtinem regula compunerii secventiale generale: &t9;CSG: Dac {P0} A1 {P1}, {P1} A2 {P2}, ...., {Pn-2}An-1{Pn-1} si {Pn-1}An{Pn} sunt algoritmi corecti, atunci{P0}A{Pn} este algoritm corect. Exemplul 2.6.1. Este usor de verificat c urmtorul algoritm este corect (presupunem c x si y sunt variabile ntregi, iar a si b constante ntregi):
{ x = a si y = b} SEQ x:=x+y; y:=x-y; x:=x-y END { x = b si y = a }

Regula implicatiei (I): Aceast regul este util n cazul algoritmilor ce urmeaz a fi aplicati n conditii mai tari dect pentru cele care au fost deja verificati. O proprietate P este mai tare dect proprietatea Q dac este adevrat propozitia compus: P -> Q. Regula implicatiei are urmtoarea form: I: Dac{P} A {Q} este algoritm corect, P1 -> P si Q -> Q1, atunci {P1} A {Q1} este algoritm corect.

Regula instructiunii de atribuire (A):

13 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

Fie notatiile: 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 obtinut din P(x) prin substituirea variabilei simple x cu expresia e, ori de cte ori x este variabil liber n P, iar dac e contine o variabil y care apare legat n P, nainte de substitutie variabila y se nlocuieste printr-o nou variabil (care nu mai apare n e).

P(x) P(x/e)

Valoarea de adevr a propozitiei P -> Q(x/e) nu se schimb dac n Q(x/e) se efectueaz substitutia 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. Exemple de instructiuni corecte: a){x = n!} n:=n+1; x:=x*n {x = n!} b){(x = a) and (y = b)} t:=x; x:=y; y:=t {(x = b) and (y = a)} c){(1<=i < n-1) and (s = SUM{b[k]: k=1, ..., i-1})} s:=s+b[i]; i:=i+1 {(1< i < n) and (s = SUM{b[k]: k=1, ..., i-1})} dac b este declarat prin integer array b(n).

Regula instructiunii if (IF si IFR): Dac c este o expresie boolean si A si B sunt algoritmi, pentru cele dou forme ale instructiunii if valabile regulile de corectitudine: IF: Dac {P and c} A {Q}, {P and not c} B {Q} sunt corecte, iar P -> Def(c) este adevrat Atunci formula {P} if c then A else B {Q} este corect. IFR: Dac

14 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

{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 instructiunii de decizie nu este n sine dificil corectitudinea acestei instructiuni se reduce la corectitudinea instructiunilor componente. Exemple de instructiuni corecte: a){true} if x>y then SEQ t:=x; x:=y; y:=t END {x <= y} b){(x=a) and (y=b)} if x>=y then m:=x else m:=y {m = max(a,b)} c){x=a} if x<0 then x := -x {x = |a|}

Regula instructiunii while (W): Regula instructiunii while trebuie s precizeze dac nu apare fenomenul de ciclare, iar prelucrrile 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) si o functie de terminare t cu valori numere ntregi care satisfac urmtoarele conditii: Cnd I este adevrat atunci expresia boolean c este bine definit (adic I -> Def(c)). Proprietatea I rezult prin executarea secventei A (adic, {P} A {I} este algoritm corect). La terminarea instructiunii while, proprietatea final Q poate fi dedus (adic, I and (not C) -> Q). I este proprietate invariant la executarea unei iteratii: dac I este adevrat nainte de executarea secventei S si expresia boolean c este adevrat, atunci executarea secventei S se termin ntr-un interval finit de timp si I este adevrat la sfrsit (adic, {I and c} S {I} este algoritm corect). Dac rezultatul evalurii expresiei c este true si proprietatea I este adevrat, atunci exist cel putin o iteratie de efectuat (adic, I and c -> (t > =1)). Valoarea lui t descreste dup executarea unei iteratii (adic, {(I and c) and (t=a)} S {t < a}). n aceste conditii, algoritmul considerat este corect.

Exemplul 2.6.2. (Determinarea celui mai mare divizor comun a dou numere ntregi). Fie a si b dou numere ntregi, iar m = |a| si n = |b|. Atunci urmtorul algoritm este corect. {(x > 0) and (y > 0) and (x = m) and (y = n)} while x <> y do if x > y then x := x - y else y := y - x;

15 of 16

12/9/2007 7:20 PM

ALGORITMI I

file:///C:/ACTIV/Proc/FINAL/C3.HTM

{x = cmmdc(m,n)} ntr-adevr, exist proprietatea invariant I: (cmmdc(x,y) = cmmdc(m,n)) and (x > 0) and (y > 0), iar ca functie de terminare se poate lucra cu: t(x,y) = x+y. Verificarea ipotezelor regulii W este simpl. Exemplul 2.6.3. (Al n-lea termen al sirului lui Fibonacci). Fie fn, al n-lea termen al sirului lui Fibonacci. Urmtorul algoritm este corect. {n >= 0} a:=0; b:=1; k:=n; while (k > 0) do SEQ temp := b; b := a + b; a := temp; k := k-1 END; {a = fn} ntr-adevr, lum functia de terminare t(k) = k, iar proprietate invariant este: I: (a = fn-k ) and (b = fn-k+1) and (temp = fn-k) and (0 <= k <=n). Deoarece instructiunile repetitive for (ciclu cu contor) si repeat (ciclu cu test final) se pot implementa folosind instructiunile anterioare, pentru demonstrarea corectitudinii acestora se aplic aceleasi principii. Mergi la inceputul capitolului Text prescurtat dupa G. Albeanu. Algoritmi si limbaje de programare. Editura Fundatiei "Romnia de Mine", 2000

16 of 16

12/9/2007 7:20 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

3. Limbaje de programare
3.1. Vocabularul si sintaxa limbajelor de programare 3.2. Tipuri de date. Constante. Variabile. Expresii 3.3. Programare n C

3.1. Vocabularul si sintaxa limbajelor de programare


Vocabularul unui limbaj de programare este format din cele mai simple elemente cu semnificatie lingvistic numite entitti lexicale sau tokens. Elementele vocabularului sunt alctuite din caractere Unicode (c constituie alfabetul limbajului). Standardul Unicode contine ca subset codul ASCII, dar reprezentarea intern a caracterelor Unicode foloseste 16 biti. Cele mai utilizate simboluri sunt: literele mari si m alfabetului englez, cifrele sistemului zecimal, diferite semne speciale. Unittile lexicale sunt separate, ntre ele, prin comentarii si spatii. Pentru aproape toate limbajele programare se pot evidentia unitti lexicale precum: cuvinte cheie, identificatori, literali, separatori operatori. Cuvintele cheie sunt secvente de caractere ASCII rezervate (nu pot avea alt semnificatie) utilizate pentru definirea unittilor 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 si in limbajul C alaturi de altele. Identificatorii sunt secvente, teoretic nelimitate, de litere si cifre Unicode, ncepnd cu o liter sau liniuta 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 si tipul sir de caractere. Mai precis, lieralii sunt constante ntregi, flotante, booleene, caracter si, siruri de caractere. Literalii ntregi, n general, pot fi descrisi prin reprezentri n una din bazele de numeratie: 10, 16 s Lungimea reprezentrii interne depinde de implementarea limbajului. De exemplu, n limbajul Pascal, literal ntreg, cu semn, este reprezentat pe 16 biti, descrierea sa n baza 16 fiind o secvent a simbolur asociate reprezentrii numrului ntreg n baza 16 avnd prefixul $. Literalii flotanti reprezint numere rationale. Ei sunt formati din urmtoarele elemente: partea ntrea partea fractionar si exponent. Exponentul, dac exist, este introdus de litera E sau e urmat optional de un semn al exponentului. Exemple: 3.14; 23.5 e+2; 3e-123. Literalii booleeni sunt TRUE si FALSE, primul reprezentnd valoarea boolean de adevr, iar cell valoarea boolean de fals. Desi TRUE si FALSE nu sunt cuvinte rezervate, acestea nu pot fi folos

1 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

identificatori (n Pascal, Java s.a). Literalii caracter sunt folositi pentru a desemna caracterele codului Unicode (sau ASCII, acolo unde es cazul). Descrierea unui literal caracter se fie folosind o liter, fie o secvent special. Secventele specia (numite secvente escape n C, C++ si Java) permit specificarea caracterelor fr reprezentare grafic precum si a unor caractere speciale. Caracterele ce au reprezentare grafic pot fi descrise ntre apostrofuri, ca n exemplele: 'P', 'A', '3', '!'. Secventele speciale se descriu diferit de la un limbaj la altul. Vom exemplifica folosind limbajele Pascal si Java. Secvente 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. Secventele escape n limbajul Java se descriu folosind apostrofuri, semnul \, litere si cifre. Vom exem indicnd cteva secvente predefinite: '\b' (backspace = #8), '\n' (linefeed), '\r' (carriage return) etc. Un literal sir de caractere este constituit din zero sau mai multe caractere ntre delimitatori. Secvent caractere ce formeaz sirul poate contine att caractere imprimabile ct si secvente speciale. n limbaj Pascal se utilizeaz apostroful ca delimitator, iar n limbajul C( C++, Java) ghilimelele. Separatorii sunt caractere ce indic sfrsitul unui token si nceputul altuia. Acestia particip si la constructia sintaxei limbajelor de programare. Ei nu trebuie confundati cu spatiile care si acestea sepa unitti lexicale distincte. Separatorii sunt necesari cnd unittile lexicale diferite sunt scrise fr spatii ntre ele. Cei mai ntlniti separatori sunt: ( ) { } [ ] ; , . Exemple: x[10], f(x,y), carte.autor etc. Operatorii sunt simboluri grafice ce desemneaz operatiile definite de un limbaj de programare. n unele limbaje programare este posibil redefinirea operatorilor, acelasi simbol fiind utilizat pentru operatii diferite rezult din contextul n care apar. Lista minimal a operatorilor aritmetici include: +(adunare), /(mprtire), *(nmultire). Mai sunt admise si operatii precum: % (C, Java) sau mod (Pascal, Modu (mprtire ntreag n limbajul Pascal). Alti operatori sunt: operatori logici, operatori relationali, operatori asupra sirurilor de caractere etc. Toti operatorii pot fi priviti si ca separatori. O constructie aparte utilizat n programe pentru explicarea sau documentarea textului program comentariul. Comentariile sunt delimitate de textul programului folosind anumiti delimitatori. n limb Pascal, un comentariu este scris ntre acoladele } sau ntre secventele (*, *). Programele C++, Java p contine comentarii pe o singur linie si ncep cu //, sau pe mai multe linii si sunt cuprinse ntre /* si */. Alte elemente lexicale ce pot fi prezente ntr-un program sunt etichetele si clauzele. Etichetele sunt siruri de cifre zecimale/hexazecimale sau identificatori folosite n legtur cu o instructiune de salt (goto) pen marcarea unor instructiuni. Clauzele (numite si directive) sunt cuvinte cheie ce desemneaz instructiuni efect n timpul compilrii. Prin sintaxa unui limbaj de programare se ntelege, n general, un ansamblu de reguli privind agreg unittilor lexicale pentru a forma structuri mai complexe (declaratii, instructiuni, module, progra Prezentarea acestor reguli se poate folosind limbajul natural sau mecanisme formalizate. Descrierea sintaxei n limbaj natural poate conduce la neclaritti sau specificatii incomplete. Cu ajutorul mecanismelor formale sintaxa unui limbaj este complet specificat. Cea mai folosit notatie este cunoscut sub numel notatie BNF (Backus-Naum-Form) si a fost folosit pentru prima dat, n anul 1959, la specificarea limbajului Algol-60. Aceast notatie are aceeasi putere generativ cu gramaticile independente de c introduse de N. Chomsky. Totusi, limbajele de programare nu sunt independente de context ci numa portiuni ale acestora pot fi modelate cu ajutorul limbajelor independente de context. Pentru a putea specifica un ntreg limbaj de programare se poate folosi notatia BNF extins. n prezentarea din acest capitol vom utiliza opt metasimboluri: ::= < > { } [ ] | pentru a defini unittile sintactice ale limbajului Pascal. Metasimbolurile < si > sunt folosite pentru delimitarea numelui unei unitti sintactice. Presupunem, de asemenea, existenta unei operatii de concatenare pe multimea unittilor sintactice. Metasimbolul ::=

2 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

apare dup numele unei unitti sintactice si are semnificatia "se defineste prin". Metasimbolul | este utilizat pentru a delimita mai multe variante de definire ale unei unitti sintactice, aceasta fiind obtinut p reuniunea variantelor. Metasimbolurile { si } indic repetarea posibil (de zero sau mai multe ori) simbolurilor pe care le delimiteaz. Pentru a desemna prezenta optional a unor simboluri se util delimitatori, metasimbolurile [ si ]. Vom admite, pentru prescurtare metasimbolul ... care indic continuarea unui sir de valori conform contextului n care apare. Iat cteva exemple: 1. <liter> ::= A ... Z descrie multimea literelor mari; 2. <cifr> ::= 0 ... 9 descrie multimea cifrelor zecimale; 3. <identificator> ::= <liter> { <liter> | <cifr>} descrie modul de formare a identificatorilor: un sir de litere si/sau cifre, primul semn fiind o liter. 4. <secvent cifre> ::= <cifr> { <cifr>} descrie modul de formare a unei secvente de cifre; 5. <ntreg fr semn> ::= <secvent cifre> defineste 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 unittilor sintactice. n cele ce urmeaz vom prefera limbajul natural (n anumite cazuri) si notatia BNF extins (n alte ca cititorul interesat asupra diagramelor sintactice poate consulta, de exemplu: N. Wirth: Systematic Programming: An introduction, Prentice Hall, 1972.

3.2. Tipuri de date. Constante. Variabile. Expresii

Un tip de date este o structur compus din: 1) o multime X de valori numite date si 2) o multime d compozitie pe X (operatii ce se pot efectua cu valori din X). O dat are un singur tip (apartine unei s multimi). Exist limbaje de programare puternic tipizate (n sensul verificrii cu regularitate a apartente unei date la multimea de valori a tipului su, nc din faza de compilare). Astfel de limbaje de programa sunt: Pascal, Modula, Ada etc. Tipurile de date sunt standard sau definite de utilizator. Tipurile definite de utilizator se introduc intermediul unei definitii folosind un cuvnt cheie precum type (n Pascal), typedef (n C) sau class ( limbajele C++ si Java). De asemenea se vor utiliza diverse cuvinte cheie pentru a specifica structura tipu Dac pentru o anumit structur a unui tip nu este stabilit un identificator, spunem c avem de-a cu u anonim. Valorile unui tip de date (elementele multimii X sunt referite fie prin variabile, fie prin constante (literali sau constante simbolice). O locatie de memorie care poate stoca o valoare a unui anumit tip de date s numeste, prin abuz de limbaj, variabil. Orice variabil trebuie s fie declarat pentru a putea fi folosit. O declaratie contine un tip de valori - ce indic: ce se stocheaz, cum se stocheaz si n ce operatii interv valorile stocate - si un identificator pentru a ne referi la variabila ce obiectul declaratiei. Practic o variabil este un obiect caracterizat de tip, adres si valoare, pentru care atributul valoare poate fi modificat. n limbajul Pascal declaratia variabilelor este precedat de cuvntul cheie var:

3 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

var <declaratie variabile>{ <declaratie variabile>} unde <declaratie variabile> ::= <identificator>{,<identificator>}: <tip>. n limbajele C, C++ si Java declaratia variabilelor arat astfel: <tip> <identificator>{,<identificator>} n functie de locul n care apare declaratia unei variabile, acesteia i se pot atribui atribute precum: local, global, static etc. Exist limbaje de programare (de exemplu Java) care definesc o valoare implicit pentru fiecare variab atunci cnd prin program aceasta nu "primeste" nici o valoare. Totusi, cele mai multe limbaje de programare nu ofer acest serviciu si este necesar s se realizeze operatii de "initializare" explicit. Initializarea unei variabile chiar n momentul declarrii acesteia, n limbajele de programare care pe aceasta, se realizeaz folosind o descriere de forma: IdentificatorulTipului IdentificatorulVariabilei = ValoareInit; unde se presupune c ValoareInit este de acelasi tip cu tipul variabilei sau poate fi convertit (transformat foarte usor) ntr-o valoare de acest tip. Prezentarea elementelor unui tip este posibil fie prin intermediul literalilor, fie prin intermediu constantelor simbolice. Constantele simbolice sunt identificatori asociati unor elemente ale anumitor multimi. Declararea unei constante simbolice Pascal se realizeaz conform regulii: const <identificator> = <constant>; unde <constant> este un literal, o expresie constant (n care intervin literali) sau elemente structura limbajul C, constantele simbolice se pot introduce prin intermediul directivei #define sau cuvntului cheie const atunci cnd sunt declarate variabile ce nu pot fi modificate n program. Operatiile cu elemente ale unui tip sunt fie predefinite, fie sunt introduse prin declaratii function sau procedure (n Pascal) sau operator (n C++). Agregarea variabilelor, constantelor si a operatorilor conduce la constructii numite expre Expresiile sunt evaluate n cursul executrii unui program. Rezultatul unei expresii depinde de va 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 referint (pointer), tipuri procedurale. n limbajele C, C++ si Java exist tipu void. Aceast multime notat prin void nseamn fie multimea vid, fie o multime neprecizat. Tipurile de date simple numite si tipuri primitive (sau tipuri standard) se refer la multimi de elemen precum: numere ntregi, numere rationale, valori de adevr (logice sau booleene), caractere, valor apartinnd unei enumerri sau unui interval (subdomeniu). O parte dintre tipurile simple sunt tipur ordinale, adic tipuri caracterizate printr-o multime finit de valori, pe care este definit o ordine liniar si, prin urmare, pentru orice element al unei asemenea multimi se stabileste numrul de ordine ord(.), elementul predecesor pred(.) si cel succesor succ(.). Tipurile ordinale sunt cele care se refer la multi precum: multimea numerelor ntregi, multimea valorilor de adevr, multimea caracterelor, multime valorilor unei enumerri, multimea valorilor dintr-un subdomeniu al uneia dintre multimile anterioa Tipurile rationale (simpl precizie, dubl precizie, precizie extins etc.) nu sunt considerate tipuri ordinal desi sunt tot multimi finite de elemente. Trebuie observat c metoda de reprezentare n memor calculatorului a numerelor rationale ar permite considerarea unei ordini liniare si, elementele unei astfel de multimi ar avea un numr de ordine.

4 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Tipurile ntregi ale limbajului Pascal sunt : a) Tipul Byte : reprezint multimea numerelor ntregi fr semn ncepnd de la 0 pn la 255, reprezen intern folosind 8 biti (un byte). b) Tipul ntreg scurt Shortint : reprezint multimea numerelor ntregi cu semn ncepnd de la -128 pn 127, reprezentate n complement fat de doi, pe 8 biti. c) Tipul ntreg Integer : se refer la domeniul de valori dintre -32768 si 32767, reprezentate pe 16 complement fat de doi. d) Tipul Word : reprezint multimea numerelor naturale de la 0 la 65535 reprezentate pe 16 biti. 5) Tipul ntreg lung Longint : defineste un domeniu reprezentabil, cu semn, pe 32 de biti, n complement fat de doi. Tipuri ntregi ale limbajului Java sunt: a) Tipul byte : este echivalent cu tipul Shortint al limbajului Pascal. b) Tipul short (ntreg scurt): este acelasi cu tipul integer al limbajului Pascal. c) Tipul int (ntreg): corespunde tipului Longint al limbajului Pascal. d) Tipul ntreg lung (long): reprezint multimea numerelor ntregi, reprezentabile cu semn n complemen fat de doi, pe 8 bytes adic 64 de biti. Tipurile de numere rationale ale limbajului Pascal sunt desemnate prin identificatorii standard real (6 bytes), single (4 bytes), double (8 bytes), extended (10 bytes) si comp (8 bytes) si descriu submultimi de num rationale reprezentate n memoria intern n virgul mobil. Tipul de date comp este o multime de num ntregi utilizate n calcule fr parte fractionar. Tipurile de numere rationale ale limbajului Java se mai numesc tipuri flotante. Sunt disponibile multimile float (4 bytes) si double (pe 8 bytes). Ca si pentru reprezentrile single, double, extended si comp al limbajului Pascal, n cazul tipurilor float si double se utilizeaz standardul IEEE 754. Tipul boolean este folosit pentru descrierea multimii valorilor de adevr desemnate prin literalii true si false. Pen declararea unei variabile de tip boolean n limbajele Pascal si Java se foloseste cuvntul boolean. n versiunea 7.0 a limbajului Turbo Pascal au fost introduse nc trei tipuri booleene: bytebool, wordbo longbool. Lungimea reprezentrii este: boolean - 8 biti, bytebool - 8 biti, wordbool - 16 biti, longbool - 32 biti. Referitor la tipul boolean, n Pascal, sunt adevrate: false < true, ord(false) = 0, ord(true) =1, succ(false) = true, pred(true) = false. Limbajul C++ nu are un cuvnt rezervat pentru variabile logice. Totusi, limbajul permite utilizare operatiilor uzuale ale calculului cu propozitii: si, sau, negatia prin intermediul operatorilor: &&, ||, Limbajul trateaz orice valoare nenul drept true si valoarea zero drept false. Valoarea rezultat n urm unei operatii logice este 1 pentru rezultat true si 0 pentru rezultat false. Pentru declararea variabilelor de tip caracter se utilizeaz cuvntul char. O variabil de tip caracter poate avea ca valori coduri Unicode (pe 16 biti, n Java) sau coduri ASCII (pe 8 biti, n Pascal, C, etc.). caracterelor fiind ordonat, au sens functiile: ord, pred si succ. Functia ord(.) ntoarce codul caracteru dintre paranteze. O alt functie utilizat este chr, care transform o valoare ntreag ntr-un caracter c codul Unicode corespunztor. Caracterele pot fi comparate ntre ele pe baza pozitiei lor n setul de caractere Unicode.

5 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Tipul enumerare este reprezentat printr-o multime finit de identificatori separati prin virgule si inclusi ntre pa Componentele acestui tip se consider ordonate, deci se pot aplica functiile: pred, succ si ord. De asemenea se pot realiza comparatii ntre elemente. n limbajul Pascal, enumerarea (R, G, B) poate fi utilizat pentru a introduce tipul RGB, iar ord(R) = 0, pred(B) = G, R<G, etc. n limbajul C, valorile asociate identificatorilor pot fi sub controlul total al programatorului, precum n exemplul: enum {v1 = -100, v2 = 5, v3 = 7, v4 = -10 } v; permitnd astfel o nou metod de definire a constantelor. Unii identificatori pot fi initializati de ctre programator urmnd ca ceilalti s primeasc valori n urma compilrii. n descrierea enum {a, b = 6, c, d} x; vom avea a = 0, b = 6, c = 7, d = 8. Componentele de tip enumerare nu pot fi citite de la un mediu de intrare si nici afisate deoarece este retinut numai pozitia lor n enumerare. Un identificator prezent ntr-o list nu poate fi prezent si ntr-o alt list. Exemple (Pascal):
type A = (mat, fiz, chim, bio, inf); B = (ian, feb, mar, apr, mai, iun, iul, aug, sep, oct, nov, dec);

Exemple (C):
typedef enum {mat, fiz, chim, inf} A; enum {r, g, b} culoare;

Un subdomeniu este definit ca un interval de valori ale unui tip ordinal, numit tip de baz. Este suficient s se precizeze valoarea initial si valoarea final. De exemplu, subdomeniul 6..12 descrie secventa de numere: 6, 7, 8, 9, 10, 11, 12. Functia ORD (n limbajul Pascal) aplicat unui element dintr-un interval furniz numrul de ordine al elementului considerat ca apartinnd tipului de baz. Reprezentarea intern variabilelor de tip subdomeniu se n aceleasi conditii ca reprezentarea variabilelor tipului de baz. Exemple (Pascal):
type culori = (red, green, blue, yellow, black); rgb = red..blue; Mint = 1..100;

Nu se pot defini subdomenii ale tipurilor rationale si nici ale tipurilor structurate, referint etc. Pentru tipurile de date descrise mai sus exist operatii predefinite desemnate prin operatori si fun predefinite. De cele mai multe ori, operatorii sunt binari. Exist ns si operatori unari. Unii dintre operatori sunt extinsi pentru a actiona si asupra datelor structurate. Tipurile de date structurate sunt agregri ale unor tipuri de date deja definite. Limbajele de program actuale permit definirea urmtoarelor tipuri structurate: tablou, sir de caractere, nregistrare, multime, fisier si obiect sau clas. Tipurile fisier vor fi introduse n 4.7, iar conceptele fundamentale privi programarea orientat spre obiecte (definirea claselor) vor fi prezentate n alta parte. Tipul tablou este implementat diferit de la limbaj la limbaj. Vom exemplifica modurile de definire a acestu tip n cazul limbajului Pascal. Modul de utilizare a tablourilor n limbajul C va fi studiat altundeva. Fie T un tip de date oarecare numit tip de baz si I un tip ordinal cu exceptia tipurilor longint sa subdomeniu al acestuia. Prin tip de date tablou cu indici din I si elemente din T se ntelege multimea tuturor functiilor x definite pe I cu valori n multimea T. Un tip tablou poate fi introdus prin declaratia: type <identificator> = array [<tip indice> {,<tip indice>}] of <tip element>;

6 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

unde [ si ] sunt elemente ale limbajului Pascal, iar { si } sunt elemente ale metalimbajului, <tip indice> {, <tip indice>} descrie multimea I, iar <tip element> descrie multimea T din definitie. Multimea I se poate obtine ca produsul cartezian al unui numr oarecare, dar finit de multimi de tip ordinal (fr longint), dar zona din memoria intern utilizat pentru stocarea elementelor tabloului nu trebuie s depseasc o anumit dimensiune, dependent de implementare. Multimea T este orice tip de date cu exceptia tipului fisier. Exemple:
type a b T c d = = = = = array [1..10] of integer; array [-5..5, -10..7] of real; (r, g, b); array [T] of char; array [1..10] of array[-5..5] of array[char] of integer;

Referirea unui element al unui tablou se prin identificatorul tabloului si indexul elementului scris n paranteze drepte: x['a'] dac x este un tablou cu <tip indice> = char. Dac un tablou este multi-dimensional, referirea la o component se poate n mai multe moduri. De exemplu, dac x este un tablou bidimensio atunci x[i,j] si x[i][j] reprezint acelasi element. Limbajul Borland Pascal permite definirea unui tip numit sir de caractere (string). Lungimea sirurilor est cel putin 0 (null) si cel mult 255. Din punct de vedere al reprezentrii interne, pentru un sir de caractere aloc n+1 bytes (n conventia ASCII pe 8 biti), unde n este lungimea efectiv a sirului. Octetul cu adre relativ zero este rezervat pentru memorarea lungimii efective. Declararea unui tip sir de caracter se supune regulii:
type identificator = string[lungime_maxima] | string;

unde <lungime_maxim> stabileste limita superioar a numrului de caractere ce pot fi stocate; c parantezele drepte si aceast limit lipsesc se consider siruri de lungime maxim 255. Un caracter din sir este accesat similar ca elementul unui tablou, lungimea efectiv a sirului x este obtinut fie prin ord(x[0]) sau prin length(x). Exemplu: Definitia Type T30 = string[30]; introduce T30 ca fiind multimea sirurilor de caractere cu cel mult caractere. Tipul de date nregistrare (record) se introduce n Pascal prin: type <identificator> = <tip record>; unde <tip record > este definit prin urmtoarele reguli BNF: <tip record> ::= record <list de cmpuri> [;]end <list de cmpuri> ::= <parte fix> [; <parte variante>] | <parte variante> <parte fix> ::= <sectiune record> {; <sectiune record> } <sectiune record> ::= <identificator>{, <identificator>}: <tip> <parte variante> ::= case[ <identificator>:] <tip> of <variant> {; <variant>} <variant> ::= <constant> {, <constant>} :[<list de cmpuri>] [;]) adic un element de tip record este n general format dintr-o parte fix si una variabil, fiecare dintre acestea fiind alctuit din cmpuri. Exemple:

7 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

1) Tipuri de date predefinite n Pascal: const maxim = 100000;{constanta este de tip longint} pi = 3.14159; mint:integer :=maxint-1; {o expresie constanta} pi2 = pi / 2; {o alta expresie constanta} stop:char = 'Z'; type T1 = 0..maxint; { subdomeniu al tipului integer; maxint este constanta predefinita 32767} const st:T1 = 100; var x,y:T1; {x, y sunt variabile de tip T1} a, b : real; { a, b sunt variabile de tip real} flag, poz: boolean; {flag, poz sunt variabile logice} ch: char; i,j,k: integer; wi, wj, wk: word; li, lj,lk:longint; u:single; v:double; w:extended; {tipuri flotante - standardul IEEE 754} 2) Tipuri de date structurate n Pascal: Type Data = record ziua: 1..31; luna: 1..12; anul: 1..9999 end; Figura = (triunghi, patrulater, cerc, elipsa); Stare = array [Figura] of boolean; Rvariante = record {parte fixa} cod:byte; nume: string[30]; dataNasterii: Data; {parte variabila}

8 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

case studii : (medii, superioare) of medii:(NrClase:byte; Scoala:string[30];); superioare: (Facultatea:string[20]; specializarea: string[15]) end; {un singur end} Var d:Data; f: Figura; S: Stare; R: Rvariante; {Constante denumite impropriu constante cu tip, de fapt sunt variabile initializate} const d1:Data =(ziua:30; luna:5; anul:1999); d2:Figura = cerc; d3: Stare = (true, true, false, false); d4:Rvariante = (cod:123; nume:'Ionescu'; dataNasterii: (ziua:12; luna:8; anul:2000); studii:medii; Nrclase:12; Scoala:'Liceul XXXX'); Un identificator de cmp trebuie s fie unic numai n nregistrarea unde a fost definit. Un cmp al u variabile de tip record este referit prin identificatorul variabilei si identificatorul cmpului separate printr-un punct. Exemplu: R.cod, R.nume, R.dataNasterii.ziua, R.studii, R.Nrclase, R.Scoala, d.anul etc. Tipul multime (prezent n Pascal, Modula etc.) permite definirea multimilor ale cror elemente sunt multimi. Fie T un tip scalar diferit de real si de tipurile flotante. Se numeste tip set cu tipul de baz T multimea P(T) format din toate submultimile multimii T. Definitia unui tip set se prin: type <identificator> = set of <Tip>; unde <Tip> este tipul de baz. Constructia unui element de tip set urmeaz regulile: <set> ::= [list de elemente] | [] <list de elemente> ::= <element> {, <element>} <element> ::= <expresie> | <expresie>..<expresie> cu [ , ] elemente ale limbajului Pascal, |, { si } simboluri ale metalimbajului, iar <expresie> reprezint o expresie a crei valoare apartine tipului de baz. Astfel, pentru a ne referi la multimea vid folosim notatia [], pentru a definii multimea vocalelor scriem ['a', 'e', 'i', 'o', 'u'], iar pentru a definii primele cinci numere naturale nenule putem scrie n una din formele: [1, 2, 3, 4, 5], [5, 4, 3, 2, 1], [1..5], [1..3, 4, 5] etc. Cu variabile de tip set se pot efectua urmtoarele operatii: + (reuniune), - (diferent), * (intersectie) ce au ca rezultat un element de tip set, sau operatii relationale cu rezultat de tip logic: = (egalitate), <= (inclus n), >= (include pe), <> (diferit). Apartenenta unui element t la o multime A este dat de valoarea de adev expresiei t in A. Aceast expresie este adevrat dac primul operand - element de tip ordinal - este element
9 of 48 12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

al operandului al doilea - cu multimea de baz compatibil cu t. Dac tipul de baz are n valori, o variabil de tip set corespunztoare tipului de baz va fi reprezenta memorie folosind n biti, depusi ntr-o zon de memorie contigu cu lungimea (n div 8) + 1 bytes dac n nu este divizibil cu 8, respectiv (n div 8) bytes dac n este multiplu de 8. Prin urmare tipul set of char reprezentat pe 32 de bytes. Operatorii sunt simboluri care specific operatii efectuate asupra unor variabile sau constante denu operanzi. Combinatiile valide de operanzi si 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 b (adunare, scdere, nmultire, mprtire, obtinerea ctului si restului mprtirii a dou numere ntre operatori logici (si, sau, negare), operatori relationali (egal, diferit, mai mic, mai mic sau egal, mai mare, mai mare sau egal, apartine), operatori la nivel de bit (si, sau, negare, sau exclusiv, deplasare stnga deplasare dreapta), operatori combinati (n limbajele C, C++ si Java), operatori asupra multimilor (reuniune, intersectie, diferent), operatori asupra sirurilor de caractere (concatenare) precum si a operatori. Evaluarea expresiilor trebuie s tin seama de pozitia parantezelor si de propriettile operatorilo (precedent, asociativitate, conversii implicite n cazul tipurilor compatibile, conversii explicite). Precedenta stabileste ordinea de evaluare a operatiilor n cazul expresiilor care contin mai multi operatori diferiti. Dac ntr-o expresie se ntlnesc operatii cu aceeasi precedent atunci ordinea de evaluare este dat de tipul asociativittii (de la stnga la dreapta sau de la dreapta la stnga). Cnd ntr-o expresie apar operati operanzi de tipuri diferite, nainte de efectuarea operatiei are loc un proces de conversie implicit (cnd nu se semnaleaz explicit si nu apare fenomenul de incompatibiltate) prin care tipul cu cardinalul mai m promovat ctre tipul mai bogat (se presupune aici c natura elementelor celor dou tipuri este aceasi).

3.3. Programare n C
Limbajul C a fost creat la AT & T Bell Laboratories in anul 1972 de Dennis Ritchie. Versiunea stan limbajului C pana in anul 1988 a fost cea furnizata odata cu sistemul de operare UNIX si descrisa in [14]. In anul 1983 a inceput redactarea standardului ANSI pentru limbajul C. Standardul ANSI a fost finalizat in anul 1990. 3.3.1. Structura programelor C In limbajul C programul este o colectie de module distincte numite functii, organizate in una sau mai m unitati de translatare. Fiecare unitate de translatare poate fi compilata (analizata lexical si sintactic) separat. O unitate de translatare trebuie sa contina cel putin o declaratie sau o definitie de functie. Ea consta din fisierul sursa impreuna cu oricare fisier antet si fisiere sursa incluse prin directiva #include. O unitate de translatare, prin compilare, conduce la un fisier obiect (.obj) relocabil. Directivele precedate de delimitatorul # se numesc directive preprocesor, acestea specificand opera anterioare procesului de compilare ce sunt efectuate de o componenta a mediului de programare preprocesor. O declaratie C specifica atributele unui identificator sau multime de identificatori. Regulile sintactice ce stau la baza scrierii declaratiilor sunt redate prin: <declaratie> ::= <specificator declaratie> [ <lista declaratori de initializare> ] ;

10 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

<specificator declaratie> ::= <clasa de memorare> [ <specificator declaratie> ] | <specificator tip> [ <specificator declaratie> ] | <calificator tip> [ <specificator declara#ie> ] <lista declaratori de initializate> ::= < declarator initializare> | <lista declaratori initializare>, <declarator initializare> <declarator initializare> ::= <declarator> | <declarator> = <initializare> A face o declaratie nu presupune si alocarea memoriei pentru indentificatorul declarat. Exista situati alocarea se realizeaza in alta unitate de translatare (cazul datelor externe). Declaratia unui identificator asociaza numelui in mod explicit sau implicit o serie de atribute din m urmatoare: Clasa de memorare - localizeaza zona de memorie in care este plasat elementul declarat (zona de date, un registru al procesorului, stiva mediului de programare, zona de alocare dinamica) si delimiteaza durata alocarii (intreg timpul de executare a programului, executarea unei functii sau a unui bloc etc.). Domeniul numelui - reprezinta portiunea din program in care poate fi utilizat identificatorul pentru accesarea informatiei asociate si este determinat de pozitia declaratiei. Durata de stocare - reprezinta perioada cat elementul asociat exista efectiv in memorie. Legatura - indica modul de asociere a unui identificator cu un anumit obiect sau functie, in procesul de editare a legaturilor. Tipul datei (standard sau definit de utilizator) - descrie informatia continuta de elementul definit de identificator. Clasa de memorare este specificata prin unul dintre cuvintele cheie: typedef, extern, static, auto, register. Declaratia auto se poate utiliza pentru variabile temporare - alocate folosind stiva, cu domeniul local. Variabilele declarate in interiorul unui bloc sunt implicit locale, deci auto este rar utilizat. In limbajul C clasic, o declaratie register reprezinta un apel la compilator pentru a stoca o variabila int sau char intr-un registru al procesorului pentru a creste viteza de executare. Versiunile actuale permit specificarea register pentru orice tip, semnificatia apelului fiind de optimizare a timpului de acces. Specificatorul typedef indica faptul ca nu se declara o variabila sau functie de un anumit tip, ci se asociaza un nume tipului de date. Sintaxa este: typedef <definitie tip> <identificator>; Specificatorul static poate sa apara in declaratii locale de variabile pentru a indica durata statica sau in declaratii globale d functii si de variabile pentru a indica legatura interna. Specificatorul extern este utilizat pentru declaratii

11 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

de functii sau variabile locale sau globale pentru a indica legatura extera si durata statica. In C (precum si in Pascal), declaratia unei variabile trebuie sa preceada orice referire a ei. Ea poate aparea in exteriorul oricarei functii, in lista de parametri formali ai unei functii sau la inceputul unui bloc. Domeniul numelui este regiunea dintr-un program C in care identificatorul este "vizibil". Pozitia declaratiei determina urmatoarele domenii: Domeniul bloc - caracterizeaza identificatorii locali (identificatorii declarati in interiorul unui bloc si au domeniul cuprins intre declaratie si sfarsitul blocului; parametrii formali din definitia unei functii au ca domeniu blocul functiei). Domeniul fisier - caracterizeaza identificatorii declarati in exteriorul oricarei functii - numiti identificatori globali - si care au domeniul cuprins intre declaratie si sfarsitul fisierului. Domeniul functie - aplicabil pentru etichetele instructiunilor si este blocul functiei. Domeniul prototip - definit pentru identificatorii specificati in lista de parametrii din prototipul unei functii - si care au domeniul limitat la acel prototip. Partea din domeniu in care informatia asociata este accesibila se numeste zona de vizibilitate. O declaratie a unui identificator este vizibila in tot domeniul sau mai putin blocurile sau functiile in care identificatorul este redeclarat. Pentru identificatorii globali se poate repeta declaratia, dar initializarea trebuie sa se faca o singura data. Din punct de vedere al duratei de stocare, sunt posibile trei situatii: Durata statica: Obiectele cu durata statica au alocata zona de memorie pe toata durata de executare a programului. Toate variabilele globale au durata statica. Alte variabile pot avea aceasta calitate prin utilizarea specificatorilor static sau extern. Durata locala: Obiectele cu durata locala sunt cele automatice - spatiul de memorare se aloca (in stiva sau in registru) la intrarea in executare a blocului sau functiei si este eliberat la iesirea din bloc sau din functie. In concluzie, orice obiect automatic dispare la incheierea duratei sale de viata, deci informatia continuta de acesta se pierde. Durata dinamica: Pentru obiectele cu durata dinamica, zona de memorie este alocata si eliberata la initiativa programatorului prin apelarea unor functii C (de exemplu: malloc, free). Un identificator declarat in diferite domenii, de mai multe ori, sau redeclarat in acelasi domeniu se poat referi la acelasi obiect sau functie prin procesul numit legare. Legarea poate fi interna, externa sau unic Daca un identificator are domeniul fisier si clasa de memorare static, el se supune legarii interne. Daca un identificator are clasa de memorare extern, el se supune aceluiasi tip de legare precum orice declarati vizibila a identificatorului cu domeniu fisier; daca nu exista declaratii vizibile cu domeniul fisier, se supune implicit legarii externe. Pentru identificatorii cu legatura externa sunt permise mai multe declaratii referinta, dar trebuie sa existe o singura definitie. Functiile au implicit legatura externa si durata statica. In regulile sintactice de mai sus, <calificator tip> se refera la modificatorii de acces (const si volatile) care controleaza modul in care valoarea unei variabile poate fi modificata. O variabila de tip const nu poate fi modificata in timpul executiei programului. Declaratia volatile specifica faptul ca variabila poate fi modificata din exteriorul programului sau intr-un mod care nu rezulta explicit prin program (de exempl

12 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

prin utilizarea adresei variabilei). Specificatorii de tip indica modul de alocare asociat unei variabile sau tipul rezultatului unei functii. In exista urmatoarele categorii de tipuri: tipuri de functii, tipuri de variabile si tipul void. Variabilele pot fi de tip scalar, de tip structurat sau de tip uniune. Tipurile scalare sunt tipuri aritmetice si tipul pointer . Tipurile structurate cuprind tablourile si inregistrarile (numite in C, structuri). In categoria tipuri aritmetice intra multimile 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 si tipul obtinut prin enumerare. Dimensiunea zonei de memorie ocupata de un tip sau de o variabila se poate afla prin intermediu operatorului sizeof cu sintaxa sizeof (<tip>) sizeof <expresie>. Tipul void indica absenta oricarei valori si este utilizat in urmatoarele situatii: declaratia unei functii fara parametrii sau fara rezultat, tipul pointer generic si conversii de tip pentru pointeri. Literalii sunt si ei afectati de existenta modificatorilor de tip prin indicarea unui sufix (U, u, L, l, f, F). Efectul sufixului asociat unui literal intreg este ilustrat prin situatiile: U sau u - unsigned int sau unsigned long int (in functie de valoare); L sau l - long int sau unsigned long int (in functie de valoare); UL, ul, Ul, uL - unsigned long int. Un literal de tip numar zecimal, este automat de tip double; daca se utilizeaza sufixul F sau f va fi considerat de tip float, iar daca se utilizeaza sufixul L sau l, va fi considerat de tip long double. Tabloul este o lista de elemente de acelasi tip plasate succesiv intr-o zona contigua de memorie. Nu exist limita pentru numarul dimensiunilor tabloului. Structura este o colectie de date eterogene (corespunde tipului record din limbajul Pascal). O declaratie de structura precizeaza identificatorii si tipurile elementelor componente si constituie o definitie a unui tip d date nou. Acestui tip i se poate asocia un nume. In cazul general, sintaxa declaratiei unei structuri este: <declaratie structura> ::= struct < id _tip> { <tip _camp _1> <id _camp _1>; <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 cuvant cheie pentru construirea unui tip inregistrare; <id_tip> - este un identificator ce desemneaza numele tipului structura ce este declarat; <tip_camp_i> - tipul campului i;

13 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

<id_camp_i> - identificatorul campului i (campurile structurii se mai numesc si membrii structurii); <lista identificatori de tip struct> - lista identificatorilor declarati. Daca numele tipului <id_tip> lipseste, structura se numeste anonima. Daca lista identificatorilor declarati lipseste, s-a definit doar noul tip structura. Cel putin una dintre aceste specificatii trebuie sa existe. Daca <id_tip> este prezent, ulterior, se pot declara noi variabile de tip structura prin intermediul declaratiei: struct <id_tip> <lista noilor identificatori>; Referirea unui membru al unei variabile de tip structura se face folosind operatorul de selectie (.) Int expresie care precizeaza identificatorul variabilei si al campului. Structurarea datelor la nivel de bit este posibila in limbajul C prin definirea de campuri de biti. Ace facilitate este utila pentru scrierea aplicatiilor de control al dispozitivelor fizice s.a. Campurile de biti se pot declara ca membrii ai unei structuri astfel: struct <id_tip> { <tip_ camp_ 1> <id_camp_1>:lungime _1; <tip_camp_ 2> <id_camp _2>:lungime_ 2; ... <tip_camp_i> <id _camp_ i>:lungime_i; ... <tip_ camp_n> <id_ camp_n>:lungime_n; } <lista identificatori de tip struct>; Totusi, pentru c|mpurile de biti, < tip_ camp_i> poate fi doar int, signed sau unsigned, lungime _i este o constanta intreaga cu valoarea in domeniul 0-15. In exemplul: struct s _bit { unsigned a:1; unsigned b:3; unsigned :4; unsigned c:3; unsigned d:2; } s; pentru variabila s se vor aloca 16 biti (numerotati 0-15), ce pot fi accesati prin: s.a (bitul 0); s.b (bitii 1-3); s.c (bitii 8-10); s.d (bitii 11,12). Observam ca bitii 4-7 nu pot fi accesati, pentru ei nu s-a specificat nici un identificator de camp.

14 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Alocarea campurilor poate ridica probleme de portabilitate, deoarece organizarea memoriei depinde sistemul de calcul. Uniunile sunt entitati care pot contine (la momente de timp diferite) obiecte de tipuri diferite. Practic, mai mu variabile sunt suprapuse in acelasi spatiu de memorie. Sintaxa declaratiei este similara cu cea a structurii dar identificatorii declarati ca membrii reprezinta numele cu care sunt referite diferitele tipuri de variabile ce ocupa aceeasi zona de memorie: <declaratie 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>; Spatiul alocat in memorie corespunde tipului cu dimensiune maxima. Tipurile uniune sunt utile pen conversii de date, in implementarea programelor de comunicatie etc. Tipul enumerare consta dintr-un ansamblu de constante intregi (cel putin un element), fiecare fiind asociata cate identificator. Constanta unui element al enumerarii este fie asociata implicit, fie explicit. Implicit, pri element are asociata valoarea 0, iar pentru restul este valoarea _precedenta+1. Cel mai simplu program C este constituit din directive preprocesor, declaratii globale si functii. Printr functii trebuie sa existe una cu numele "main " cu care va incepe executarea programului. Chiar daca programul este organizat pe mai multe fisiere sursa, numai intr-un singur fisier, numai o singura functie poate purta numele "main". Celelalte functii sunt subprograme definite de programator sau functii din biblioteca de subprograme. Limbajul C nu contine functii predefinite cum sunt cele din unitatea System a mediului Borland Pascal. Functiile din bibliotecile C sunt declarate impreuna cu constantele, tipurile si variabilele globale asociate, in fisiere antet, cu extensia ".h", situate in subarborele include al arborelui asociat mediului de programare. Operatiile de intrare-iesire necesita specificarea fisierului stdio.h, incadrat de delimitatoriii < si >, intr-o directiv{ # include. Fisierele antet ale programatorului vor fi incadrate folosind delimitatorul ". O functie C are structura: <tip_ rezultat> <id_functie> (<lista _parametri_ formali>){ declaratii_locale secventa_instructiuni }
15 of 48 12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

unde <tip_ rezultat> indica tipul rezultatului returnat de functie, <id _functie> reprezinta numele (identificatorul) functiei, iar <lista_parametri_ formali> consta in enumerarea declaratiilor parametrilor functiei sub forma: <tip_ parametru> <id_ parametru> [ ,<tip_parametru> <id _parametru>] Acoladele { sunt delimitatori ce incadreaza o instructiune compusa (bloc) alcatuita din declaratii si instructiuni. }

Secventa de instructiuni a functiilor pentru care <tip _rezultat> este diferit de tipul void, trebuie sa contina o instructiune return, cu sintaxa generala: return <expresie> Rezultatul functiei este valoarea expresiei. Functia main poate avea parametri si poate intoarce un rezultat. In exemplele ce urmeaza functia main nu va avea parametri si nu va intoarce nici un rezultat. Exemplul 3.3.1. (Program C pentru implementarea metodei bisectiei) /* declaratii pentru functiile din biblioteci */ # include <stdio.h> /* biblioteca functiilor de intrare/iesire */ # include <math.h> /* biblioteca necesara pentru functia abs */ /* declaratii macrodefinitii: Preprocesorul inlocuieste in tot textul sursa, partea stanga (epsilon, pi) cu sirul din dreapta. O conventie uzuala este folosirea literelor mari pentru identificatori, pentru a usu recunoasterea lor in program. Sirul se termina cu linie noua. */ # define EPSILON 1E-7 # define PI 3.1415926 /* prototipuri ale functiilor definite in program */ float f(float); float bisectie( float, float ); /* definitii ale functiilor */ float f( float x){ return (x-1)*(x-2)*(x-PI)*(x-2*PI); } float bisectie(float a, float b){ float c, fc; int sfa; sfa = (f(a) < 0.0);

16 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

do { c = (a+b)/2; fc = f(c); if ((fc<0) == sfa) a=c; else b=c; } while (fabs(b-a) >= EPSILON && fabs(fc) <= EPSILON); return c; } /* functia main */ void main (void){ float a,b; printf("\ n"); do { printf("Introduceti a:"); scanf("%f",&a); printf("Introduceti b:"); scanf("%f",&b); } while (f(a)*f(b) >= 0); printf("Radacina ecuatiei este: %15.8f \ n",bisectie(a,b)); } Exemplul 3.3.2. (Ridicarea la putere a unui numar complex dat sub forma algebrica se poate face cu ajutorul conversiei in forma trigonometrica si al utilizarii formulei lui Moivre). Se solicita cititorului scrierea unei functii main pentru folosirea functiilor definite in cadrul acestui exemplu. #include <stdlib.h> /* pentru iabs */ #include <math.h> /* pentru sqrt, atan2, sin, cos etc. */
typedef struct {float mod; float arg;}F_TRIG; typedef struct {float re; float im;} F_ALG; float z_power(float x, int n){ float p=1.0; int m; m = iabs(n); if (m == 0) return 1.0; else { while (m--) p *=x; } if (n<0) return 1.0/p; else return p; } F_TRIG cnv_trig(F_ALG t) { F_TRIG s; s.mod = sqrt(t.re*t.re+t.im*t.im); s.arg = atan2(t.im, t.re); return s; } F_ARG cnv_alg (F_TRIG s){

17 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu


F_ALG t ; t.re = s.mod * cos (s.arg); t.im = s.mod *sin(s.arg); return t; } F_ALG c_power(F_ALG z, int n) { F_TRIG s = cnv_trig(z); s.mod:=int_power(s.mod, n); s.arg *= n; return cnv_alg(s);}

file:///C:/ACTIV/Proc/FINAL/C4.HTM

In exemplele de mai sus am folosit functiile printf si scanf. De asemenea am folosit mai multi operatori. Acestea vor fi prezentate in urmatoarele subsectiuni. 3.3.2. Functiile de intrare-iesire pentru consola Consola sau dispozitivul standard de intrare-iesire reprezentate de tastatura (zona de date - stdin ) si ecran (zonele de date - stdout si stderr) permit utilizatorului interactiunea cu programul aflat in executare. posibile operatii de citire/scriere fara formatare si operatii de citire/scriere cu formatare. Operatii de citire/scriere fara formatare. Acestea permit lucrul cu caractere (char ) sau cu siruri de caractere (* char). Pentru citirea unui caracter din stdin pot fi utilizate functiile: int getchar(void), int getche(void ) si int getch( void), ultimele doua variante nefiind prevazute de standardul ANSI, dar sunt prezente in vers Borland (fisierul antet conio.h). Functia getchar intoarce primul caracter din stdin , care corespunde primei taste apasate, dar numai dupa apasarea tastei Enter. Caracterul este transformat in intreg fara semn. In cazul unei erori sau la intalnirea combinatiei EOF (sfarsit de fisier) functia intoarce valoa (codificata prin EOF). Functia getche asteapta apasarea unei taste si intoarce caracterul corespunzator pe care il afiseaza pe ecran (nu e nevoie de Enter ). Functia getch este similara cu getche(), dar nu afiseaza ecoul pe ecran. Pentru scrierea unui caracter la stdout se utilizeaza functia int putchar (int c) care afiseaza pe ecran caracterul cu codul ASCII c. Daca operatia reuseste, intoarce caracterul afisat, iar in caz de esec valoare EOF (-1). Pentru citirea (resp. scrierea) sirurilor de caractere se lucreaza cu functia gets (respectiv puts). Functia cu prototipul char *gets (char *s) citeste caractere din stdin si le depune in zona de date de la adresa s, pana la apasarea tastei Enter. In sir, tastei Enter ii va corespunde caracterul '\0'. Daca operatia reuseste, functia intoarce adresa sirului, altfel valoarea NULL ( = 0 ). Functia cu prototipul int puts( const char *s) afiseaza pe ecran sirul de la adresa s sau o constanta sir de caractere (secventa de caractere intre ghilimele) si apoi trece la linie noua. La succes, functia intoarce ultimul caracter, altfel valoarea EOF. Operatii de citire/scriere cu formatare. La citire, formatarea specifica conversia datelor de la reprezentarea externa in reprezentarea binara. Pentru operatia ce scriere se efectueaza conversia inversa. Pentru citirea datelor se utilizeaza functia scanf cu prototipul: int scanf( const char * format [ , lista_adrese_ variabile] ); iar pentru scrierea datelor se utilizeaza functia printf cu prototipul: int printf( const char *format, lista_valori);

18 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Sirul de caractere format poate contine in general: I. specificatori de format: siruri precedate de caracterul '%' care descriu fiecare camp asteptat; II. caractere de spatiere: spatiu (' '), tab ('\t'), linie noua ('\n'); III. orice alt caracter Unicode. Fiecarei variabile din lista ii corespunde o specificatie de format (tipul I.). Functia scanf intoarce numarul de campuri citite si depuse la adresele din lista. Daca nu s-a stocat nici o valoare, functia intoarce valoarea 0. Functia printf intoarce numarul de octeti transferati sau EOF in caz de esec. Functia scanf citeste succesiv caractere din stdin pe care le interpreteaza prin compararea succesiva a caracterului citit cu informatia curenta din sirul format. Prezenta unui caracter de tip II determina citire fara memorare a secventei pana la intalnirea unui caracter de tip I sau III. Prezenta unui caracter de tip III determina citirea fara stocare a caracterului curent de la tastatura, daca este identic. La scriere, caracterele de tip II si III se afiseaza pe ecran asa cum apar in sirul format. Forma generala a unui descriptor pentru scriere este: % [ flags] [ width] [ .prec] [ lmod] type specificatiile dintre [ si ] fiind optionale. Elementele de mai sus au urmatoarea semnificatie: flags - poate fi unul dintre semnele: +, -, 0, spatiu, #. Semnele au urmatoarea semnificatie: - : aliniere la stanga a argumentului in cadrul campului; + : numerele vor fi obligatoriu tiparite cu semn; 0 : indica completarea la stanga cu zerouri (la numere); spatiu: daca primul caracter nu e semnul, se va afisa un spatiu; width: este un numar care specifica latimea minima a campului. Argumentul corespunzator va fi afisat pe un camp cu latime cel putin width. Daca sunt mai putine caractere de scris, se va completa campul cu spatii la stanga (implicit) sau la dreapta, daca s-a specificat flagul - . Daca s-a specificat flagul 0, se va completa la stanga cu zero. Daca width este caracterul *, atunci latimea este data de urmatorul argument din lista (trebuie sa fie neaparat un int). prec: este un numar care specifica precizia de scriere; pentru %s prec indica numarul maxim de caractere ce se va scrie; pentru %e, %E si %f prec indica numarul de zecimale; pentru %g si %G prec indica numarul de cifre semnificative, iar la descriptorii pentru intregi indica numarul minim de cifre. Daca prec este *, atunci se considera ca latimea de scriere este data de urmatorul argument din lista, 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 utilizeaza urmatoarele caractere: d, i (int ) - notatie zecimala cu semn; 0 (int ) - notatie in baza 16 fara semn; x, X (int ) - notatie in baza 16 fara semn cu abcdef pentr x si ABCDEF pentru X; u (int ) - notatie zecimala fara semn; c (int ) - un caracter; s (char *) - sir de caractere terminat cu '\0' ; f (double ) - numarul in virgula mobila cu format standard; e, E (double ) - numarul in virgula mobila cu format exponential; g, G (double ) - in loc de f, e, E; p (void *) - se tipareste argumentul ca adresa; % - se tipareste %.

19 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Forma generala a unui descriptor pentru citire este: % [ *] [ width] [ lmod] type unde: * - suprima atribuirea urmatorului camp din stdin la urmatoarea variabila; width, lmod - ca mai sus; type - descrie tipul de conversie. Cele mai importante specificatii de conversie sunt: d (int *) - intreg zecimal; i (int *) - intreg oarecare (zecimal, octal sau hexa); o (int *) - intreg octal; u (unsigned int *) intreg zecimal fara semn; x (int *) - intreg hexa, c (char *) - caractere; s (char *) - sir de caractere (se va incheia cu '\0 '); e, f, g (float *) - numere in virgula mobila; p (void *) - valoarea unei adrese asa cum e tiparita de printf. In descrierea de mai sus, intre paranteze se indica tipul argumentului supus operatiei de intrare-iesire Notatia tip * inseamna adresa unei locatii de tipul tip. 3.3.3. Operatori si expresii Operatorii sunt simboluri care descriu operatii efectuate asupra unor variabile sau constante (numite generic operanzi). O combinatie corecta de operatori, variabile, constante, apeluri de functii reprezin expresie. Pentru constructia expresiilor, limbajul C ofera o gama foarte larga de operatori. Operatorul de atribuire. Operatorul de atribuire (=) permite crearea unei expresii de forma: <variabila> = <expresie> ce se evalueaza de la dreapta la stanga. Dupa evaluarea membrului drept, valoarea rezultata este inscrisa in <variabila>, iar intreaga constructie are valoarea variabilei dupa inscriere. Operatori aritmetici. Operatorii aritmetici sunt: + (adunare), - (scadere), * (inmultire), / (impartire), % (impartire mo impartitor). Ordinea operatiilor este cea binecunoscuta, dar se pot utiliza paranteze pentru schimbare ordinii operatiilor. Pentru scrierea instructiunii de atribuire <variabila> = <variabila> + 1; se poate uti forma prescurtata <variabila>++, operatorul ++ fiind numit operator de incrementare. Exista, de asemenea, si un operator de decrementare (--): <variabila>--; ce este echivalentul instructiunii: <variabila> <variabila> - 1; Operatori logici si relationali. Pentru scrierea expressilor booleene se utilizeaza operatorii logici si operatorii relationali. Exista trei operatori logici: || (SAU logic - SAU INCLUSIV), && (SI logic), ! (NU logic) Operatorii relationali intalniti in limbajul C sunt urmatorii: < (mai mic strict), > (mai mare strict), <= (m mic sau egal), >= (mai mare sau egal), == (egal cu), != (diferit de). Ori de cate ori relatia este falsa s genereaza valoarea 0, valoarea 1 fiind generata atunci cand relatia este adevarata. Trebuie evidenti operatorii aritmetici au prioritate fata de operatorii relationali. Operatori la nivel de bit. Operatorii la nivel de bit se pot aplica operanzilor de tip intreg (char, int, short, long , cu sau fara semn): & (SI logic la nivel de bit), (SAU logic la nivel de bit), ^ (SAU exclusiv la nivel de bit), << (deplasare stanga), >> (deplasare dreapta) si (negare la nivel de bit). Operatori de atribuire combinati. Pentru realizarea atribuirii <variabila> = <variabila> <operator> <var _sau_const>;

20 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

se pot utiliza operatorii de atribuire combinati: += (atribuire cu adunare), -= (atribuire cu scadere), (atribuire cu inmultire), /= (atribuire cu impartire), %= (atribuire cu impartire modulo); expresia fiind scrisa prescurtat: <variabila> <operator>= <var _sau_const>; Operatorul virgula. In limbajul C, virgula (,) este un operator binar, care leaga expresii oarecare. Constructia <expresie_ 1>, <expresie_2> este una corecta, constand din evaluarea celor doua expresii, in ordinea in care apar, valoarea intregii constructii fiind data de valoarea lui <expresie_2>. Asocierea operatorului virgula se face de la stanga la dreapta, astfel incat o expresie de forma e1,e2,e3 este echivalenta cu: (e1, e2), e3. Exemple: 1) # define Schimba(a,b) (temp=a, a=b, b=temp) 2) for(i=0, j=1; i<n; i+=1, j+=1) printf("%d, %d", i,j); Operatorul conditional (?:) Operatorul conditional este o constructie decizionala a limbajului C care are urmatoarea <Expresie-booleana> ? <expresie_ 1> : <expresie _2>; avand urmatorul inteles: Daca <Expresie-booleana> este adevarata atunci intreaga expresie conditionala are valoarea <expresie_1>, in caz contrar, valoarea expresiei conditionale fiind valoarea <expresie_2>. Exemple: 1) # define Semn(x) ((x<0) ? (-1) : (1)) 2) e = (x<0) ? x*x+3 : sqrt(x); Alti operatori: In aceasta categorie includem operatorii specifici tipului referinta, operatorii pentru selectarea elemente unui tip de date structurat, precum si operatorii introdusi de extensia C++. 3.3.4. Instructiuni C Cea mai simpla instructiune C este instructiunea <expresie>; ce ofera un echivalent in C pentru urmatoarele instructiuni Pascal: atribuire, apel de procedura, instructiunea vida. O secventa de instructiuni incadrata de acolade este considerata ca o singura instructiune si este numita instructiune compusa sau bloc. Spre deosebire de instructiunea compusa a limbajului Pascal, instructiunea compusa din limbajul poate contine atat declaratii cat si instructiuni, declaratiile fiind pozitionate la inceputul blocului. In implicit, identificatorii declarati in blocul delimitat de acolade, au ca domeniu de vizibilitate blocul, iar timpul de viata este limitat la timpul de executare a blocului. Programarea unei structuri decizionale, in C, poate fi realizata folosind: instructiunea if...else ; operatorul conditional (?:) si instructiunea switch. Sintaxa instructiunii if...else este: <instructiune_if> ::= if ( <Expresie>) <Instructiune _T>; if ( <Expresie>) <Instructiune_ T> else <Instructiune_ F> unde: <Expresie> are o valoare de tip scalar reprezentand o constructie de tip expresie, <Instructiune_ T>

21 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

reprezinta o instructiune C care se va executa cand <Expresie> are o valoare nenula (adevarat), <Instructiune_ F> reprezinta acea instructiune C ce se va executa pentru valoare 0 (false) a lui <Expresie>. Conform celor de mai sus, constructia: e1 ? e2 : e3; poate inlocui instructiunea if...else: if (e1) e2; else e3; Atunci cand o selectie multipla este controlata de valoarea unei singure expresii, se poate utiliza instructiunea switch , un pseudo-echivalent C a constructiei Pascal case . Sintaxa instructiunii switch este: <instructiune_switch> ::= switch ( <expresie>) { case <const_ 1> : <lista_ instructiuni> [ break;] case <const_2> : <lista _instructiuni> [ break;] ... [ default:] <lista_instructiuni> [ break ;] } unde: <expresie> este o o expresie cu valoare intreaga (tip intreg sau enumerare); <const_1>, <const _2>, ... sunt constante de selectie, cu valori distincte, convertibile la tipul expresiei <expresie>, iar <lista _instructiuni> este o secventa de instructiuni C. Fiecare eticheta case indica o singura constanta, dar se pot asocia mai multe etichete case , scrise consecutiv, pentru aceeasi secventa de instructiuni. Instructiunea break intrerupe lista de instructiuni si duce la incheierea instructiunii switch . Daca valoarea expresie nu apare in lista constantelor de selectie, se executa instructiunile asociate etichetei default , daca exista. Programarea ciclurilor poate fi realizata folosind instructiunile de ciclare: ciclul cu test initial (instructiunea while ), ciclul cu test final (instructiunea do...while ) si ciclul cu test initial si contor (instructiunea for ). Forma instructiunii while este: while (<expresie>) <instruc#iune>. ]n particular, <instruc#iune> poate fi chiar instruc#iunea vid{. Sintaxa instruc#iunii do..while este: do <instruc#iune> while (<expresie>); Instruc#iunea dintre do ~i while se execut{ cel pu#in o dat{ ~i se repet{ c|t timp <expresie> este compatibil{ cu valoarea log adev{rat. Instruc#iunea for, ofer{ cea mai compact{ metod{ pentru scrierea ciclurilor cu test ini#ial ~i are o defini# care }i extinde domeniul de aplicare fa#{ de alte limbaje de programare. Forma instruc#iunii for este: for ( <expresie_1> ; <expresie_3> ; <expresie_3> ) <instruc#iune> ~i are efectul similar cu al secven#ei:

22 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

<expresie_1>; while (<expresie_2>) { <instruc#iune> <expresie_3>;} Cele trei expresii dintre paranteze pot fi toate vide, caz }n care avem de-a face cu un ciclu infinit. ]ntreruperea necondi#ionat{ a unei secven#e de instruc#iuni ~i continuarea dintr-un alt punct al programului este posibil{ prin utilizarea instruc#iunilor de salt: goto (salt la o instruc#iune etichetat{), break (}n contextul instruc#iunii switch c|t ~i }n instruc#iunile de ciclare pentru a determina ie~irea for#at{ din ciclu, indiferent de valoarea condi#iei de ciclare) ~i continue (}n cadrul blocului instruc#iunilor de ciclare pentru a }ntrerupe execu#ia itera#iei curente). ]n instruc#iunilor while ~i do..while, instruc#iunea continue determin{ activarea testului condi#iei de ciclare, iar pentru instruc#iunea for se va continua cu evaluarea, }n aceast{ ordine, expresiilor <expresie_3>, <expresie_2>. Exemplul 3.4.3. (Evaluarea unei func#ii polinomiale date explicit): # include <stdio.h> void main (void) { float t, val; do { printf(\ n Introduce#i valoare lui t:); scanf(%f, &t); val = (((5*t+4)*t-2)*t+3)*t-10; printf(\ n Rezultatul evalu{rii este: %f, val); printf(Continua#i [ d/n] ?); } while (getche() == d);} Exemplul 3.4.4. (O implementare a algoritmului lui Nicomachus - utilizarea sc{derilor repetate - pentru determinarea celui mare divizor comun a dou{ numere naturale nenule) # include <stdio.h> int cmmdc(int, int); void main(void) { int u,v; while (scanf(%d %d, &u, &v) != EOF) if ( (u>0) && (v>0) ) printf(%d %d %d \ n, u,v, cmmdc(u,v)); }

23 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

int cmmdc(int m, int n){ int temp; do { if (m<n) { temp = m; m = n; n = temp; } m = m - n; } while (m != n); return(m); } Exemplul 3.4.5. (Implementarea algoritmului lui Roy Warshall pentru determinarea matricei existen#ei drumurilor pentru u graf G cu maxim 20 de v|rfuri.) Se noteaz{ cu x matricea de adiacen#{ a grafului (x[ i,j] = 1 dac{ v{rfurile i ~i j sunt conectate direct, x[ i,j] =0, }n caz contrar). Se va ob#ine matricea y cu elemente: y[ i,j] =1 dac{ v{rfurile i ~i j sunt conectate printr-un drum, y[ i,j] =0, }n caz contrar. # include <stdio.h> # define maxlin 20 # define maxcol 20 # define or | | # define nl \ n unsigned char x[ maxlin] [ maxcol] , y[ maxlin] [ maxcol] ; void main(void){ int i,j,n,k; do { printf( nl Introduce#i dimensiunea grafului (cel mult %d):, maxlin); scanf(%d, &n); } while ((n<1) or (n>maxlin)); printf( nl Introduce#i matricea de adiacen#{: nl); for (i=0; i<=n-1; i++) for (j=0; j<n; j=j+1) scanf(%d, &x[ i] [ j] ); printf( nl S-a introdus matricea: nl); for (i=0; i<n; i++) { for (j=0; j<n; j++) { printf(%d, x[ i] [ j] ); y[ i] [ j] =x[ i] [ j] ; } printf(nl);

24 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

} for (j=0; j<n;j++) for (i=0; i<n; i++) if (y[ i] [ j] != 0) for (k=0; k<n; k++) y[ i] [ k] = y[ i] [ k] or y[ j] [ k] ; printf(nl Matricea existen#ei drumurilor: nl); for(i=0; i<n; i++) { for(j=0; j<n; j++) printf(%d, y[ i] [ j] ); printf(nl); } } Exemplul 3.4.6. (Evaluarea unei func#iei). Se cere evaluarea func#iei urm{toare pentru un n ~i x da#i:

fn(x) := if (n = 1) then if (n = 2) then log3(1+| 3x+5| ) else if (n = 3) then # include <stdio.h> # include <math.h> void main(void) { int n; float x,v,a,b,c; do { printf(N in [ 1, 2, 3] :); scanf(%d, &n); } while ((n-1)*(n-2)*(n-3) != 0); printf(x = ); scanf(%f, &x); switch (n){ case 1: a=x*x; v=atan((2+a)/(1+a))+atan(3+2*a); break; case 2: v=log(1+fabs(3*x+5))/log(3); break; case 3: a=exp((x-3)*log(5)); b=x*x*x*x*x; c = a+b+1; v=exp(log(fabs(c))/5.0); if (c<0) v=-v; .

else

25 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

break; } printf(\ n N= %d x = %4.2f --> v =%7.3f, n, x, v);}

3.5. Recursivitate }n limbajele de programare


3.5.1. No#iunea de recursivitate ]n general, prin recursivitate se }n#elege proprietatea intrinsec{ a unei entit{#i (fenomen, proces etc.) de a fi definit{ ~i/sau analizat{ folosind entit{#i de aceea~i natur{. Un algoritm }n a c{rui descriere este nece referirea la el }nsu~i se nume~te algoritm recursiv. Aceast{ clas{ de algoritmi este }nt|lnit{ frecvent. Ea face posibil{ prezentarea unor descrieri simple ~i elegante ale opera#iilor algoritmului. Iat{ dou{ moduri diferite utilizate pentru a defini n!, unde n este un num{r }ntreg pozitiv. Prima defini#ie este nerecursiv{, a doua este recursiv{. n! este produsul tuturor numerelor }ntregi cuprinse }ntre 1 ~i n inclusiv. Dac{ n=1 atunci n!=1, altfel n! este produsul dintre n ~i (n-1)!. Observ|nd c{ }n a doua defini#ie se folose~te aceea~i descriere dar pentru o valoare mai mic{, adic{ n-1, ~i c{ procedeul poate continua p|n{ la }nt|lnirea valorii 1, putem demonstra prin induc#ie c{ a doua defini#ie este corect{. Limbajele de programare moderne ofer{ posibilitatea descrierii ~i utiliz{rii subprogramelor recursive Printre aceste limbaje g{sim: Pascal, PL/C, Lisp, APL, C, Java precum ~i altele. Ceea ce caracter subprogram recursiv este faptul c{ se autoapeleaz{, pentru un domeniu restr|ns de valori. Exemplific{m implementarea defini#iilor de mai sus }n subprograme Pascal. Func#ia fact_nr descrie algoritmul de calcul nerecursiv, iar func#ia fact_r codific{ defini#ia recursiv{. Exemplul 3.5.1. (Calculul factorialului) Program Demonstrativ_Fact; var eroare: boolean; function fact_nr(n:integer):integer; var p, i:integer; begin if n<=0 then begin eroare:=true; fact_nr:=-1 end else begin eroare:=false; p:=1; for i:=1 to n do p:=p*i; fact_nr:=p end

26 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

end; function fact_r(n:integer):integer; begin if n<=0 then begin eroare:=true; fact_r:= -1 end else if n=1 then begin eroare:=false; fact_r:=1 end else fact_r:=n*fact_r(n-1) end; var x:integer; begin eroare:=false; for x:= -1 to 7 do if not eroare then writeln(fact_nr(x), , fact_r(x)) end. Trebuie s{ observ{m necesitatea testului n<=0, omiterea acestuia, pentru numere }ntregi negative, conduce la continuarea autoapelului p|n{ la blocarea sistemului. ]ntotdeauna, structura global{ a unui subprogram recursiv este: subprogram Id(lista de variabile); SEQ ... If (conditie_de_oprire_a_autoapelului) then SEQ .... END else SEQ ...; apeleaz{ ID(lista de variabile - cu valori modificate); ... END; ... END 3.5.2. Tipuri de recursivitate ]ntr-un program Pascal de forma p[ ...; q; ...; r; ...] :
program p;

27 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

...
procedure q;

...
begin ... end;

...
procedure r;

...
begin ... end;

...
begin ... end. subprogramul r poate sa apeleze subprogramul q, dar q nu poate apela r deoarece r nu este }nc{ decla Pentru aceasta, limbajul Pascal, propune utilizarea directivei forward. ]n declara#ia propriu-zis{ a lui r care urmeaz{ subprogramului q, se folose~te un antet redus }n care se specific{ doar numele lui r, parametrii ~i tipuri (similar prototipurilor de func#ii C). Programul P va avea forma p[ ..; r forward; ..; q; ..; r; ..] : program p; ... procedure r (var x:integer); forward; ... procedure q; ...; var t:integer; ... begin ...; r(t); ... end; ... procedure r; ... begin ...; x:=x+1; ... end; ... begin ... end.

28 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Referirea lui r }n q este posibil{ ~i f{r{ folosirea directivei forward, prin simpla inversare a ordinei declara#iilor lui q ~i r }n p. Se ob#ine forma: p[ ...; r; ...; q; ...] . Dac{ at|t q apeleaz{ r c|t ~i r apeleaz{ q, inversarea nu rezolv{ problema ~i trebuie folosit{ directiva forward. Referirea mutual{ a dou{ sau mai multe subprograme }n maniera descris{ mai sus, cu folosirea necesar{ a directivei forward, se nume~te recursie indirect{. Atunci c|nd un subprogram se apeleaz{ nemijlocit pe el }nsu~i spunem c{ recursia este direct{. ]n limbajul C, introducerea declara#iilor f{r{ defini#ii ale func#iilor (prototipuri), nu numai c{ p recursivit{#ii indirecte este rezolvat{, dar compilatorul are ~i posibilitatea de a efectua verificarea validit{#ii apelurilor (num{rul ~i tipul parametrilor) precum ~i eventualele conversii de tip pentru parametri ~i rezultat. Vom prezenta un exemplu de program C }n care apare fenomenul de recursivitate Mai multe exemple (subprograme recursive codificate }n C sau Pascal) vor fi introduse }n capitole urm{toare. Exemplul 3.5.2. (Metoda inser#iei binare pentru ordonarea cresc{toare a unui tablou unidimensional cu numere }n virgu mobil{). ]ncep|nd cu al 2-lea element, se determin{ - prin c{utare binar{ - pozi#ia pe care trebuie s-o ocupe un element, cunosc|nd numai elementele deja prelucrate, apoi deplaseaz{ anumite elemente pentru a-l depune la locul potrivit. # include <stdio.h> # include <conio.h> # define max 100 typedef float vector[ max] ; int n,i,k; vector a; int poz(int st, int dr, int j) { /* determin{ pozi#ia elementului a[ j] }n ~irul a[ st] ,..., a[ dr] */ int mijloc; if (st == dr) if (a[ j] < a[ st] ) return (st); else return (j); else if (dr-st == 1) if (a[ j] <a[ dr] ) if (a[ j] >= a[ st] ) return (dr); else return (st); else return(j); else { mijloc = (st+dr)/2; if (a[ mijloc] <= a[ j] ) return (poz(mijloc, dr, j)); else return (poz(st, mijloc, j)); }}

29 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

void deplasare(int j, int pozi#ie) { int i, temp; if (j > pozitie) { temp = a[ j] ; for (i=j; i>=pozi#ie+1; i--) a[ i] = a[ i-1] ; a[ pozi#ie] = temp; } } void main(void) { clrscr(); printf(n>); scanf(%d, &n); for (i=0; i<=n-1; i++) { printf(%d:, i); scanf(%f, &a[ i] );} for (i=1; i<=n-1; i++) { k=poz(0,i-1,i); deplasare(i,k); } printf(Sirul ordonat:\ n); for (i=0; i<=n-1; i++) printf(%5.2f, a[ i] ); gotoxy(1,25); printf(Apas{ orice tast{!); getch(); }

3.6. Tipul referin#{ }n limbajele de programare


Tipul referin#{ reprezint{ adrese ale unor zone de memorie. Variabilele ce stocheaz{ adrese ale unor zone de memorie se mai numesc ~i pointeri. Pointerii sunt necesari pentru a permite lucrul cu variabile dinamice, care pot fi create ~i distruse }n timpul execut{rii unui program. Datorit{ diferen#elor sintactice }n definirea ~i utilizarea pointerilor, care exist{ }ntre limbajele de programare Pascal ~i C, }n cele ce urmeaz{ se va discuta mai }nt|i tipul de date referin#{ }n Pascal, apoi declararea ~i opera#iile cu poin limbajul C. 3.6.1. Tipul referin#{ }n Pascal ]n limbajul Pascal este permis{ utilizarea a dou{ clase de variabile: statice ~i dinamice. Variabilele statice sunt alocate }n timpul compil{rii, iar locul ocupat de ele }n memorie nu poate fi modificat }n execu#ie; e exist{ pe durata }ntregii execu#ii a blocului (program, procedur{ sau func#ie). Variabilele statice su declarate }n sec#iunea VAR. Variabilele dinamice nu apar }ntr-o declara#ie explicit{ }n sec#iunea VAR, ~i accesul la acestea nu se poate face direct. Crearea ~i distrugerea variabilelor dinamice se realizeaz{ cu procedurile New ~i Getmem respectiv Dispose ~i Freemem. Aceste subprograme aloc{, respectiv elibereaz{ spa#iul de memorie pentru variabile dinamice. Adresa zonei de memorie alocat{ unei variabile dinamice va fi stocat{ }ntr-o variabil{ de tip special, numit referin#{. Lungimea zonei alocate depinde de tipul variabilei dinamice. De exemplu, pentru adrese ale locatiilor de tip intreg se aloca 2 bytes, pentru locatii care s{ stocheze numere }n virg

30 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

mobil{ dubl{ precizie (double) se aloc{ 8 bytes. Spa#iul de memorie supus aloc{rii/eliber{rii dinamice nume~te HEAP. Definirea unui tip referin#{ se poate face }n sec#iunea type, astfel: type PT = ^ T; unde semnul ^ semnific{ o adres{. Mul#imea valorilor de tip PT const{ dintr-un num{r nelimitat de adrese; fiecare adres{ identific{ o variabil{ de tip T. La aceast{ mul#ime de valori se mai adaug{ valoarea NIL care identific{ nici o variabil{. Limbajul Pascal permite ca }n momentul }nt|lnirii tipului variabilei dinamice acesta s{ nu fi fost definit; acest tip va fi, totu~i, declarat }n aceea~i declara#ie de tip (referire }nainte sau autoreferire). Exemplu: (Referire }nainte) type referinta = ^articol; articol = record u, v: integer end; var p,q:referinta; (* referire la variabile de tip articol *) r: ^real; (* referire variabile dinamice *) s: ^boolean; (* de tip ra#ional, respectiv logic *) Exemplu: (Tip de date recursiv) O list{ este fie vid{, fie este identificata prin primul element ~i prin sublista ce urmeaza acestuia. Defini#ia are caracter recursiv ~i este specificat{ prin: type reper = ^lista; lista = record element: string[ 10] ; sublista: reper end; var p, q, r: reper; begin new(p); new(q); q^.sublista:=NIL; p^.sublista:=q; p^.element:=PRIMUL; q^.element:=ULTIMUL; end. Dup{ crearea unei variabile dinamice a c{rei adres{ este depus{ }ntr-o variabil{ de tip referin#{, ea po accesat{ prin a~a numita dereferen#iere: numele variabilei de tip referin#{ este urmat de semnul ^. Acest semn poate fi urmat ~i de un alt calificator (de c|mp, ca }n exemplul 2; de tablou etc.). ]ntotde

31 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

dereferen#ierea unei variabile cu con#inut NIL declan~eaz{ o eroare de execu#ie. Exist{ un tip predefinit Pointer, care este un tip referin#{ f{r{ tip de baz{. Astfel, o variabil{ de acest tip poate s{ repereze o variabil{ de orice tip, motiv pentru care acestui tip i se mai spune ~i tip generic sau liber. Declararea unei variabile de acest tip se face prin Var <identificator>:pointer; Variabilele de acest tip sunt compatibile cu valoarea NIL, dar nu pot fi dereferentiate: scrierea simbolului ^ dup{ o astfel de variabil{ constituie eroare. Variabilele de tip pointer sunt utile pentru memorarea unor variabile de tip legat. Valorile de tip referin#{ pot fi create cu operatorul @ ~i cu func#ia standard Ptr. Aceste valori sunt tratate ca referin#e pentru valori dinamice.

3.6.2. Tipul referin#{ }n C Facilit{#ile oferite pentru lucrul cu variabile de tip referin#{ (numite }n continuare pointeri) reprezint{ unul dintre atuurile foarte importante ale limbajului C. Din punct de vedere al zonei adresate se disting 2 categorii de pointeri cu roluri ~i propriet{#i distincte: pointeri de date (con#in adrese de variabile s constante din memorie) ~i pointeri de func#ii (con#in adresa codului executabil al unei func#ii). O a categorie permite utilizarea pointerilor generici, numi#i ~i pointeri void. 3.6.2.1. Pointeri de date. Sintaxa unui pointer de date este: TIP *vpt; Simbolul * precizeaz{ c{ vpt este numele unei variabile pointer, iar TIP este tipul obiectelor a c{ror adres{ o va con#ine (adic{ tipul de baz{). Pentru toate opera#iile }n care intervin pointeri, compilatorul interpreteaz{ zona de memorie adresat{ pointer ca obiect de tipul indicat la declarare, cu toate atributele tipului: num{rul de bytes necesa semnifica#ia con#inutului zonei. Declara#ia void *vpt; este permis{ ~i permite declararea unui pointer generic. Tipul de baz{, }n declararea de mai sus, nu este specificat. ]n cazul unui pointer void, dimensiunea zonei de memorie adresate ~i interpretare informa#iei nu sunt definite, iar propriet{#ile sunt diferite de cele ale altor pointeri de date. O variabil{ pointer este tot un obiect, deci are sens declararea unui pointer (ac#iune de indirectare) d pointer. ]ntotdeauna, }nainte de utilizare, o variabil{ pointer trebuie ini#ializat{ (cu valoarea NULL = adresa unei variabile existente sau adresa returnat{ de un apel de alocare a memoriei). Pointerii de orice tip pot fi compara#i cu NULL pentru a verifica egalitatea sau inegalitatea. Folosirea variabilelor pointer este posibil{ prin intermediul operatorilor unari: operatorul & (adresa }ntoarce adresa unui variabile oarecare ~i, operatorul * (indirectare) pentru accesul la variabila adresat{ de un pointer. Fie declara#iile: TIP var; TIP *pvar;

32 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

atunci: &var (se cite~te adresa variabilei var) este de tip TIP *, deci atribuirea pvar = &var este corect{. De asemenea, *pvar (se cite~te la adresa pvar) reprezint{ obiectul de tip TIP adresat de pvar. Construc#ia *pvar poate utilizat{ oriunde folosirea unei variabile este permis{ (inclusiv modificare). Atribuirea este permis{ }ntre pointeri dac{ sunt de tipuri compatibile. ]n cazul pointerilor generici, e necesar{ utilizarea operatorului cast de forma (TIP *). Exemplu: (Un pointer poate indica obiecte diferite pe parcursul execu#iei programului) void main(void){ int a=1, b=2, *c=&a; float d=3.14; void *e; printf(%d,*c); *c=10; printf(%d %d,*c,a); (*(c=&b))++; printf(%d %d %d,a,b,*c); e=&d; printf(%f%f, d, *(float *)e); e=&a; printf(%d %d, a, *(int *)e); } Al{turi de atribuire, }n limbajul C, sunt permise ~i opera#ii de comparare, adunare ~i sc{dere ( incrementare ~i decrementare): compararea a doi pointeri folosind operatori rela#ionali. De exemplu, secven# a {...; int *a, *b; ...; if (a<b) printf(%d %d, a, b); ... }este corect{. compararea unui pointer cu valoarea NULL. Secven#a de alocare a 5 loca#ii de tip float:
{...; if ((ptr=malloc(sizeof(float)*5))=NULL) printf(Mem. insuficient{); else ... }

este echivalent{ cu:


{...; if (!(ptr=malloc(sizeof(float)*5))) printf( Mem. insuficient{); else ...}.

adunarea dintre un pointer de obiect ~i un }ntreg, conform unei reguli specifice: Pentru variabila pointer declarat{ prin: TIP *varpt; opera#iile: varpt + n ~i varpt - n corespund adun{rii (resp. sc{derii la (resp. din) adresa varpt a valorii n*sizeof(TIP). Pentru aceste opera#ii, operanzii nu pot fi de tip void ~i nici pointeri de func#ii. sc{derea a doi pointeri de acela~i tip (excluz|nd cazurile void ~i pointeri de func#ii), av|nd ca rezultat num{rul de obiecte de tipul respectiv. De exemplu, pentru declara#iile: ...; int n; float *a, *b; ..., atribuirea n=b-a; este echivalent{ cu atribuirea lui n a valorii (adr_b - adr_a)/sizeof(float). Limbajul C permite utilizarea variabilelor tablou ca pointeri. Mai precis un nume de tablou f{r{ index este

33 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

un pointer constant de tipul elementelor tabloului ~i are ca valoare adresa primului element din ta Opera#iile aritmetice de mai sus sunt definite consider|nd c{ obiectele adresate constituie un tablou. Referitor la leg{tura dintre tablouri ~i pointeri, se impun urm{toarele observa#ii: se pot da exprim{ri echivalente privind adresa unui element oarecare al unui tablou: dac{ tab este un tablou cu elemente de tip int, atunci construc#ia &tab[ i] este echivalent{ cu tab+i. Este evident c{ expresia &tab[ 0] == tab este adev{rat{. se pot realiza exprim{ri echivalente privind elementele tabloului: dac{ tab este tabloul de mai sus, atunci tab[ i] este echivalent cu *(tab+i). La parcurgerea unui tablou, utilizarea pointerilor poate fi mai rapid{ dec|t indexarea. Urm{toarele secven#e determin{ lungimea unui ~ir de caractere. Secven#a: ... int k; char sir[ 50] ; ... for (k=0; sir[ k] ; k++); printf(%d, k); ... realizeaz{ aceea~i opera#ie ca secven#a: ... int k; char sir[ 50] , *ad=sir; ... for(k=0; *ad; ad++) k++; printf(%d,k); .... Observa#i diferen#ele de implementare. Referitor la constantele ~ir de caractere, compilatorul C aloc{ zona de memorie necesar{ ~i }nscrie codurile ASCII ale caracterelor ~i codul final \0. Urm{toarele exprim{ri sunt corecte: char *sir; ...; sir=Acesta este un sir; char *sir=Al doilea sir; char sir[ ] =Al treilea sir; ]n privin#a tablourilor multidimensionale trebuie remarcat c{ numele tabloului este un pointer de tablouri, iar c|nd numele unui tablou apare f{r{ indexare ca operand }ntr-o expresie sizeof, el refer{ }ntregul tablou. incrementarea pointerilor este echivalent{ cu accesarea elementelor unui tablou: dac{ consider{m declara#ia int *p; atunci opera#ia *(p+i) este echivalent{ cu srierea p[ i] . Pentru alocarea memoriei din spa#iul HEAP, biblioteca de func#ii a compilatorului C ofer{ un se subprograme. Prototipurile func#iilor de alocare/eliberare se afl{ }n fi~ierele alloc.h ~i stdlib.h. Cele mai folosite func#ii sunt: malloc() ~i free(). Func#ia care realizeaz{ alocarea unei zone de memorie ar prototipul: void *malloc(unsigned nr_bytes); unde nr_bytes reprezint{ dimensiunea }n octe#i a zonei solicitate. Dac{ alocarea a reu~it, func#ia }ntoarce un pointer care con#ine adresa primului octet al zonei alocate. ]n caz contrar (spa#iul disponibil e

34 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

insuficient), rezultatul returnat este NULL (=0). Cum tipul rezultatului este void, trebuie utilizat operatorul cast pentru conversie de tip la atribuire, iar pentru precizarea dimensiunii zonei solicitate se poate utili operatorul sizeof. Eliberarea spa#iului alocat trebuie solicitat{ de c{tre programator. Acesta va utiliza func#ia cu prototipul: void free(void *ptr); unde parametrul ptr indic{ adresa de }nceput a zonei care trebuie eliberat{. Este obligatoriu ca ptr s{ fi fost rezultatul unui apel al func#iei malloc. Alte func#ii de alocare/eliberare disponibile }n implementarea Borland/Turbo C sunt: calloc(), corel realloc(); farmalloc(); farcalloc(); farfree() etc. 3.6.2.2. Transferul parametrilor }n C. ]n limbajul C transferul implicit al parametrilor, }ntre subprograme, este prin valoare. Dac{ dorim ca func#ie s{ modifice o variabil{ parametru formal, trebuie s{ transmitem func#iei adresa variabilei, ia interiorul func#iei s{ folosim operatorul *. Func#ia schimba () din exemplul urm{tor inverseaz{ valorile dou{ variabile av|nd tipul TIP. Pentru a schimba efectiv, func#ia are nevoie de adresele celor dou{ variabile: schimba(TIP *u, TIP *v) { TIP t; t=*u; *u=*v; *v=t; } Apelul functiei pentru a schimba con#inutul a dou{ loca#ii este: ... TIP a,b; ... schimba(&a, &b); ... Transferul parametrilor prin referin#{ este permis }n limbajele Pascal ~i C++. Astfel }n limbaju subprogramul schimba, se scrie: procedure schimba(var x, y:TIP); var t:TIP; begin t:=x; x:=y; y:=t end;

35 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Prin folosirea parametrilor formali referin#{, limbajul C++ permite realizarea transferului prin referin#{ de o manier{ apropiat{ limbajului Pascal. Secven#a de program C++ demonstrativ{ este: void schimba(TIP &, TIP &); void main (void) { TIP u,v; ... schimba(u,v); ... } void schimba(TIP &x, TIP &y){ TIP t; t=x; x=y; y=t; } Relativ la transmiterea tablourilor }ntre unit{#i de program C, trebuie spus c{ este implementat transfer prin referin#{ deoarece tablourile con#in, }n general, o cantitate mare de date. Pe de alt{ parte chiar numele tabloului este echivalent cu adresa sa. Pentru tablourile multidimensionale, compilatorul treb cunoasc{ modul de organizare a tabloului pentru ca elementele s{ poat{ fi referite indexat }ntr-o func# Pentru un tablou multidimensional, este esen#ial ca func#ia s{ cunoasc{ toate dimensiunile tabloului ma pu#in prima. Fie scrmat func#ia care tip{re~te elementele unei matrice de numere }n virgul{ mobil{. Urm{torul cod C reprezin solu#ia corect{ a problemei. void scrmat(float (*a)[ ] , int m, int n){ int i,j; for (i=0; i<m; i++) { for (j=0; j<n; j++) printf(%f, ((float * ) a)[ i*n+j] ); printf(\n); } } La apel (scrmat(p,5,7);) se transmite adresa matricei (p) interpretat{ de tip float (*)[ ] , adic{ pointer c{tre un tablou. Pentru a ob#ine adresa elementului a[ i] [ j] se utilizeaz{ expresia a+(i*n+j)*sizeof(float). 3.6.2.3. Pointeri de func#ii Variabilele pointer care con#in adresa de start a unei func#ii permit: transferul func#iei asociate parametru, apelul func#iei prin intermediul pointer-ului.

36 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

]ntotdeauna, o declara#ie de forma: tip_r (* pfct) (<l_param>); introduce pfct ca pointer cu tipul de baz{ func#ie cu lista de parametrii l_param ~i cu rezultat tip_r. Trebuie observat rolul parantezelor pentru a distinge }ntre un pointer de func#ie ~i o func#ie care }ntoarce un pointer. Declara#ia tip_r * fct(...); este prototipul unei func#ii care }ntoarce un pointer, }n timp ce prin declara#ia tip_r (* fct) (...); se introduce fct ca pointer c{tre o func#ie. Urm{torul tabel indic{ diverse situa#ii }n care apar pointeri, tablouri, func#ii.

Expresia (* x) (* x)[ ] (* (* x[ ] )) (* (* x)[ ] )() int (*p)[ ] ; int (**p)[ ] ; int *(*p)[ ] ; int * (*f)();

Semnifica#ia expresiei x este un pointer x este un pointer la un tablou x este un pointer la un tablou de pointeri. x este un pointer la un tablou de pointeri la func#ii care }ntorc pointeri p este un pointer la un tablou de }ntregi p este un pointer la un pointer la un masiv de }ntregi p este un pointer la un masiv de pointeri la }ntregi f este un pointer la o func#ie care }ntoarce un pointer la un }ntreg.

Exemplu: (Utilizarea func#iei standard qsort ce implementeaz{ algoritmul Quick Sort pentru sortare intern{). Func#ia qsort, are prototipul (descris }n fi~ierul stdlib.h):

37 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

void qsort (void *tab, int n, int m, int (* comp) (const void *, const void *)); unde tab este tabloul de sortat cu n elemente, fiecare element av|nd dimensiunea m. Func#ia de comparare trebuie scris{ de utilizator, comp fiind un pointer la aceast{ func#ie. Dup{ compararea a dou{ elemente, func#ia va }ntoarce: 0 dac{ elementele sunt egale, o valoare negativa dac{ primul element este mai mic dec|t al doilea ~i o valoare pozitiv{ altfel. Sensul rela#iilor mai mic, mai mare, egal este abstract ~i trebuie specificat de programator Fie func#iile: int comp_int (const void *x, const void *y) { return * ((int *)x) -*((int *)y); } int comp_str (const void *x, const void *y) { return strcmp(x,y); } Urm{toarele secven#e de apel realizeaz{: ordonarea cresc{toare a unui tablou cu 500 de numere }ntregi: int vector[ 500] ; qsort(vector, 100, sizeof(int), cmp_int); ordonarea cresc{toare a unui tablou de 500 de ~iruri de caractere, fiecare put|nd avea maxim 65 de octe#i: char x[ 500] [ 65] ; qsort(x,100,65,cmp_str);

3.7. Fi~iere
Organizarea datelor, necesar{ pentru prelucrarea acestora, presupune realizarea unor opera#ii identificarea, clasificarea ~i descrierea atributelor caracteristice; gruparea datelor }n colec#ii; stoc colec#iilor pe suporturi de date (}n general de tip magnetic); specificarea ~i implementarea procedu prelucrare a datelor precum ~i alte proceduri (manuale sau automate) de }ntre#inere a suporturilo informa#ii. Structurile de date necesare prelucr{rii colec#iilor mari de date sunt at|t interne c|t ~i externe. Structura de date de tip extern o reprezint{ fi~ierul. Un fi~ier este o mul#ime de date omogene ca interpretare ~ prelucrare. Din punct de vedere al sistemului de operare un fi~ier are anumite caracteristici (nume, atribute, informa#ii pentru asigurarea accesului ~i a securit{#ii datelor). Intern, un fi~ier este o colec#ie de articole (date de tip record }n Pascal, struct - }n C). Lungimea unui articol este dat{ de suma lungimii c|mpurilor componente. Dac{ toate articolele unui fi~ier au aceea~i lungime spunem c{ fi~ierul are articole de lungime fix{, altfel fi~ierul este cu articole lungime variabil{. Implementarea fizic{ difer{ de la un limbaj de programare la altul. De asemenea difer{ ~i subprogramele care permit realizarea opera#iilor asupra fi~ierelor. Limbajele Pascal ~i C permit definirea ~i prelucrarea at|t a fi~ierelor cu articole de lungime fix{ c|t ~i a celor ce con#in articole de lungime variabil{. Pe suportul extern, datele stocate sunt secven#e de octe#i. Numai }n momentul prelucr{rii, aceste succesiuni
38 of 48 12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

interpreteaz{ adecvat, fiind posibil{ prelucrarea octet cu octet sau prelucrarea blocurilor de octe#i. Pentru organizarea extern{, pe suport de date, sunt posibile mai multe metode: organizarea secven#i (singura permis{ de limbajele Pascal ~i C), organizarea relativ{ ~i cea indexat{ (permise de limba specializate, de exemplu COBOL). Pozi#ia din care se cite~te, respectiv }n care se scrie, este dat{ de un indicator de articol (notat, }n continuare, prin IA). Accesul pentru scrierea datelor sau pentru citirea datelor stocate depinde de modul de organizare ale Accesul secven#ial este posibil pentru orice mod de organizare, el presupune }nregistrarea articolelor ordinea }n care devin disponibile ~i citirea articolelor }n ordinea }n care au fost }nscrise pe suport. Pentru citirea }n acces secven#ial, este foarte important controlul identific{rii sf|r~itului fi~ierului. Dup citirea ultimului articol (a ultimei linii sau a ultimului octet) indicatorul este pozi#ionat pe marcatorul d sf|r~it de fi~ier (EOF - End Of File). Sesizarea sf|r~itului de fi~ier se face diferit }n func#ie de limbajul de programare: la citirea articolului (FORTRAN, COBOL); adic{ dup{ citirea ultimului articol, mai este necesar{ o citire pentru a vedea dac{ s-a ajuns la sf|r~it. independent de opera#ia de citire (Pascal, C, etc.), adic{ dac{ indicatorul IA este dup{ ultimul articol, automat se consider{ sf|r~it de fi~ier; urm{toarea citire va produce eroare de intrare/ie~ire. ]n consecin#{, algoritmii de prelucrare depind de modul de sesizare a marcajului EOF. De regul{, }ncercarea de citire din fi~ier dup{ ce s-a atins marcajul EOF este urmat{ de apari#ia unei erori. Accesul direct este permis numai pentru fi~ierele cu o anumit{ organizare, entitatea de citire este articolu sau blocul ~i sunt memorate pe discuri magnetice, CD sau VideoDisk (suporturi adresabile). Valoarea IA, nu se calculeaz{ }n func#ie de valoarea sa anterioar{ (ca }n cazul accesului secven#ial) ci direct cu ajutoru unei func#ii f cunoscute de sistem IA(k)=f(k). Sunt posibile dou{ moduri de acces direct: acces dup{ chei acces dup{ num{rul articolului. ]n cazul accesului dup{ cheie (implementat de exemplu }n COBOL) presupune c{ fiecare articol are un c|mp, numit cheie, care este folosit la }nregistrare/citire pentr identificarea articolului. Pentru accesul relativ, implementat }n limbajele Pascal ~i C, articolul este localizat prin num{rul s{u relativ. Articolul k are num{rul relativ k-1. Asupra fi~ierelor se pot executa opera#ii de gestiune divizate pe dou{ niveluri: opera#ii globale (}ns fi~ierului }ntr-un folder (catalog), deschiderea pentru consultare/actualizare a unui fi~ier, }nchiderea u fi~ier, redenumirea, ~tergerea, copierea fi~ierelor, etc.) ~i opera#ii la nivel de articol (scriere sau citire de articole). ]n programele Pascal, C, Java, ~. a. , opera#iile de intrare-ie~ire sunt realizate prin intermediul subprograme (proceduri ~i func#ii }n Pascal, func#ii }n C, metode }n Java). Pentru unele opera#ii es necesar{ utilizarea mai multor subprograme (de exemplu, }n Pascal, pentru opera#ia de deschidere s necesare fie apelurile Assign+Reset, fie apelurile Assign+Rewrite.) 3.7.1. Fi~iere }n C Bibliotecile standard C ofer{ o gam{ larg{ de func#ii care implementeaz{ opera#iile de intrare-ie~ire orientate pe fi~iere. Dispozitivele periferice, ale sistemului de calcul, sunt v{zute tot ca fi~iere, da identificatori predefini#i. Prototipurile func#iilor de bibliotec{ privind lucrul cu fi~iere sunt cuprinse fi~ierul antet stdio.h. Se pot distinge 3 categorii: sistemul de intrare-ie~ire standard ANSI C; sist intrare-ie~ire compatibil UNIX ~i, func#ii specifice implement{rii mediului de dezvoltare, neprev{z standardul limbajului. ]n cele ce urmeaz{ ne vom ocupa de prima categorie. Celelalte categorii se pot studia folosind manualul on-line sau manualul firmei produc{toare a mediului de dezvoltare.

39 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Conceptul de baz{ relativ la opera#iile de intrare-ie~ire standard este cel de pointer la fi~ier. Mai precis, }n fi~ierul stdio.h este definit un tip de structur{, numite FILE. Din punct de vedere intern, fi~ierele prezente }n func#iile de intrare-ie~ire sunt specificate prin variabile de tip FILE *. Declararea unui pointer la fi~ier se realizeaz{ prin scrierea FILE * <identificator>; Prin opera#ia de deschidere, se aloc{ o zon{ de memorie care va con#ine: numele extern al fi~ierului, adresa unei zone tampon util{ }n realizarea transferurilor ~i informa#ii utile privind starea opera#iilor intrare-ie~ire. Aceast{ conexiune logic{ rezultat{ }n urma deschiderii unui fi~ier se nume~te stream (sau flux de date). Eliberarea zonelor alocate se realizeaz{ }n urma opera#iei de }nchidere. Dispozitivele de intrare-ie~ire standard au asociate permanent c|te un asemenea pointer al c{rui nu predefinit: stdin (conola pentru intrare), stdout (consola pentru ie~ire), stderr (consola pentru ie~ire), stdaux (pentru primul port al interfe#ei seriale ), stdprn (pentru primul port al interfe#ei paralele); ultimi doi identificatori fiind caracteristici implement{rii BORLAND pentru calculatoare compatibile IBM Microsoft. De asemenea, }n fi~ierul stdio.h, mai sus definite constantele FILENAME_MAX (pentru a indica lungimea maxim{ a numelui unui fi~ier, din punct de vedere extern) ~i FOPEN_MAX (care precizeaz{ num{rul maxim de fi~iere ce pot fi deschise simultan). Se disting dou{ categorii de tipuri de transfer: un stream de tip text care transfer{ secven#e de caractere organizate }n linii; }n C separarea liniilor se face prin caracterul LF. Pentru adaptarea la dispozitivul fizic poate fi necesar{ }nlocuirea caracterului LF cu secventa CR-LF. un stream binar care transfer{ o secven#{ de octe#i f{r{ alte prelucr{ri. Distingerea }ntre cele dou{ categorii nu este obligatorie; programatorul este cel care trebuie s{ rez eventualele probleme de conversie a datelor. Deschiderea fi~ierelor se realizeaz{ folosind func#ia fopen care returneaz{, c|nd toate condi#iile de prelucrare sunt }ndeplinite, un pointer c{tre structura FILE. Este unica modalitate prin care se atribuie corect o valoare unui pointer la fi~ier. Prototipul func#iei fopen este: FILE * fopen(const char * nume_fis, const char * mod_acces); unde nume_fis este un ~ir de caractere (constant{ sau ob#inut prin atribuire) care specific{ numele extern al fi~ierului, iar mod_acces este un ~ir de caracter (constituit similar) care descrie modul de acces. ]n cazul unei opera#ii de desch corecte, pointerul returnat de apel este diferit de NULL, }n caz contrar rezultatul apelului este NULL. Modurile posibile de acces sunt: deschidere flux de tip text (r sau rt), respectiv flux binar (rb) pentru citire (mod reset); deschidere flux de tip text (w sau wt), respectiv flux binar (wb) pentru scriere (cu distrugerea fi~ierului anterior, dac{ acesta exista- mod rewrite);

40 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

deschidere flux de tip text (a sau at), respectiv flux binar (ab) pentru ad{ugare la sf|r~it (mod append); deschidere flux de tip text (r+ sau r+t), respectiv flux binar (r+b) pentru actualizare (citire ~i scriere folosind acela~i pointer la fi~ier); deschidere flux de tip text (w+ sau w+t), respectiv flux binar (w+b) pentru actualizare (dac{ fi~ierul exista anterior, con#inutul s{u se pierde); deschidere flux de tip text (a+ sau a+t), respectiv flux binar (a+b) pentru actualizare cu scriere la sf{r~itul fi~ierului. ]nchiderea fi~ierelor se realizeaz{ apel|nd func#ia fclose definit{ astfel: int fclose(FILE * <identificator>); care }ntoarce EOF }n caz de eroare sau 0 }n caz normal. Prin }nchidere }nceteaz{ conexiunea logic{ d pointer ~i fi~ier cu scrierea datelor din zona tampon de ie~ire (}n cazul deschiderii pentru scriere/actualizare), respectiv cu pierderea datelor din zona tampon de intrare (}n cazul desc citire/actualizare). Detectarea sf|r~itului de fi~ier se realizeaz{ prin utilizarea macrodefini#iei feof cu prototipul: int feof(FILE * <identificator>); al c{rei rezultat este nenul (s-a detectat EOF) respectiv zero (nu s-a detectat EOF). Exemplul 3.7.4 (Copierea unui fi~ier de tip text f1.txt }n fi~ierul text f2.txt). #include <stdio.h> int main(void) { FILE *sursa, *dest; if ((sursa=fopen(f1.txt, rt)) == NULL) { fprintf(stderr,Nu se poate deschide f1.txt !); return 1; } if ((dest=fopen(f2.txt,wt)) == NULL) { fprintf(stderr, Nu se poate deschide f2.txt!); return 2; } while (!feof(sursa)) fputc(fgetc(sursa),dest); fclose(sursa); fclose(dest);

41 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

return 0; } Asocierea unui nou fi~ier la un flux deschis deja se realizeaz{ folosind func#ia freopen cu prototipul: FILE * freopen(const char * <id_fis_extern>, const char * <mod_acces>, FILE * p_fis); unde p_fis este identificatorul fluxului existent care se va }nchide }n urma apelului ~i se va deschide fi~ierul cu numele <id_fis_extern> }n modul de prelucrare <mod_acces> atribuind pointerul la structura creat{ variabilei p_fis. Func#ia returneaz{ valoarea atribuit{ lui p_fis. For#area scrierii zonelor tampon asociate fi~ierelor deschise pentru scriere se realizeaz{ folosind func#ia fflush cu prototipul: int fflush(FILE * <id_fis>); care returneaz{ EOF }n caz de eroare, respectiv 0 }n cazul normal. Multe aplica#ii necesit{ lucrul cu fi~iere temporare care s{ fie ~terse automat la }nchidere sau la terminarea normal{ a programului. ]n sprijinul programatorului, pentru a }nlesni astfel de activit{#i, biblioteca C ofer{ o func#ie cu prototipul: FILE *tmpfile(void); care la apel creeaz{ un fi~ier temporar }n modul wb+. Func#ia }ntoarce un pointer la acel fi~ier. A succesive vor deschide fi~iere distincte. Alte opera#ii la nivel de fi~ier sunt: ~tergerea unui fi~ier - folosind func#ia int remove(const char * nume_fis); schimbarea numelui - folosind func#ia int rename(const char *f_nou, const char *f_vechi); Ambele func#ii }ntorc o valoare nenul{ }n caz de eroare ~i zero }n cazul normal. Opera#iile de transfer se realizeaz{ folosind urm{toarele func#ii clasificate }n func#ie de tipul fluxului (text, respectiv binar) ~i de tipul opera#iei (scriere, respectiv citire). I. Flux de tip text a) Scriere cu format. Se pot folosi urm{toarele func#ii: fprintf, printf, sprintf, vfprintf, vprintf, vsprinf. Ne vom referi numai la func#iile fprintf ~i printf. Func#ia fprintf este o func#ie cu num{r variabil de parametrii av|nd prototipul: int fprintf(FILE *fp, const char *format, ...); unde fp se refer{ la fluxul de date deschis }n vederea realiz{rii transferului, iar format respect{ specifica#iile descrise }n sec#iunea 4.4.2. Func#ia printf a fost descris{ }n sec#iunea amintit{ anterior ~i este echivalent{ cu fprintf(stdout,format, ...). b) Citire cu format.

42 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

Se pot folosi urm{toarele func#ii:fscanf, scanf, sscanf. Vom utiliza numai func#iile fscanf ~i scanf. Func#ia fscanf este o func#ie cu num{r variabil de argumente ~i are prototipul: int fscanf(FILE *fp, const char *format, ...); Parametrii apelului au acelea~i semnifica#ii ca mai sus, iar func#ia scanf este echivalent{ cu scanf(stdin, format, list{_adrese). c) Citire de caractere din fi~iere text. Se pot utiliza func#ia fgetc ~i macrodefini#ia getc. Func#ia cu prototipul int fgetc(FILE *fp); }ntoarce urm{torul caracter din fp ca un }ntreg f{r{ semn, convertit la int sau EOF dac{ s-a detectat sf|r~itul de fi~ier sau a ap{rut o eroare. Macrodefini#ia cu prototipul int getc(FILE *fp); este echivalent{ cu func#ia fgetc. Reamintim posibilitatea utiliz{rii apelului getchar() echivalent cu getc(stdin). d) Scriere de caractere }n fi~iere text. Scrierea unui caracter c }ntr-un fi~ier text cu identificatorul intern fp se realizeaz{ folosind func#ia cu prototipul: int fputc(int c, FILE *fp); care returneaz{ c }n cazul succesului, respectiv EOF }n caz de e~ec. Se poate utiliza ~i macroinstruc#iunea cu prototipul int putc(int c, FILE *fp); similar{ cu fputc, iar apelul putchar(c); este echivalent cu putc(c, stdout);. e) Citirea unui ~ir de caractere din fi~iere text se realizeaz{ folosind func#ia cu prototipul: char *fgets(char *s, int n, FILE *fp); la al c{rei apel se citesc, }n s, cel mult n-1 caractere din fi~ierul fp la care se adaug{ caracterul \0 ~i se returneaz{ s sau NULL }n caz unei erori sau la }nt|lnirea codului EOF. Reamintim utilizarea func#iei cu prototipul: char *gets(char *s); care cite~te urm{toarea linie de la consol{ ~i o depune }n s. f) Scrierea unui ~ir de caractere }ntr-un fi~ier text se realizeaz{ }n urma apelului func#iei cu prototipul: int fputs(const char *s, FILE *fp); care }ntoarce EOF }n caz de eroare, respectiv num{rul de caractere transferate }n caz de succes. Pentr scriere la consol{ se utilizeaz{ func#ia: int puts(const char *s); II. Flux binar ]n aceast{ categorie intr{ func#iile de intrare/ie~ire }n acces direct care citesc/scriu din/}n fi~iere f{r{ nici o conversie ~i f{r{ a se face vreo interpretare a datelor. No#iunea fundamental{ este cea de zon{ compact{ octe#i (}nregistrare) care se cite~te sau se scrie. Citirea/scrierea se face de la / la pozi#ia curent{ din fi~ier, care poate fi modificat{ cu func#ii speciale dup{ cum vom vedea mai jos. Dup{ executarea opera#iei, pozi#ia indicatorului (de octet }n cazul limbajului C) este actualizat{ automat, pentru a indica urm{toar }nregistrare. Citirea a nrec }nregistr{ri din fi~ierul cu identificatorul fp, fiecare av|nd lungimea L, cu stocare }n tabloul ptr se realizeaz{ folosind apelul: n = fread(ptr, L, nrec, fp);

43 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

care }ntoarce un }ntreg ce furnizeaz{ num{rul de }nregistr{ri citite. Dac{ s-a }nt|lnit EOF sau eroare atunci n va fi 0.

Pentru scrierea a nrec }nregistr{ri de lungime L fiecare, din zona cu adresa ptr }n fi~ierul cu identificatorul intern fp se folose~te apelul: m = fwrite(ptr, L, nrec, fp); unde m va reprezenta num{rul de }nregistr{ri scrise, cu m < nrec numai }n caz de eroare. Pentru a avea acces la orice zon{ a fi~ierului, biblioteca C ofer{ un set de func#ii care permit: citirea valorii indicatorului de pozi#ie. Func#ia fgetpos }nscrie valoarea indicatorului }n variabila poz ~i }ntoarce 0 }n caz de succes. Prototipul func#iei este: int fgetpos(FILE *fp, long int *poz); Func#ia ftell }ntoarce pozi#ia curent{ }n fi~ier }n caz de succes ~i -1L }n caz de e~ec. Aceasta are prototipul: long int ftell(FILE * fp); modificarea valorii indicatorului de pozi#ie. Sunt disponibile func#iile: fsetpos, fseek ~i rewind. Func#ia fsetpos are prototipul: int fsetpos(FILE *fp, const long int *poz); ~i atribuie indicatorului valoarea variabilei poz ~i }ntoarce 0 }n caz de succes. Func#ia fseek face o deplasare a indicatorului de pozi#ie cu nr_octe#i relativ la o pozi#ie de referin#{ specificat{ prin constanta }ntreag{ origine. Se pot utiliza urm{toarele valori: 0 = SEEK_SET (}nceput de fi~ier), 1=SEEK_CUR (pozi#ie curent{), 2 = SEEK_END (sf|r~it de fi~ier). Valoarea }ntoars{ de fseek este 0 pentru succes ~i nenul{ }n caz de e~ec. Prototipul func#iei fseek este: int fseek(FILE *fp, long nr_octe#i, int origine); Folosind func#ia rewind are loc pozi#ionarea la }nceputul fi~ierului. Prototipul func#iei rewind este: void rewind(FILE *fp); Pentru a ob#ine informa#ii despre un fi~ier se pot utiliza func#iile stat ~i fstat cu prototipul declarat }n fi~ierul stat.h din catalogul sys. Ne vom referi la func#ia stat. Aceasta are prototipul: int stat (char *cale, struct stat * statzona); unde: cale reprezint{ numele fi~ierului sau catalogului, iar statzona este adresa unei structuri de tip stat ale c{rei c|mpuri descriu starea entit{#ii }n discu#ie. De exemplu, c|mpul st_size furnizeaz{ dimensiunea unui fi~ier, }n octe#i. Exemplul (Aflarea dimensiunii unui fi~ier folosind func#iile de citire-modificare a indicatorului de pozi#ie). #include <stdio.h> 3.7.5.

44 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

long filesize(FILE *fp); int main(void){ FILE *f; f=fopen(F_test.txt,w+); fprintf(f, Aflarea dimensiunii unui fisier folosind functiile\n); fprintf(f, de citire-modificare a indicatorului de pozitie.\n); printf(%ld,filesize(f)); return 0; } long filesize(FILE *fp){ long cp, lung; cp = ftell(fp); fseek(fp, 0L, SEEK_END); lung=ftell(fp); fseek(fp, cp, SEEK_SET); return lung; } Exemplul 3.7.6. (Metode de sortare a datelor stocate }n fi~iere). Sortarea datelor stocate }ntr-un fi~ier se poate realiza cu aducerea integral{ a datelor }n memoria volatil{ (sortare intern{) sau cu aducerea } memoria principal{ a c|te unui articol (sortare extern{). Metodele prezentate aici nu sunt din cele performante, ele ilustreaz{ accesul la datele stocate }n fi~iere ~i principalele modalit{#i de prelucrare. O alt{ metod{ de sortare a datelor externe utilizeaz{ procedeul interclas{rii colec#iilor ordonate. Principiul a metode va fi explicat }n contextul rezolv{rii problemelor prin metoda divide et impera. a) Sortarea intern{. Metoda se poate aplica numai fi~ierelor de dimensiune redus{ (ca num{r de articole sau ca lungim articolelor). Se pot descrie mai multe solu#ii posibile: Stocarea integral{ a datelor }n memoria principal{. Se va utiliza un vector de articole, compararea fiind realizat{ prin intermediul cheii de sortare. Pentru sortare intern{ se poate utiliza oricare dintre metodele cunoscute. ]n final vectorul sortat se }nregistreaz{ }n fi~ierul redeschis pentru scriere. Con#inutul vechi va fi automat distrus. Sortare cu manipularea cheii de sortare ~i a pozi#iei articolului }n fi~ier (indexul articolului). Aceast{ metod{ presupune memorarea }ntr-un vector numai a valorii cheii de sortare, }mpreun{ cu num{rul relativ al articolului fi~ierul de sortat. Compara#iile asupra valorilor cheii de sortare vor conduce la interschimb{ri }n vectorul intern, Ob#inerea fi~ierului sortat se va face prin parcurgerea }n acces secven#ial a fi~ierului ini#ial ~i scrierea articolelor, }n ordine, }n fi~ierul rezultat. Programul care

45 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

urmeaz{ ilustreaz{ aceast{ strategie. #include <stdio.h> typedef struct {char nume[ 20] ; float media;} articol; FILE *fin, *fout; float x[ 300] ; int index[ 300] ; long filesize(FILE *fp); /* se include definitia de la exemplul 4.7.5 */ void main (void){ long i,j,n,itemp; float xtemp; articol a; fin=fopen(f1.dat,rb); fout=fopen(f2.dat,wb); n=filesize(fin)/sizeof(articol); for (i=0; i<n; i++){ fread(&a,sizeof(articol),1,fin); x[ i] =a.media;index[ i] =i; } for (i=0; i<n-1; i++) for (j=i+1; j<n; j++) if (x[ i] >x[ j] ) { itemp=index[ i] ; index[ i] =index[ j] ; index[ j] =itemp; xtemp=x[ i] ; x[ i] =x[ j] ; x[ j] =xtemp; } for (i=0; i<n; i++) { fseek(fin,index[ i] *sizeof(articol),SEEK_SET); fread(&a, sizeof(articol), 1, fin); fwrite(&a, sizeof(articol), 1, fout); } fclose(fin); fclose(fout); } b) Sortare extern{. Aceast{ abordare este caracteristic{ fi~ierelor de dimensiune mare, pentru care nu se poate aplica o metod de tip a2. Opera#iile trebuie realizate direct }n fi~ier. Ilustr{m utilizarea func#iilor fseek, fread ~i fwrite }n implementarea metodelor de sortare prin interschimbare ~i prin selec#ie. Se utilizeaz{ elementele descrise }n cadrul programului a2.
46 of 48 12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

sortare prin interschimbare: FILE *fp; int ok; long i, n; articol a[ 2] ; fp=fopen(fis.dat,r+b); n= filesize(fp)/sizeof(articol); do {ok=0; for(i=0; i<n-1; i++) { fseek(fp, i*sizeof(articol), SEEK_SET); fread(a, sizeof(articol), 2,fp); if (a[ 0] .media > a[ 1] .media) { fseek(fp, i*sizeof(articol), SEEK_SET); fwrite(a+1, sizeof(articol),1,fp); fwrite(a,sizeof(articol),1,fp); ok = 1; } }while (ok); sortare prin selec#ie: FILE *fp; long i,j, n; articol a, b; fp=fopen(fis.dat,r+b); n= filesize(fp)/sizeof(articol); for(i=0; i<n-1; i++) { fseek(fp, i*sizeof(articol),SEEK_SET); fread(&a, sizeof(articol), 1, fp); for(j=i+1; j<n; j++) { fseek(fp, j*sizeof(articol), SEEK_SET); fread(&b, sizeof(articol),1,fp); if (a.media>b.media) {

47 of 48

12/9/2007 7:21 PM

Limbaje de programare - C Autor: Prof. Univ. Dr. Grigore Albeanu

file:///C:/ACTIV/Proc/FINAL/C4.HTM

fseek(fp, i*sizeof(articol), SEEK_SET); fwrite(&b, sizeof(articol), 1, fp); fseek(fp, j*sizeof(articol), SEEK_SET); fwrite(&a, sizeof(articol), 1, fp); a=b;} }}

Vreau sa merg la inceputul capitolului Vreau sa vad Cuprinsul documentului. Versiune prescurtata a capitolulul 4 din: G. Albeanu, Algoritmi si limbaje de programare. Editura "Fundatiei Romnia de Mine", 2000

48 of 48

12/9/2007 7:21 PM

Bazele Informaticii: Bibliografie suplimentara

file:///C:/ACTIV/Proc/FINAL/BIB.HTM

Bibliografie suplimentara
1. Aho A. V., Hopcroft J. R., J. D. Ullman, The Design and Analysis of Computer Algorithms, Addison-Wesley Comp, 1975. 2. Albeanu G., Algoritmi si limbaje de programare. Editura Fundatiei "Romnia de Mine", 2000 3. Albeanu G., Programarea in Pascal si Turbo Pascal. Culegere de probleme. Editura Tehnic, 1994. 4. Albeanu G., Sisteme de operare, Editura Petrion, 1996. 5. Blnescu T., Corectitudinea algoritmilor, Editura Tehnic, Bucuresti, 1995. 6. Barbu Gh., Vduva I., M. Bolosteanu, Bazele Informaticii, Editura Tehnic, 1995. 7. Bohm C., G. Jacopini, Flow-diagrams, Turing Machines and languages with only two formation rules. Comm. ACM., 9, 1966, 366-371. 8. Calude C., Complexitatea calculului. Aspecte calitative. Editura stiintific si Enciclopedic, Bucuresti, 1982. 9. Dahl, O.J., Dijkstra E. W., C. A. R. Hoare, Structured programming, Academic Press, London, 1972. 10. Enescu Gh., Logica simbolic, Editura stiintific, Bucuresti, 1971. 11. Flanagan D., Java in a Nutshell: A Desktop Quick Reference for Java Programmers, O Reilly & Associates, INC. 12. Hoare C. A. R. s.a., Laws of Programming, Comm. ACM., 30(8), 1987, 672-685. 13. Ichim I., Gh. Marinescu, Metode de aproximare numeric, Editura Academiei RSR, 1986. 14. Isaacson E., H. B. Keller, Analysis of Numerical Methods, Wiley, 1966. 15. Kerninghan B. W., D. M. Ritchie, The C Programming Language, Englewood Cliffs, Prentice Hall, 1978, 1988. 16. Knuth D., The art of computer programming, Vol. 1, Fundamental Algorithms, 3rd ed., Addison Wesley Longman, 1997; Arta Programrii Calculatoarelor. Vol. 1, Algoritmi Fundamentali, Teora, 1999. 17. Knuth D., The Art of Computer Programming. Seminumerical Algorithms, Vol. 2, 1981; Tratat de programare a calculatoarelor. Algoritmi seminumerici, Editura Tehnic, 1983. 18. Knuth, Moris, Pratt, Fast pattern matching in strings, SIAM Journal on Computing, 6, 2, 1975. 19. Livovschi L., H. Georgescu, Sinteza si analiza algoritmilor. Editura Stiintific si Enciclopedic, 1986. 20. Mateescu E., I. Maxim, Arbori, Editura Tara fagilor, 1996. 21. Niculescu R., Albeanu G., V. Domocos, Programarea calculatoarelor. Probleme rezolvate in limbajul Pascal, Editura Tempus, 1992. 22. Novikov P.S., Elemente de logic matematic, Editura Stiintific, Bucuresti, 1966. 23. Pan V., Strassen's algorithm is not optimal, Proc. 19th Annual Symposium on the Foundations of Computer Science, 1978. 24. Plya G., Cum rezolvm o problem? Editura Stiintific, Bucuresti, 1965. 25. Popovici C., Georgescu H, L. State. Bazele Informaticii I, Tipografia Universittii Bucuresti, 1990. 26. Popovici C., Rudeanu S., H. Georgescu, Bazele Informaticii II, Tipografia Universittii, 1992. 27. Rudeanu S., Latici si algebre booleene, Tipografia Universittii Bucuresti, 1982.

1 of 2

12/9/2007 7:22 PM

Bazele Informaticii: Bibliografie suplimentara

file:///C:/ACTIV/Proc/FINAL/BIB.HTM

28. State L., Elemente de logic matematic si demonstrarea automat a teoremelor. Tipografia Universittii Bucuresti, 1989. 29. Strassen V., Gaussian elimination is not optimal, Numeriche mathematik, 13, 1969, 354-356. 30. Vduva I., Modele de simulare cu calculatorul. Editura Tehnic, 1975. 31. Vduva I., Sisteme Informatice, Tipografia Universittii Bucuresti, 1981. 32. Wirth N., Systematic Programming. An Introduction, Prentice Hall, 1972. 33. Wirth N., Algorithms + Data Structures = Programs, Prentice Hall, 1976. 34. Wirth N., The programming development by stepwise refinement, Comm. ACM., 14 , 1971, 221-225. 35. Wirth N., Modula: A language for modular programming, Software Practice and Experience, 7, 1977, 3-35. 36. Waite M., R. Lafore, Data Structures & Algorithms in Java, Waite Group Press, 1998; Structuri de date si algoritmi in Java, Teora, 1999. 37. Winograd S., On the multiplication of 2x2 matrices, IBM Research Report 267, 1970.

Vreau sa vad inceputul listei Vreau sa vad Cuprinsul. Data ultimei actualizari: 26 Februarie 2002

2 of 2

12/9/2007 7:22 PM

1
5 min

Se consider programul:
#include <stdio.h> void main(void){ int n, a; printf("a = "); scanf("%d", &a); printf("n = "); scanf("%d", &n); printf("a = (a>>n)<<n are rezultatul : %d", a = (a>>n)<<n ); } Ce se afieaz pentru: i) a = 7, n = 2; ii) a = 8, n = 2; iii) a = 8, n = 4;

2
5 min

Se consider programul: #include <stdio.h> int a, b; float x; int f(int c){ int a; /* linia T */ a = 7; b = 3; return a+b+c;
}

void main(void){ a = 1; b= 2; x = f(a)/10; printf("%3d %3d", a, b); printf(" %5.1f", x);


}

Ce va afia, dac: a) lipsete linia T b) nu lipsete linia T

3
5 min

Se consider programul: #include <stdio.h> int suma(int max){ int i; static int s = 0; for (i=s; i<max; i++) s+=i; return s; } void main(void){ int j, n= 5; for (j=0; j<n; j++) printf("%3d",suma(j)); }

Ce afieaz ?

4
5 min

Se consider programul: #include <stdio.h> void main(void){ int t[4]={0, 1, 2, 3}; int *p = &t[1]; printf("%d\n", *p++); printf("%d\n", *++p); printf("%d\n", ++*p); } Ce afieaz ?

5
3 min

Se consider programul: #include <stdio.h> int a[5] = {0, 1, 2, 3, 4}; int i = 4; int j =2; void main(void){ printf("\n%d", a[i]); printf("\n%d", i[a]); }
Ce afieaz? a) 4, 4 b) 4, 0 c) Construcia i[a] nu este corect.

6
1 min

Declaraia char *x[]; l definete pe x ca:


pointer ctre tablouri de tip char; b) tablou de pointeri ctre elemente de tip char c) a i b
a)

7
2 min

Ce afieaz secvena de program ?

#include <stdio.h> void main(void){ putchar (getchar()-'A'+'a'); putchar('\n'); } dac se introduce: i) A ii) G iii) X

8
6 min

Se consider programul: #include <stdio.h> char i = 0x63; signed char j = 9, k; void main(void){ printf(" i = %d \n", i = i & 0x5f); printf(" j = %d \n", ~j); printf(" k = %d \n", k = j | 0x30); }

Ce valori vor avea variabilele i , j i k dup executarea acestuia? Interpretai rezultatul.

9
4 min

Fie programul: #include <stdio.h> unsigned f(unsigned n){ unsigned i, p; for (p = 1, i = 1; i<=n; i++) p *= i; return p; } void main(void){ for (int n = 0; n<=20; n++) printf("%d \n", f(n)); }

Exista numere naturale n pentru care programul nu afieaz rezultatul corect ? Argumentai !

10
9 min

Fie programul:
char *dup2(const char *str){ static char p[1000]; int n, k; for(n=0; str[n]!='\0';n++); for(k=0; k<n; k++){ p[2*k]=str[k]; p[2*k+1] = str[k]; } p[2*n]='\0'; return p; } int main(int argc, char *argv[]){ char *p1, *p2; if(argc != 3){printf("Utilizai: %s sir1 sir2\n", argv[0]); return 0; } p1=dup2(argv[1]);puts(p1); p2=dup2(argv[2]);puts(p2); puts(p1); puts(p2); return 1; } Studiai comportamentul programului i simulai executarea acestuia. Precizai intrrile i ieirile programului.

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