Academia de Studii Economice din Bucureşti Facultatea de Cibernetică, Statistică şi Informatică Economică Catedra de Informatică Economică

Algoritmi în programarea calculatoarelor
* material didactic pentru ID *
Cristian Uscatu Cătălina Cocianu Cătălin Silvestru

Acest material are la bază lucrarea Programarea calculatoarelor. Algoritmi în programare autori I. Gh. Roşca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea publicată la Editura ASE Bucureşti, 2007

Bucureşti, 2010

2

Algoritmi în programare

Titlul cursului:

Algoritmi în programare

Introducere: Cursul Algoritmi în programare se adresează studenţilor facultăţii de Cibernetică, Statistică şi Informatică Economică din cadrul Academiei de Studii Economice din Bucureşti. Conform planului de învăţămînt, cursul se desfăşoară în semestrul 1 al anului 2 de studiu. Cursul Algoritmi în programare este destinat iniţierii studenţilor în cîteva aspecte ale programării calculatoarelor. Limbajul utilizat pentru demonstrarea conceptelor şi aplicaţiile practice este C standard (ANSI C). Obiectivele principale ale cursului vizează însuşirea de către studenţi a cunoştinţelor teoretice şi a abilităţilor practice de lucru referitoare la următoarelor elemente din cadrul programării calculatoarelor: tipurile dinamice de date şi modul de lucru cu date alocate dinamic (masive de date); analiza, proiectarea, implementarea şi utilizarea a subprogramelor; structurile de date externe (fişiere) şi abilităţi de lucru cu acestea. Cursul Algoritmi în programare este structurat în patru unităţi de învăţare, corespunzătoare elementelor principale studiate (date dinamice şi subprograme – cîte o unitate de învăţare, fişiere de date – 2 unităţi de învăţare). În cadrul procesului de instruire pot fi utilizate ca resurse suplimentare materialele puse la dispoziţie de biblioteca facultăţii, atît în cadrul sălii de lectură cît şi prin serviciul de împrumut. De asemenea, laboratoarele Catedrei de Informatică Economică pot fi utilizate pentru studiu individual pe toată durata programului de lucru, atunci cînd nu se desfăşoară ore (programarea orelor în laboratoare poate fi consultată la secretariatul Catedrei). Calculatoarele din laboratoare oferă medii de programare adecvate pentru dezvoltarea abilităţilor de lucru în limbajul C şi posibilitatea de accesare a bibliotecii virtuale a instituţiei. Evaluarea cunoştinţelor se va realiza prin intermediul lucrărilor susţinute pe parcursul semestrului astfel: o probă practică, constînd în rezolvarea a două probleme de programare din cadrul tematicii cursului; o lucrare scrisă. Ambele teste se susţin în timpul orelor aferente temelor de control din calendarul disciplinei. Cele două teste contribuie la formarea notei finale astfel: proba practică constituie 40% din nota finală; lucrarea scrisă constituie 50% din nota finală; din oficiu se acordă 10%.

* material didactic pentru ID *

3

Cuprins
1. Tipuri dinamice de date .......................................................................................................5 Obiectivele unităţii de învăţare ...........................................................................................5 1.1. 1.2. 1.3. Tipuri de date dinamice ...........................................................................................5 Declararea şi iniţializarea pointerilor.....................................................................6 Utilizarea pointerilor................................................................................................7

1.3.1. Operaţii cu pointeri ................................................................................................7 1.3.2. Legătura între pointeri şi masive ..........................................................................9 1.4. 1.5. 1.6. Alocarea dinamică a memoriei..............................................................................11 Modificatorul const.................................................................................................12 Tratarea parametrilor din linia de comandă .......................................................13

Răspunsuri şi comentarii la testele de autoevaluare .......................................................13 Rezumat...............................................................................................................................14 Bibliografia unităţii de învăţare ........................................................................................14 2. Subprograme.......................................................................................................................15 Obiectivele unităţii de învăţare .........................................................................................15 2.1. 2.2. Construirea şi apelul subprogramelor..................................................................15 Transferul datelor între apelator şi apelat ...........................................................18

2.2.1. Transferul prin parametri ...................................................................................18 2.2.2. Simularea transmiterii parametrilor prin adresă .............................................20 2.2.3. Comunicaţia prin variabile globale ....................................................................21 2.3. 2.4. 2.5. 2.6. Pointeri spre funcţii ................................................................................................ 22 Funcţii cu număr variabil de parametri...............................................................26 Calcul recursiv ........................................................................................................29 Exemple de aplicaţii cu subprograme recursive..................................................31

Răspunsuri şi comentarii la testele de autoevaluare .......................................................36 Rezumat...............................................................................................................................38 Bibliografia unităţii de învăţare ........................................................................................38

... Răspunsuri şi comentarii la testele de autoevaluare ............ 4........ 78 Corespondenţa internă dintre chei şi numere relative .................. Răspunsuri şi comentarii la testele de autoevaluare . 98 . 63 4..........7...................................................................................... 62 Rezumat....... 3........1...............................................1...................................................... Articolul şi fişierul................... 4......... Algoritmi de prelucrare a fişierelor de date ................... 95 4..................................... 79 4.......................................3....................6.................2... 3............................... 49 Operaţii de prelucrare a fişierelor.............................................. 4.........................3....................... Articol: caracteristici generale şi mod de declarare .................... 4.....................................................................3.6. 51 Nivelul inferior de prelucrare a fişierelor .. 39 3................. 44 Constante de tip articol. 3....................................... 39 Referirea articolului şi a elementelor componente............ 4........................................ 94 Prelucrarea vectorilor............................... 76 Codificarea internă prin numere relative ........................................... 62 Bibliografia unităţii de învăţare......................... 68 Algoritmi de prelucrare a fişierelor binare care necesită actualizare ................................................ 47 Structura sistemului de fişiere sub MS-DOS/Windows.............................................. 4...............3....1............................6.................................. 4.... 46 Metode de organizare a fişierelor şi tipuri de acces........... Sortarea fişierelor binare memorate dens....................................... Caracteristici generale ale algoritmilor de prelucrare a fişierelor ...........................................2..........2..........8.... 63 Algoritmi de prelucrare a fişierelor binare care nu necesită actualizare..........1...................1..............................................................8........... 89 Interclasarea fişierelor binare memorate dens ..... 3..3...............................2.................................... 3............................................ 76 Codificarea externă prin numere relative ..............................................................8...... 62 4.................3........... 55 3.... 93 Prelucrarea masivelor memorate în fişiere binare ...................................6........... 39 Obiectivele unităţii de învăţare ..... 63 Obiectivele unităţii de învăţare .................................................................................................. 4...........................5............................................... 97 Bibliografie..................................................... 3..... 41 Articole cu structuri complexe ..........................................4 Algoritmi în programare 3....................................................................................................4.............................4...... 3......2..................................... 45 Fişierul şi articolul................................................ 94 Prelucrarea matricelor .. 52 Nivelul superior de prelucrare a fişierelor.... 96 Bibliografia unităţii de învăţare......... 3.......................5.............................

....................... legătura dintre pointeri şi masivele de date........................... alocarea dinamică a datelor............................................. 1......................................1...3.7 1............................................ operaţii cu datele de tip pointer şi aritmetica pointerilor...................................................... se vor asimila cunoştinţe şi abilităţi de lucru privind: tipurile de date pointer...5................12 1............ studenţii vor avea cunoştinţe teoretice şi abilităţi practice despre tipurile dinamice de date utilizate în programarea calculatoarelor şi vor putea utiliza aceste tipuri de date şi structurile de tip masiv de date pentru rezolvarea problemelor de programare....5 1.......1..... Memoria internă Segmen:offset Pointer Zona de memorie indicată de pointer Fig................... tratarea parametrilor primiţi în linia de comandă....................4...........................14 Obiectivele unităţii de învăţare După studierea acestei unităţi de învăţare...............................2...............................3.....................................................6......... Utilizarea pointerilor..11 1...........................................................7 1................................6 1..............1................ Un pointer este adresa unei alte zone de memorie Folosirea pointerilor prezintă următoarele avantaje: ....... Tratarea parametrilor din linia de comandă ............................13 Răspunsuri şi comentarii la testele de autoevaluare ........14 Bibliografia unităţii de învăţare ....................................................... în limbajul C...................... Modificatorul const...... Tipuri dinamice de date Cuprins Obiectivele unităţii de învăţare .............. Legătura între pointeri şi masive ...................5 1...9 1.................................................................................. Operaţii cu pointeri . Tipuri de date dinamice Pointerul este un tip de dată predefinit...2...* material didactic pentru ID * 5 1.......................... Tipuri de date dinamice ............... Concret.......1)..................................... Declararea şi iniţializarea pointerilor......................... Alocarea dinamică a memoriei.......3................................................... 1...........13 Rezumat..... care are ca valoare adresa unei zone de memorie (figura 1..............1..........

Exemple 4. este o declaraţie de pointer. poate primi ca valoare adresa unei variabile numai de tipul TIP. atunci nume poate primi adresa oricărei variabile. de orice tip.}* x. În operaţiile cu pointeri se folosesc următorii operatori specifici: Operatori Simbol Utilizare * tip* Operator de referenţiere & &nume Operator de referenţiere * *nume Operator de dereferenţiere ⇒ defineşte un nou tip de dată (pointer la tip). struct complex pul complex. Dacă TIP este void. int* nume = &a. ⇒ accesează conţinutul zonei de memorie indicate de pointer. {a. ⇒ extrage adresa unei variabile (creează o referinţă). int a. • posibilitatea alocării dinamice a memoriei. ⇒ este o atribuire corectă. ⇒ n este o variabilă de tip pointer spre întreg. nume poate primi ca valoare doar adresa . ea primeşte implicit valoarea NULL. ⇒ este o atribuire incorectă. 1. • calculul adreselor. Declararea şi iniţializarea pointerilor Fie TIP un tip de dată oarecare în limbajul C (inclusiv void). iar nume este o variabilă de tipul pointer spre TIP. Declararea TIP* nume. * & * Exemplu 1. float b. ⇒ x este o variabilă de tip pointer spre o structură de ti- 3. 4. Deoarece nu se cunoaşte tipul zonei de memorie. Operaţiile care se pot efectua asupra zonei respective de memorie sînt definite de tipul acesteia. ⇒ p este o variabilă de tip pointer spre void. Cînd variabila nume nu este iniţializată prin declarare. atunci TIP* este adresa unei zone de memorie de tip necunoscut. TIP* este un nou tip de dată denumit pointer spre TIP. p poate primi ca valoare adresa unei zone de memorie de orice tip. Exemple 2. Dacă TIP este un tip oarecare (mai puţin void) atunci tipul TIP* este adresa unei zone de memorie de un tip cunoscut.2. nume = &b. Cei doi operatori au efect invers: *&nume nume.b:real. void* p. Dacă TIP este void. nume. nume are ca valoare adresa variabilei a. La execuţie. • folosirea tipurilor procedurale de date. nu sînt definite operaţiile care se pot efectua asupra ei. *&nume reprezintă valoarea de la adresa variabilei nume (valoarea variabilei nume).6 Algoritmi în programare • înlocuirea expresiilor cu indici – înmulţirile din formula de calcul al rangului se transformă în adunări şi deplasări. Pentru pointerii din exemplele anterioare se rezervă în memoria principală (în segmentul de date) cîte o zonă de 4B în care se va memora o adresă. int* n.

la declarare. Utilizarea pointerilor 1. c.3. Fie secvenţa: int *nume. . b. prin incrementare/decrementare. Se observă folosirea conversiei de tip (typecasting). de orice tip. c=*(int*)nume2. Operaţii cu pointeri Asupra pointerilor se pot efectua operaţii aritmetice. nume = &a.* material didactic pentru ID * 7 unei variabile întregi. Teste de autoevaluare 1. Care sînt cele două tipuri de pointeri? Care sînt diferenţele dintre ele? 1. ⇒ pointer fără tip ⇒ variabile de tip întreg.c. b=5. Chiar dacă un pointer spre tipul void poate primi ca valoare adresa unei variabile de orice tip. int* nume=&a. *(int*)nume2=10.3. valoarea lui nume se incrementează/decrementează cu numărul de octeţi necesari pentru a memora o dată de tip TIP. ca şi pentru celelalte variabile. cît şi pentru referirea datelor de la adresa indicată de pointer. a. astfel: int a. La alocarea dinamică a memoriei se foloseşte o altă metodă pentru iniţializarea unui pointer. 2. nume = &b. nume poate primi ca valoare adresa oricărei variabile. nume2=&a. nume=&a. respectiv real ⇒ atribuire corectă ⇒ atribuire corectă. int a. float b. Iniţializarea pointerilor se poate realiza ca în exemplul precedent sau. nume=&a.*nume2. Operatorul de dereferenţiere se utilizează atît pentru definirea tipului pointer. int a. adică cu sizeof(TIP). Incrementare/decrementare Dacă nume este pointer spre un tip TIP.b. nume2-nume are ca valoare o adresă care este decrementată şi primeşte valoarea nume-sizeof(int) (care este adresa lui c). int* nume. atunci cînd se lucrează cu pointeri spre tipul void (fără tip). 5. Se observă folosirea operatorului de referenţiere & pentru a crea o referinţă către variabila a. Exemplu 6. Care sînt operatorii specifici lucrului cu pointeri în limbajul C? Daţi exemple de utilizare a lor. c=*nume+b. void* nume2. nume++ nume are ca valoare o adresă care este incrementată şi primeşte valoarea nume+sizeof(int) (care este adresa lui b). pentru a putea lucra cu ea este necesară gestionarea corectă a tipului operanzilor. nume2=&b.1. *nume=b. void* nume.

În urma atribuirii ++p sau lui v[i+1]. dacă adresa pe care o conţine p este mai îndepărtată de începutul memoriei decît adresa conţinută de q. deci adevărată. >. Asemănător se execută scăderea unui întreg dintr-un pointer.h astfel: #define NULL 0 De multe ori se preferă comparaţia directă cu zero (nume==0 sau nume!=0). Valoarea NULL este definită în stdio.8 Algoritmi în programare Situaţia iniţială este următoarea: După cele două operaţii: nume 4B nume2 4B c a 2B 2B b 2B nume 4B nume2 4B c a 2B 2B b 2B Analog se execută operaţiile ++nume şi --nume. deci expresia este falsă. rezultatul va fi tot un pointer spre TIP. Este permisă şi compararea unui pointer cu o valoare constantă. Operaţiile descrise anterior se folosesc frecvent în lucrul cu masive. float* p. În loc de nume==0 se poate folosi expresia nume. atunci cînd se adună un întreg n la pointerul p. >=). care are ca valoare adresa memorată în p. p=&v[i]. *q). !=. folosind unul din operatorii == sau !=. <. int i. adică n*sizeof(TIP). adică adresa Adunarea/scăderea unui întreg În general. Compararea a doi pointeri Limbajul C permite compararea a doi pointeri într-o expresie. <=. mai mare sau mai mic decît nume2. Presupunînd că p a fost iniţializat cu valoarea 0x0fff:0x3450. float v[20]. Doi pointeri sînt egali dacă adresele care constituie valorile lor sînt egale. dacă p este un pointer spre un tip TIP. Rezultatul expresiei nume op nume2 (unde op este unul din operatorii precizaţi anterior) este adevărat (nenul) sau fals (zero) după cum nume este egal. q primeşte valoarea 0xffff:0x344a (se scad 2*4 octeţi). Uzual se foloseşte comparaţia cu valoarea NULL pentru a verifica dacă pointerul a fost iniţializat (un pointer neiniţializat are valoarea NULL). Exemplu 7. q primeşte valoarea 0xfff:0x345c (se adună 3*4 octeţi). atunci are valoarea NULL (adică 0). p va avea ca valoare adresa lui v[i] plus 4 octeţi. unde i poate avea valori între 0 şi 19 p++. în urma operaţiei q=p+3. nume+n nume primeşte valoarea nume+n*sizeof(int) nume-n nume primeşte valoarea nume-n*sizeof(int) Exemplu 8. În caz contrar valoarea expresiei este nenulă. folosind oricare din operatorii relaţionali (==. la care se adună de n ori numărul de octeţi necesari pentru a memora o dată de tip TIP. Aceasta se interpretează astfel: dacă nume nu a fost iniţializat. un pointer p este mai mare decît altul q. În urma operaţiei q=p-2. Asemănător se foloseşte expresia !nume. Fie p şi q pointeri spre tipul float (float* p. începînd de la adresa 0 (zero). . Privind memoria internă liniar.

q=&b. 1. Valoarea unei expresii diferenţă se calculează astfel: se face diferenţa între cele două adrese (în octeţi). Pentru masivele unidimensionale: int m[50]. m i j a b Fig. variabilele p şi r au ca valoare adresa lui a. care este mai mare decît a lui a (datorită faptului că a a fost alocat primul). if(p==r) printf("Pointeri egali\n"). Diferenţa dintre doi pointeri Fie secvenţa: int m[50]. Pe ecran se va afişa: Pointer neinitializat! Pointeri egali 7 deoarece t are valoarea NULL. De aceea m nici nu poate primi valori în timpul execuţiei programului (nu se poate schimba adresa memorată în m). if(t) printf("Pointer initializat!\n"). Expresia a-b are valoarea i-j. else printf("Pointer neinitializat!\n"). m are tipul int* int* p.* a. Cei doi pointeri trebuie să refere acelaşi tip de dată. nu despre scăderea a doi pointeri. a=&m[i]. b=&m[j]. Reprezentarea semnificaţiei variabilelor din exemplul anterior Atenţie: vorbim despre diferenţa dintre doi pointeri (înţelegînd distanţa dintre cele două adrese). else printf("%d\n". altfel rezultatul nu are semnificaţie (trebuie să aibă tipuri identice).b. float* p.49].. else printf("Pointeri diferiti\n"). r=&a. exprimată în zone de memorie de lungime sizeof(int). if(p>q) printf("%d\n".a). float a.2.* material didactic pentru ID * 9 Exemplu 9. b=7.3.t. p are tipul int* Diferenţa constă în faptul că zona de memorie către care punctează m este rezervată la compilare (ceea ce nu se întîmplă în cazul pointerilor declaraţi ca atare). a=5. * b.2).r. Operaţia este utilă în lucrul cu masive. El memorează adresa primului element din masiv. Legătura între pointeri şi masive În limbajul C numele unui masiv este un pointer către tipul de dată al elementele masivului. unde i şi j sînt întregi în intervalul [0. interpretată ca distanţă între adresele a şi b.2. iar q conţine adresa lui b.b).q. apoi se împarte la dimensiunea tipului de dată referită de cei doi pointeri (tipul int în exemplul de mai sus – vezi figura 1. Referirea unui element m[i] este echi- . p=&a.1.

Figura 1. Limbajul C nu face nici un fel de verificări în privinţa depăşirii limitelor indicilor masivului.1] m[0.1] m[3.49 m[4] … m[49] m[49.0] m[2. Un masiv cu n dimensiuni este tratat ca un pointer spre un vector de pointeri către masive cu n-1 dimensiuni.49] … m[49. Un masiv cu trei dimensiuni float m[10][10][10] poate fi interpretat ca un pointer spre un vector de pointeri spre matrice. . de aceea expresiile m[500] sau m[-7] vor fi considerate corecte de compilator. Exemplu 11.1] … Fig. Mai mult. float* v[10]. Reprezentarea modului de alocare dinamică a spaţiului necesar pentru memorarea unei matrice 50x50 Analog pot fi interpretate masivele cu mai multe dimensiuni. pentru vectori se poate declara un pointer iniţializat cu adresa de început a masivului. int m[50][50].0] m[3.3. Să se scrie secvenţa de program care citeşte de la tastatură elementele unei matrice. v[3] şi p[3] referă aceeaşi zonă de memorie. De exemplu. Test de autoevaluare 3.49] m[3. Atenţie: această figură NU reprezintă modul de alocare în memorie a unei matrice statice! Doar pentru matricele alocate dinamic zonele de memorie sînt alocate în acest fel.49] m[2. Exemplu 10.0] m[49.0] m[0.0] m[4. p=v.1] … … … … … … … m[0.49] m[0. fiecare element al vectorului fiind la rîndul lui un pointer spre o linie a matricei (un vector de elemente de tip float). float* p. pointerul p conţine adresa de început a masivului şi poate fi folosit pentru referirea elementelor masivului. folosind expresii cu pointeri pentru adresarea elementelor matricei. Pentru masivele bidimensionale: m are semnificaţia următoare: m[i][j] *(*(m+i)+j). m m[0] m[1] m[2] m[3] m[0. După atribuire.0] … m[0.49] m[4. reprezintă „conţinutul de la adresa j plus conţinutul de la adresa memorată în m plus i”.1] m[2. poate fi utilizată pentru a înţelege mai bine cum se accesează elementele unui masiv bidimensional. Pentru a lucra cu elementele unui masiv static se poate folosi adresarea indexată (m[i] pentru vectori sau m[i][j] pentru matrice) sau adresarea elementelor prin pointeri (*(m+i) pentru vectori sau *(*(m+i)+j) pentru matrice etc). existînd riscul unor erori logice.10 Algoritmi în programare valentă cu *(m+i) – conţinutul de la adresa m+i. iar elementele masivului să fie referite prin intermediul acestui pointer. Aceasta poate fi interpretată astfel: m este un pointer spre un vector de pointeri.1] m[4. Este sarcina programatorului să se asigure că indicii nu vor depăşi limitele.1.3.

Funcţia returnează numărul va- . 14.* material didactic pentru ID * 11 1. Deoarece funcţia returnează pointer spre void este necesară conversia rezultatului spre tipul dorit. for(int i=0. Să se scrie o funcţie care să citească cel mult n numere întregi şi le păstreze în zona de memorie a cărei adresă de început este dată printr-un pointer. ⇔ rezervă în heap spaţiu pentru o valoare de tip întreg. prin funcţia standard: void* calloc(unsigned nr_elem. memorie pentru un vector cu 50 de elemente întregi. Funcţia primeşte ca parametru un pointer (indiferent de tip) spre zona de memorie pe care trebuie să o elibereze. /* se alocă spaţiu pentru vectorul cu adresele celor n linii ale matricei */ m=(int**)malloc(m*sizeof(int*)). Exemple 12. Limbajul C oferă posibilitatea de a aloca contiguu zone de memorie pentru mai multe date de acelaşi tip. Funcţia rezervă o zonă de n octeţi în heap şi returnează adresa acesteia.i<m. prin dublă indexare. int n. Există şi o variantă a lui malloc care returnează în mod explicit un pointer „îndepărtat” (far): void* farmalloc(unsigned long n). Pentru a rezerva spaţiu în heap se foloseşte funcţia standard: void* malloc(unsigned n). Eliberarea unei zone de memorie rezervate anterior se face prin funcţia standard: void free(void* p). Alocarea dinamică a memoriei Pentru a memora o dată de un anumit tip în heap este necesar să se declare un pointer către acel tip de dată. întorcînd un pointer spre zona respectivă. masiv=(int*)calloc(50.3 pentru a putea accesa elementele la fel ca în cazul unei matrice statice. Pentru eliberarea unei zone de memorie rezervate prin farmalloc se foloseşte funcţia standard: void farfree(void* p).p. nume=(int *) malloc(sizeof(int)). ⇔ rezervă spaţiu de 13.sizeof(int)).i++) /*se alocă spaţiu pentru fiecare linie a matricei. int** m. int* masiv. Funcţia calloc rezervă o zonă contiguă de memorie pentru mai multe elemente de acelaşi tip.4. unsigned dim_elem). Alocarea de spaţiu în heap pentru o matrice se face conform figurii 1. apoi să se rezerve memoria necesară. cîte p elemente*/ m[i]=(int*)malloc(n*sizeof(int)). astfel: int* nume.

int* p) { int nr. *t=valoare_noua. nu permite modificarea valorii lui nume printr-o expresie de atribuire nume = valoare_noua. i++. } return(i). dar se poate ca variabilei nume să i se atribuie o adresă (de exemplu. p++. În acest scop se foloseşte modificatorul const. *p=nr. while(p<q) { printf("Numarul %d= ". . b) tip const* nume = valoare. t=nume. Nu este permisă atribuirea de genul *nume=valoare_noua. &nr)!=1) break. sau Prin această declarare se defineşte un pointer spre o zonă cu valoare constantă. în cazul în care apelatorul are nevoie de valorile iniţiale. în plus. O altă posibilitate de lucru cu constante este iniţializarea unei variabile cu o valoare şi interzicerea modificării valorii acesteia. în acest caz se rezervă spaţiu de memorie în care se înscrie valoarea constantei (constantă obiect). const tip nume = valoare. i=0. i. 1. Faţă de o constantă simbolică. nume = p. int cit_nr(int n. sau Declaraţia este echivalentă cu tip nume=valoare dar. c) const tip* nume.12 Algoritmi în programare lorilor citite. int* q=p+n. } // q este adresa unde se termina zona // rezervata pentru cele n numere // cit timp nu s-au citit n numere // in caz de eroare la citire // se termina ciclul Teste de autoevaluare 4. i). if(scanf("%d". Pentru a modifica valoarea înscrisă în memorie la adresa memorată de pointerul nume se poate folosi totuşi un alt pointer: tip *t. const tip* nume = valoare. Modificatorul const În limbajul C constantele simbolice se declară prin directiva de preprocesare #define. Construcţia de mai sus se foloseşte la declararea parametrilor formali. pentru a împiedica modificarea lor în corpul subprogramelor.5. Sînt permise următoarele forme de utilizare: a) tip const nume = valoare. unde p este un pointer spre tip). Să se scrie o secvenţă de program pentru alocarea dinamică spaţiului necesar pentru un vector şi citirea de la tastatură a dimensiunii şi elementelor sale.

6. Exemplu 15. &n).i<m. Să se scrie o un program care preia din linia de comandă doua valori întregi. scanf("%f".i<argc. char* argv[]) unde argc conţine numărul de parametri ai programului.%d)= ". Programul poate accesa argumentele prin intermediul parametrilor predefiniţi ai funcţiei main: void main(int argc.* material didactic pentru ID * 13 1. .i++) for(j=0. { int i. Dacă programul nu are nici un parametru.j++) { printf("a(%d. şi. linii:\n". Răspunsuri şi comentarii la testele de autoevaluare 3. în funcţia scanf trebuie transmise ca parametri adresele unde se depun valorile citite. le adună şi afişează cele două valori şi suma lor. } Observaţie: *(a+i)+j este un pointer.i++) printf("Parametrul nr. argc are valoarea 3 etc. Exemplu 16. *(a+i)+j ). for(i=0.i. Primul şir (cu adresa argv[0]) conţine identificatorul fişierului (inclusiv calea completă) care memorează programul executabil. Aceştia sînt şiruri de caractere despărţite prin spaţii.i.j). argv[i]). în exemplul anterior se putea scrie &*(*(a+i)+j). Să se scrie un program care afişează parametrii din linia de comandă. for(i=1. scanf("%d". printf("Nr. } Teste de autoevaluare 5.h> main(int argc.j<n. &m). Următoarele şiruri conţin parametrii în ordinea în care au apărut în linia de comandă (parametrii în linia de comandă sînt şiruri de caractere separate prin spaţii). reducînd. printf("Nr.n. care conţine adresa elementului a[i][j]. Dacă în linia de comandă nu se primesc doi parametri întregi. Variabila argv este un vector de pointeri care conţine adresele de memorie unde s-au stocat şirurile de caractere care constituie parametrii programului. float a[10][10]. rezultă *(a+i)+j. argc are valoarea 1. argv[0]). dacă programul are doi parametri. Interpretarea acestor parametri cade în sarcina programului. se va afişa un mesaj de eroare. %d: %s\n".. printf("Fisierul executabil: %s\n". Tratarea parametrilor din linia de comandă În linia de comandă a unui program pot să apară parametri (sau argumente). #include<stdio. incrementat cu 1. scanf("%d". coloane:\n"). int m. char *argv[]).

vol. // sau scanf("%f".i++) { printf("v(%d)= ".Programarea calculatoarelor. I. 2007 2. } Rezumat În cadrul acestei unităţi de învăţare au fost studiate următoarele aspecte ale programării calculatoarelor cu privire la tipurile dinamice de date: definiţia tipurilor de date pointer. Ghilic-Micu. abilităţi necesare în continuare. scanf("%f". &n). preluarea şi prelucrarea parametrilor din linia de comandă prin intermediul pointerilor argc şi argv. Roşca. Gh. ASE Bucureşti. for(i=0. definirea tipurilor de date pointer în C. Cocianu. II. Cocianu. Liviu Negrescu . C. protejarea variabilelor statice şi dinamice împotriva modificării. ASE Bucureşti. printf("Nr. Ştiinţa învăţării unui limbaj de programare. Ed. Mircea .&(*v)[i]). C. Roşca. Uscatu . B. I. 2003 3. Cluj-Napoca. operaţii aritmetice cu pointeri. alocarea dinamică a datelor: alocarea dinamică a masivelor de date şi accesarea elementelor componente. Gh. C.14 Algoritmi în programare 4. 1994 . pentru abordarea aspectelor mai complexe ale programării calculatoarelor.i). *v=(float*)malloc(*n*sizeof(float)). Ed. Bibliografia unităţii de învăţare 1. utilizarea pointerilor. M. Teorie şi aplicaţii. C. I.i<n. scanf("%d ". Stoica. Algoritmi în programare. v+i). După încheierea studierii acestei unităţi de învăţare. declararea şi iniţializarea variabilelor de tip pointer. Stoica. Ed. int i. Uscatu.Programarea calculatoarelor. M. Ghilic-Micu. B. M. elemente: "). prin intermediul modificatorului const. Microinformatica.Limbajele C şi C++ pentru începători. studenţii sînt au cunoştinţele şi abilităţile necesare lucrului cu pointeri şi date alocate dinamic.

..................................... Exemple de aplicaţii cu subprograme recursive........................................................... Construirea şi apelul subprogramelor Conform teoriei programării.......15 2.........2......................3....5... Construirea şi apelul subprogramelor................ Subprogramele C sînt.................18 2...... Sistemele C au colecţii de biblioteci care conţin funcţii standard.............. Transferul datelor între apelator şi apelat ...... se vor asimila cunoştinţe şi abilităţi de lucru privind: tipurile de subprograme...........18 2.38 Bibliografia unităţii de învăţare . Subprograme Cuprins Obiectivele unităţii de învăţare .... Pointeri spre funcţii .................................................... Concret.............................* material didactic pentru ID * 15 2................ toate prin intermediul parametrilor de ieşire............15 2........ care returnează oricîte rezultate.... Transferul prin parametri ..... comportîndu-se ca o procedură (conform definiţiei din teorie)............. în mod nativ...... funcţii...2. numită main()... 22 2.........................1........................................................ studenţii vor avea cunoştinţe teoretice şi abilităţi practice necesare pentru lucrul cu subprograme............. masiv.................... 2.... subprogramele sînt clasificate în funcţii.........................1...................................20 2.................................................................... Funcţii cu număr variabil de parametri....................................29 2............. lucrul cu parametri de tip simplu.... structura şi apelul subprogramelor: transferul parametrilor între apelator şi apelat...................... lucrul cu liste de parametri cu lungime variabilă.... eventual.....6................ care este apelată la lansarea în execuţie a programului......................... Textul sursă al unui program C poate fi partiţionat în mai multe fişiere............. subprogram..38 Obiectivele unităţii de învăţare După studierea acestei unităţi de învăţare.... testate sepa- ......2.....................4........ lucrul cu subprograme recursive.............................3. Există o funcţie....................... Simularea transmiterii parametrilor prin adresă .......21 2.............2...26 2...... Calcul recursiv ............................... care returnează un singur rezultat prin „numele” funcţiei şi oricîte prin parametri de ieşire şi proceduri.. Comunicaţia prin variabile globale .. Ei vor putea analiza problemele şi construi subprogramele care le rezolvă corect........... Un program C este un ansamblu de funcţii care realizează activităţi bine definite................................................. Fiecare fişier constă dintr-un set de funcţii şi declaraţii globale....................2..... Pot fi construite subprograme care nu returnează nici un rezultat prin numele lor.. Fişierele care constituie partiţia pot fi compilate şi...1........31 Răspunsuri şi comentarii la testele de autoevaluare ..........................36 Rezumat.............

A treia formă este folosită în cazul funcţiilor care nu returnează nici o valoare prin numele lor (poate chiar să lipsească). înaintea primului apel. Tipul expresiei din instrucţiunea return trebuie să coincidă cu tipul funcţiei. în cadrul fişierului sursă. tipul funcţiei va fi void sau va fi omis. fie funcţiile se apelează unele pe altele (de exemplu. dar numai unul va conţine funcţia main(). nume este un identificator care reprezintă numele funcţiei. sau sau Prima şi a doua formă sînt folosite în cazul funcţiilor care returnează o valoarea prin numele lor. La limită. este considerat tipul implicit (int pentru unele compilatoare. Sintaxa generală este: tip nume ([lista-parametri-formali]). fie definiţia nu se află în fişierul sursă. return expresie. Numele parametrilor pot lipsi. sînt prevăzute declaraţii ale subprogramelor fără definirea corpului lor (declaraţii anticipate). Există cazuri particulare în care. fiind suficientă specificarea tipurilor lor. Forma ei generală este: return(expresie). (punct şi virgulă). void pentru altele). Pentru fiecare parametru trebuie specificat tipul. Pentru funcţiile care nu întorc o valoare prin numele lor. Prototipul este de fapt un antet de funcţie după care se scrie caracterul .16 Algoritmi în programare rat. Prin executarea acestei instrucţiuni se evaluează expresia.tip3 identificator …]]] Parametrii sînt separaţi prin virgulă. chiar dacă mai mulţi parametri sînt de acelaşi tip (nu este posibilă definirea de liste de parametri cu acelaşi tip). Pentru a oferi compilatorului posibilitatea să efectueze verificarea validităţii apelurilor. return. Dacă lipseşte. Atenţie: corect este tipul rezultatului întors de funcţie prin numele său. Vom vedea mai tîrziu că sintagma tipul funcţiei are un alt înţeles. Antetul are forma: tip nume([lista-parametri-formali]) unde: tip poate fi un tip simplu de dată. Corpul este o instrucţiune compusă: conţine declaraţiile locale şi instrucţiunile executabile care implementează algoritmul. cazul recursivităţii mutuale). Declararea unui subprogram apare. În limbajul C nu este admisă imbricarea. adică definirea unui subprogram în cadrul altui subprogram şi nu sînt permise salturi cu instrucţiunea goto (instrucţiune de salt necondiţionat) în afara subprogramului curent. mai complex. lista poate fi vidă. Dacă este prezentă. Funcţiile C sînt formate din antet şi un corp. Prototipul tre- . Corpul funcţiei se execută pînă la executarea ultimei instrucţiuni sau pînă la executarea instrucţiunii return. efectul ei este încheierea execuţiei funcţiei. Aceste declaraţii se numesc prototipuri şi apar în afara oricărui corp de funcţie.tip2 identificator[. lista-parametrilor-formali conţine parametrii formali sub forma: [tip1 identificator1[. valoarea sa este atribuită funcţiei şi se încheie execuţia funcţiei.

h).&n2). Ce tipuri de subprograme pot fi scrise în limbajul C? 3.n2)). Prototipurile funcţiilor standard se află în fişiere header (cu extensia . Domeniul de valabilitate a declaraţiei unui subprogram este limitat la partea care urmează declaraţiei din fişierul sursă. int b) { int r. int b) { int r. Expresia care conţine apelul poate la limită să conţină un singur operand şi chiar să fie o instrucţiune de tip expresie.} while(r<>0). d=i.h> int cmmdc(int a. do {r=d%i.} while(r<>0). Fiind funcţii.cmmdc(n1. return i.i=b.* material didactic pentru ID * 17 buie inserat în program înaintea primului apel al funcţiei.&n1.n2)). /* prototipul functiei cmmdc*/ void main() { int n1. } 2. } /*definirea functiei cmmdc*/ Teste de autoevaluare 1.cmmdc(n1. printf("Numerele pentru care se va calcula cmmdc:"). #include <stdio. d=i.h> int cmmdc(int. prin numele funcţiei urmate de lista parametrilor reali. Ce este un prototip? . int).n2. if(n1&&n2) printf("\ncmmdc=%d". i=r.n2.&n1. scanf("%d%d".d=a.i=b. nefiind folosită în nici un fel. Acelaşi exemplu folosind un prototip pentru funcţia cmmdc: #include <stdio. Utilizarea unei funcţii din bibliotecă impune includerea fişierului asociat. Care este diferenţa teoretică dintre un subprogram de tip funcţie şi un subprogram de tip procedură? 2. cu directiva #include. if(n1&&n2) printf("\ncmmdc=%d". else printf("Numerele nu sînt nenule! "). În aceste cazuri valoarea returnată de funcţie se pierde. subprogramele C se apelează ca operanzi în expresii. Exemple 1. } int cmmdc(int a.&n2). scanf("%d%d". } /*definirea functiei cmmdc*/ void main() { int n1.d=a. return i. i=r. printf("Numerele pentru care se va calcula cmmdc:"). utilizînd algoritmul lui Euclid şi un apelator pentru testare. Să se scrie o funcţie care calculează cel mai mare divizor comun dintre două numere întregi nenule. do {r=d%i. else printf("Numerele nu sînt nenule!").

În limbajul C este implementat numai transferul prin valoare (valoarea parametrului real este copiată în stivă iar subprogramul lucrează numai cu această copie). int n) { int i. s-au conturat două posibilităţi de transfer al datelor între apelator şi apelat: prin parametri şi prin variabile globale.1. se dereferenţiază adresa primită). Exemple 5. fiind adresa parametrului real.2. Rezultatul se întoarce prin numele funcţiei: float ps(float x[]. la ieşirea din subprogram. Exemplu 4. transferul parametrilor se poate face prin valoare sau prin adresă. Să se calculeze produsul scalar dintre doi vectori. Următoarele prototipuri sînt echivalente: tip_returnat nume1(float v[]. int dimensiune. se vor efectua explicit operaţiile care se fac automat la transferul prin adresă din alte limbaje: se transmite ca parametru adresa parametrului real iar în subprogram se lucrează cu indirectare (pentru a accesa valoarea parametrului. ……………… .prod+=x[i]*y[i++]). simularea transferului prin adresă se face în mod implicit.2. } Apelul se realizează astfel: float a[30]. Această practică este nerecomandată. tip_returnat p este transferat prin valoare. tip_returnat p este transferat prin valoare nume(tip_parametru p). 2.i<n.18 Algoritmi în programare 2. în stivă se va transfera adresa masivului iar referirea elementelor se face automat prin calcul de adrese (vezi capitolul Tipuri dinamice de date). parametrul real corespunzător. Folosind transferul prin valoare se pot transmite numai parametri de intrare în subprogram.prod=0. conversiile se fac similar atribuirilor. Transferul prin parametri Principial. Pentru parametrii de tip masiv. ci se folosesc în comun anumite zone de memorie. La apel. return prod. Operaţiile efectuate asupra unui parametru formal scalar (care nu este masiv) nu modifică. Prin utilizarea variabilelor globale nu se face un transfer propriu-zis. for(i=0. float y[]. b[30]. int n). Pentru a putea folosi parametri de ieşire trebuie simulat transferul prin adresă. Dacă prototipul precede apelul subprogramului şi nu există o sublistă variabilă de parametri. Exemplu 3. tip_returnat nume2(float *v. În acest scop. Dezvoltările ulterioare (C++) au implementat şi transferul prin adresă. datorită modului de construire a masivelor în C: numele masivului este un pointer. Transferul valorii este însoţit de eventuale conversii de tip realizate pe baza informaţiilor de care dispune compilatorul despre subprogram (tipurile parametrilor). Transferul datelor între apelator şi apelat În practica programării. nume(tip_parametru *p). int n).

ps(a. 6. int dimensiune.i++) if(*max<v[i]) {*nr_ap=1. ps(a. Să se calculeze produsul a două matrice statice. Rezultatul întors prin numele funcţiei este adresa spaţiului de memorie alocat pentru masiv.i<m.int poz[]) { int i. int dimensiune.(*prod)+=x[i]*y[i++]).&max. Să se calculeze produsul dintre o matrice şi un vector.pozitii). void produs(float a[][10].i<n.i++) for(j=0.k++)c[i][j]+=a[i][k]*b[k][j].k<n.int *nr_ap. int n.b.dimensiune.b.el_max.i=1.h> …………………… . int *nr_ap. printf("Produsul scalar al vectorilor a si b este:%f". max. Exemple 8.b[30]. nr_ap.dimensiune)). int n. *prod=0. poz sînt parametri de ieşire). Să se calculeze elementul maxim dintr-un vector şi poziţiile tuturor apariţiilor acestuia (v. În a doua variantă de rezolvare. void maxim(float v[].&produs_scalar). n sînt parametri de intrare.int p) { int i.produs_scalar.* material didactic pentru ID * 19 printf("Produsul scalar al vectorilor a si b este:%f".float b[][20].poz[0]=i. produs_scalar). 7. int *poz) pentru care corpul subprogramului este acelaşi. int n. float c[][20].k=0. Antetul subprogramului este echivalent cu construcţia void maxim(float *v. se pot scrie funcţii care returnează prin nume un tablou ca pointer – deoarece numele tabloului este echivalent în C cu adresa sa (pointer la începutul masivului). float y[]. } Apelul se realizează astfel: float a[30]. maxim(a.int n.nr_aparitii. } Observaţie: Deşi un tablou (masiv) nu poate fi returnat ca tip masiv prin numele unei funcţii. for(*max=v[0]. for(i=0.pozitii[30]. float *prod) { int i.j++) for(c[i][j]=0. #include<malloc. Unui astfel de masiv i se alocă memorie în funcţia care îl calculează.int m. max=v[i].&nr_aparitii. rezultatul se întoarce prin parametru.float *max.k. for(i=0. float *max.} else if(*max==v[i])poz[*nr_ap++]=i. simulînd transferul prin adresă: void ps(float x[].i<n.dimensiune. } Apelul se realizează astfel: float a[30].j.j<p.

Exemple 9. int m. Este preferabilă prima variantă. ………………………… prod(a.j++) p[i]+=a[i][j]*v[j]. Masivul rămîne în memoria principală şi poate fi modificat prin intermediul adresei sale de început.m. Astfel se poate simula transmiterea parametrilor prin adresă folosind pointerii. for(i=0.20 Algoritmi în programare float * prod(float a[][30]. return p. Simularea transmiterii parametrilor prin adresă Limbajul C permite transmiterea parametrilor numai prin valoare (la apelul subprogramelor se copiază în stivă valoarea parametrului real şi subprogramul lucrează cu această copie). Teste de autoevaluare 4.2.n. Folosind această proprietate. } . for(s=0.b. se pot modifica valorile elementelor masivului. iar modificările se vor propaga în blocul apelator.b.2. Subprogramul va folosi explicit acest pointer. În acest scop se transmite ca parametru un pointer spre variabila cu care trebuie să lucreze subprogramul apelat.n.i++) for(p[i]=0.b. Un exemplu în acest sens este funcţia de citire a datelor de la tastatură. b[30].j=0. float v[].m.j<n.m.i<m. prin pointeri..i++) s+=v[i]. float v[]. Pentru aceasta se folosesc două funcţii care returnează.i<n. Observaţie: la fiecare referire de element se apelează şi se execută funcţia. produsul a două matrice (înmulţire). Subprogramul nu poate modifica valoarea parametrului din apelator.i=0.m. Să se realizeze un program C pentru ridicarea unei matrice la o putere. void suma(float s. el este de fapt un pointer (adresa de început a masivului). int m. ceea ce duce la consum mare şi inutil de resurse.j.n). int n) { int i. int n) { float *p. *c. b) float a[20][30]. Dacă parametrul formal este un masiv.m ).n)[i]. deoarece valoarea care se copiază în stivă este adresa de început a masivului. ………………………… c=prod(a. 2.int i. b[30].. Fie un subprogram care calculează suma elementelor unui vector v de lungime n. i=0. Se lucrează cu „vectorul” prod(a.n) – elementele sale se referă ca i=0. Cu vectorul c se lucrează în modul obişnuit: elementele se referă prin indexare (c[i]. Subprogramul poate modifica valori care să se propage în apelator.int m. p=(float *)malloc(sizeof(float)*m). Parametrii acestei funcţii sînt adresele variabilelor ale căror valori trebuie citite (altfel spus: adresele zonelor de memorie unde trebuie depuse valorile citite de la tastatură). } Apelul se realizează astfel: a) float a[20][30]. respectiv ridicarea unei matrice la o putere (putere).

.3. } 2. m[20]. a este globală şi poate fi referită de oriunde. for(s=0. deoarece valoarea sumei este cunoscută numai în interiorul funcţiei (parametrul a fost transmis prin valoare). primul parametru actual este adresa variabilei în care se memorează suma: void main() { float x. float z(char b) { int b. x). scanf("%d". comunicarea valorilor între apelator şi apelat se poate realiza prin intermediul lor. deoarece nu se realizează un transfer propriu-zis. Comunicaţia prin variabile globale Variabilele globale se declară în afara funcţiilor. int* x) { do printf("Introduceti numarul: "). Să se realizeze un subprogram care citeşte de la tastatură o valoare întreagă care aparţine unui interval dat. …………… { /* instructiunea compusa r */ int d. Variabilele declarate într-o funcţie se numesc locale (din clasa automatic) şi pot fi referite numai din funcţia respectivă.* material didactic pentru ID * 21 Subprogramul suma calculează suma elementelor vectorului dar aceasta nu poate fi folosită de apelator. //… suma(&x. trebuie ca parametrul s să fie un pointer spre variabila în care se va memora suma elementelor vectorului: void suma(float* s. toate avînd posibilitatea utilizării şi modificării valorii din acea zonă. Exemplu 11. n). De aceea.2. …………… } } Domeniile de valabilitate a referirilor variabilelor declarate sînt următoarele: b poate fi referit doar în funcţia z. void citire(int a.i<n. } La apelul funcţiei.i=0. Domeniul de valabilitate a unei variabile locale este blocul (funcţia sau instrucţiunea compusă) în care a fost definită. int n) { int i.h> int a.i++) *s+=v[i]. …………… } main() { int c. De fapt o zonă de memorie este accesată de mai multe entităţi (subprograme). #include <stdio. d poate fi referit doar în instrucţiunea compusă r. Pentru ca subprogramul să fie utilizabil. c poate fi referit doar în funcţia main. while((*x<=a)||(*x>=b)). În apelator valoarea variabilei corespunzătoare parametrului formal s nu va fi modificată. Atenţie: este impropriu spus transfer prin variabile globale. Ele pot fi referite din orice alte funcţii. m. 10. float v[]. //… } int n. int b.

Fie o funcţie care efectuează o prelucrare asupra unui vector. numele unei funcţii este un pointer care indică adresa de memorie unde începe codul executabil al funcţiei. unde nume_var este o variabilă de tip procedural şi are tipul „pointer spre funcţie cu parametrii lista_parametri_formali şi care returnează o valoare de tipul tip_rezultat”. Descrierea funcţiei care utilizează parametrul procedural: void f(…. nume_functie. …). … } unde nume este parametrul formal de tip procedural. Declararea unei variabile de tip procedural (pointer spre funcţie): tip_rezultat (*nume_var)(lista_parametri_formali). Teste de autoevaluare 5. …) { tip_rezultat x. Pot exista mai multe funcţii care descriu prelucrări diferite asupra unui vector şi oricare din ele poate fi transmisă ca parametru. b. general valabile într-o aplicaţie şi care se modifică relativ rar (de exemplu calea către fişierul de date cu care se lucrează).3. aceasta fiind descrisă de o altă funcţie. Aceasta permite transmiterea funcţiilor ca parametri în subprograme precum şi lucrul cu tabele de funcţii. Apelul funcţiei cu parametri procedurali: tip_rezultat nume_functie(lista_parametrilor_formali) { … } void main() { … f(…. În acest scop trebuie parcurse următoarele etape: a. Nu se cunoaşte apriori tipul prelucrării. … x=(*nume)(lista_parametrilor_actuali).22 Algoritmi în programare Datorită lipsei controlului asupra modificărilor. Cum se realizează fizic transferul unui parametru prin valoare. acest mod de transmitere a datelor nu este recomandat. Pointeri spre funcţii În limbajul C. . c. } Exemplu 12. Lui nume_var i se poate atribui ca valoare doar numele unei funcţii de prototip corespunzător acestui tip: tip_rezultat nume_f(lista_parametrilor_formali). tip_rezultat (*nume)(lista_parametrilor_formali). respectiv prin adresă? Care este efectul asupra proiectării subprogramelor? 2. primită ca parametru. Se prefera utilizarea acestei metode doar atunci cînd este vorba de valori comune.

h> #include<math. Să se aproximeze soluţia unei ecuaţii de forma f(x)=0 prin metoda bisecţiei.2f\n".i++) {printf("a(%d)=". Pointerul poate fi folosit ulterior pentru apelul funcţiei sau transmis ca parametru real în apelul unui subprogram care conţine. functie(tab.i<m. m/=n. } Apelul se realizează prin transmiterea ca parametru real a funcţiei potrivite prelucrării dorite. void main() { float tab[10]. functie(tab. suma)). return(s). int m. return(m).dim)). #include<stdio. în lista parametrilor formali. Următoarea declaraţie defineşte pointer_f ca „pointer spre funcţia cu rezultatul tip_returnat şi parametrii parametri”. i++) m+=v[i]. suma)). } printf("Se calculeaza suma elementelor…\n"). cu sintaxa de forma tip_returnat *pointer_f([parametri]) Adresa unei funcţii se obţine prin simpla specificare a identificatorului acesteia (fără specificarea parametrilor sau parantezelor) şi poate fi atribuită unui pointer spre funcţie cu rezultat şi parametri compatibili.h> .2f\n". float m=0. i<n.h> #include<conio. } Limbajul C permite lucrul cu variabile de tip pointer. i++) s+=v[i]. } float media(float *v.float(* prelucrare)(float*. int n) { for(int i=0. } float functie( float vec[]. scanf("%d ". printf("Numarul de elemente(<10): "). i<n. m. float s=0. Exemplu 13.&tab[i]). printf("Rezultatul prelucrarii este: %5. un pointer la un prototip de funcţie compatibilă. return. &m). tip_returnat (*pointer_f)([parametri]) Observaţie: Nu trebuie să se confunde un pointer la o funcţie cu o funcţie care are ca rezultat un pointer. for(i=0. int)) { return (*prelucrare)(vec. int n) { for(int i=0.int dim.i).* material didactic pentru ID * 23 float suma(float *v. Aceste variabile permit transferul adresei funcţiei asociate ca parametru. scanf("%f". m. printf("Rezultatul prelucrarii este: %5. care conţin adresa de început a unei funcţii (a codului său executabil). printf("Se calculeaza media elementelor…\n"). precum şi apelul funcţiei prin intermediul pointerului său.i.

&eps). cu argumente de tip void *.long. } *cod=gata. if(!cod) printf("\nNu se poate calcula solutia aproximativa"). bisectie(a.&x).int *. int cod.eps.eps. asigurîndu-se astfel posibilitatea realizării operaţiei de schimbare a tipului („cast”) în funcţie de necesităţile ulterioare (la momentul apelului se poate realiza modificarea tipului void* în tip_element*. scanf("%f%f". caracter). int *cod. cît şi descrierea efectivă a relaţiei de ordine. } /*functia ce implementeaza metoda bisectiei*/ void bisectie(float a. for(c=0.b. dar pe care se poate defini o relaţie de ordine (de exemplu numeric.float(*f)(float). unde tip_element reprezintă tipul elementelor vectorului de sortat). } Exemplu 14. Metoda aleasă spre exemplificare este sortarea prin selecţie directă.float *). dimensiunea vectorului de sortat şi numărul de octeţi din reprezentarea tipului elementelor vectorului.(c<n)&&!gata.c++) { *x=(a+b)/2. care să permită la apel atît schimbarea de tip.float *x) { int gata=0. else printf("\n Solutia aproximativa este: %f". pointerul la o funcţie de comparare. functie=fct. printf("\nNumarul maxim de iteratii:").&b). long c. Să se sorteze un şir cu elemente de un tip neprecizat. ca pointer la tipul void.functie. /* functia principala*/ void main() { float a.x.long n. long n.b. gata=fabs(*x-a)<eps.float b. scanf("%f". else a=*x.n. Un subprogram de sortare care să nu depindă de tipul elementelor şi de criteriul de sortare considerat trebuie să aibă ca parametri formali: vectorul de sortat. } /*descrierea functiei pentru care se aplica metoda bisectiei*/ float fct(float x) { return x*x*x-3*x+14.float (*f)(float). if ((*f)(*x)*(*f)(a)<0) b=*x.float. float (*functie)(float). printf("Introduceti capetele intervalului:").&cod. clrscr().float eps. . şir de caractere.&n). printf("\nEroarea admisa:").x).&a.float. scanf("%li".24 Algoritmi în programare /*prototipul functiei bisectie*/ void bisectie(float. /*prototipul functiei pentru care se aplica metoda bisectiei*/ float fct(float).

j++) if((*compara)((char*)v+dim*i. int n. de la o adresă sursă la una destinaţie. const void *sursă. } Exemplu de apel pentru un vector de numere reale: #include <stdio.i++) for(j=i+1. printf("Dimensiunea vectorului:"). for(i=0.h> #include<conio.i. (char *)b)>0)return 1. memmove((char*)v+dim*i. } void main() { float vect[20].j.j<n.dim). } .h.&n). Fişierul sursă care conţine funcţia de sortare descrisă anterior este următorul: //fisier exp_tip. for(i=0. int dim. aux=malloc(dim). scanf("%d".(char*)v+dim*j.const void * )) { int i.h> #include <string.dim). int (*compara)(const void * .i<n. printf("\nElementele:").(char*)v+dim*j)) { memmove(aux.i<n.h> #include "exp_tip. O astfel de funcţie există în biblioteca mem.h> #include<conio.&vect[i]). sort(vect. } free(aux). operaţia de atribuire nu poate fi folosită.cpp #include <mem.dim).i<n-1. else return 0.i++) scanf("%f".n. memmove((char*)v+dim*j. } Exemplu de apel pentru un vector de cuvinte (şiruri de caractere): #include <stdio.aux. const void *b) { if(strcmp((char *)a. printf("\nElementele sortate:"). ea fiind înlocuită de o funcţie de copiere a unui număr prestabilit de octeţi.h> include<alloc. const void *b) { if(*(float *)a>*(float *)b)return 1.i++) printf("\n%f".compara). unsigned n) Pentru accesarea adresei elementului de rang i din vector se foloseşte formula (char *)v+i*nr_octeti. else return 0.(char*)v+dim*i.vect[i]).* material didactic pentru ID * 25 Deoarece tipul elementelor vectorului nu este cunoscut la momentul descrierii procedurii de sortare. void *aux. clrscr().h> void sort(void *v. sintaxa ei fiind: void *memmove(void *destinaţie. for(i=0.sizeof(float). int n.h> #include "exp_tip.cpp" int compara(const void *a. getch().cpp" int compara(const void *a.

i<n. int n. declarate în biblioteca stdarg.vect[i]). Să se modifice exemplul de mai sus privind rezolvarea unei ecuaţii prin metoda bisecţiei astfel încît să se detecteze şi situaţia găsirii soluţiei exacte a ecuaţiei. sort(vect. cuvant vect[20].h declară tipul va_list şi funcţiile va_start.i++) scanf("%s". care va permite adresarea parametrilor. scanf("%d". care permit accesul la lista de parametri. unde tip_element este tipul elementului transferat din listă.compara). în care să testaţi subprogramul. În funcţia utilizator corespunzătoare trebuie declarată o variabilă (numită în continuare ptlist) de acest tip. Limbajul C permite definirea funcţiilor utilizator cu număr variabil de parametri.26 Algoritmi în programare void main() { typedef char cuvant[10]. getch(). în care: va_list este un pointer către lista de parametri. scrieţi un subprogram care implementează metoda tangentei pentru rezolvarea unei ecuaţii. variabila ptlist este modificată astfel încît să indice următorul parametru. printf("\nCuvintele sortate:"). for(int i=0. Prototipul funcţiei este: void va_end(va_list ptlist). În unele situaţii (vezi exemplele) se transferă în acest parametru numărul de variabile trimise. va_arg întoarce valoarea parametrului următor din sublista variabilă. Fişierul stdarg. 2. unde ultim reprezintă numele ultimului parametru din sublista variabilă. Funcţii cu număr variabil de parametri Bibliotecile limbajului C conţin subprograme standard cu număr variabil de parametri. for(i=0. va_start iniţializează variabila ptlist cu adresa primului parametru din sublista variabilă.i<n.h.&n). va_arg şi va_end.10. .4. ultim).n. tip_element).i++) printf("\n%s". printf("Dimensiunea vectorului de cuvinte:"). Prototipul acestei funcţii este: void va_start(va_list ptlist. printf("\nCuvintele:"). Prototipul acestei funcţii este: tip_element va_arg(va_list ptlist. Folosind exemplul implementării metodei bisecţiei. } Teste de autoevaluare 6. 7. Scrieţi şi un program apelator. va_end încheie operaţia de extragere a valorilor parametrilor şi trebuie apelată înainte de revenirea din funcţie. prin utilizarea unui set de macrodefiniţii. După fiecare apel al funcţiei va_arg.&vect[i]). clrscr().

r.h> #include<stdarg. Funcţiei inter_var i se transmit la apel vectorul rezultat.int.. în care în lista de parametri a funcţiei cu număr oarecare de parametri figurau elemente de acelaşi tip (int).i++) { //extragerea urmatorului element din lista de parametri y=va_arg(ptlist. void inter_var(double *. printf("\nCmmdc al primelor 3 numere:%d\n".&x.&y. /*initializarea lui ptlist cu adresa de va_start(ptlist.i=y.&z.cmmdc_var(4.&w).z.. do{ r=d%i. clrscr(). int cmmdc(int. scanf("%d%d%d%d"..w.h> void inter(double *. int). acest exemplu ilustrează modul de transfer şi acces la elemente de tipuri diferite. Să se calculeze cel mai mare divizor comun al unui număr oarecare de numere întregi.. Spre deosebire de exemplul anterior. return x.int y) { int d=x. } Exemplu 16.cmmdc_var(3.int). adresa de început (pointer la tipul double) şi numărul de elemente (int). d=i. } while(r).y.nr).h> #include<conio. } //cel mai mare divizor comun a doua numere int cmmdc_var(int nr.y. return d.i=r.i<nr.h> #include<conio.) //cel mai mare divizor comun a nr numere { va_list ptlist.y).int.int). inceput a listei de parametri*/ //extragerea primului parametru. #include<stdio.). #include<stdarg.* material didactic pentru ID * 27 Problema numărului de parametri şi tipurilor lor este tratată de programator.h> #include<stdio. z=cmmdc(x. Să se interclaseze un număr oarecare de vectori. caz 2 x numărul de vectori de interclasat.. // functie cu numar variabil de parametri void main() { int x.y.z. .x. for(int i=1.z)). Exemplu 15.. } int cmmdc(int x. în acest.w)).int nr..h> int cmmdc_var(int.).. printf("\nCmmdc al tuturor numerelor:%d\n".x=z.x. de tip int x=va_arg(ptlist. Numărul parametrilor din lista variabilă este.. } va_end(ptlist).double *. iar pentru fiecare vector de interclasat.double *).

double *z) { int i.z[k++]=y[j++]). for(i=0.i++) scanf("%lf". double *y.x1. //extragerea numarului sau de elemente n2=va_arg(ptlist.n2). clrscr().x2[10].i<(int)(nr/2).) { va_list ptlist.x2. for(int j=0.i++) { //extragerea urmatorului vector y=va_arg(ptlist.j.x2. împreună cu dimensiunile masivului rezultat şi un cod de eroare (nu orice şir de matrice se pot înmulţi).double *). for(i=0.n3.j++) x1[j]=z[j].i<n1. Funcţia primeşte ca para- . //extragerea dimensiunii lui n1=va_arg(ptlist.n4.n2. } void inter_var(double *z.n1. for(j=0.double *).z[i]).. } void inter(double *x.nr). for(i=0.x4.z).&n3.j<n1..k=0.n1+=n2. if(i<n1) for(. for(int i=1. scanf("%d%d%d%d".i++) scanf("%lf". for(i=0. } va_end(ptlist).. int n2. double *x. int n1. int n1.&n2.k.y.i<n3. inter_var(z. Funcţia trebuie să aloce spaţiu de memorie pentru masivul rezultat şi să trimită către apelator adresa acestui spaţiu.j++) x1[j]=x[j].n3. for(i=0. double x1[10].i++) printf("%lf ".&n4). printf("\nRezultatul interclasarii primilor 2 vectori\n").i<n1.i++) scanf("%lf". Să se scrie o funcţie cu număr variabil de parametri care calculează produsul unui şir de maxim n matrice.n2.i++) scanf("%lf".*y. for(i=0.&x4[i]). inter(x1.x4[10].z[k++]=x[i++]). //extragerea primului vector x=va_arg(ptlist. printf("\nRezultatul interclasarii celor 4 vectori\n").4. else z[k]=y[j++].n2.8. /*initializarea lui ptlist cu adresa de inceput a listei de parametri*/ va_start(ptlist.j=0.n4).int).i<n2.i++) printf("%lf ".&x2[i]).(i<n1)&&(j<n2).x3[10].j<n2.&x1[i]).j<n1+n2.28 Algoritmi în programare void main() { int n1. for(int i=0.n2.x3. inter_var(z.int).int nr.x1[100].i<n4.i<n1+n2+n3+n4.x1.z[50].i<n1+n2.k++) if(x[i]<y[j]) z[k]=x[i++]. else for(.n1. } Teste de autoevaluare 8.&n1.&x3[i]).z[i]).n1.

} .k-1). unde n . Dacă evaluările comb(n-1. chiar dacă numărul C n este relativ mic şi poate fi reprezentat k 18. g funcţii.k) funcţia care calculează C n . De asemenea. rezultă că apelul comb(n. n! = ⎨ ⎩n(n − 1)! . altfel presupune un nou apel al aceleiaşi funcţii pentru valoarea argumentului decrementată.k)+comb(n-1. return comb(n-1. } Exemplu n! pentru calculul combinărilor ( n . nr. atunci evaluarea corespunzătoare apelului comb(n. Dacă n ≥ 1. cu fact(0)=1. Pe baza relaţiei de recurenţă C n = C n −1 + C n −11 . coloane. if ((k==0)||(k=n)) return1. C n . Fie comb(n. dacă n ≥ k ≥ 1. pentru n ≥ 13 nu poate fi reprezentat în calcuk lator ca dată de un tip întreg.k) va determina o secvenţă de apeluri ale aceleiaşi funcţii pentru valori ale argumentelor din ce în ce mai mici. n = 0 .0)=1. g : R → R. nr. Calcul recursiv Recursivitatea este tehnica de programare în care un subprogram se autoapelează. Calculul valorii n! pentru n dat poate fi efectuat pe baza formulei recursive ⎧1. funcţia fact este long fact(unsigned n) { if (!n) return 1. Exemplu 17. k-1) sînt realizate în acelaşi mod. evaluarea lui fact(n) rezultă prin multiplicarea cu n a valorii calculate de apelul fact(n-1). coloane … .k)=1. este ilustrată maniera în care sînt efectuate apelurile recursive şi tratarea condiţiilor de terminare. În limbajul C. f o g o f . comb(k. Cazurile în care este posibilă evaluarea „imediată” se numesc condiţii de terminare. 2. Conform relaţiei de recurenţă. adresă matrice.5. k ∈ N şi f.0)=1. pînă cînd este îndeplinită una din condiţiile terminale comb(n. Utilizarea formulei C n = k k k− k prin intermediul unui tip întreg. nr.k) şi comb(n-1.k) revine la însumarea rezultatelor obţinute prin apelurile comb(n-1. n > 0 Fie fact(n) funcţia C care calculează n!. nr. În continuare sînt prezentate cîteva exemple simple de subprograme C prin intermedik ul cărora sînt calculate recursiv valorile n! . k Soluţia recursivă a evaluării C n este: long comb(unsigned n. unsigned k) { if (k>n) return 0. f . in ordinea: adresă matrice.* material didactic pentru ID * 29 metri numărul de matrice care participă la operaţie. return n*fact(n-1). valoarea C n k poate fi calculată astfel. adresele şi dimensiunile lor. k-1). Cu alte cuvinte.k) şi comb(n-1. linii. linii. k ∈ N date) k ! (n − k )! este ineficientă şi uneori imposibilă deoarece n!. Limbajul C face parte din clasa limbajelor de programare care admit scrierea de funcţii recursive. n ≥ 0. apelul funcţiei fact(n) realizează calculul „imediat” dacă n=0. unde comb(n.

în caz contrar este iniţiat calculul pentru noile valori ale parametrilor.30 Algoritmi în programare k Funcţiile C recursive pentru calculul n! . 3 Fact=3*Fact(2) Fact=2*Fact(1) 3 (o) Fact=3*Fact(2) (o) 2 1 Fact=1 2 2 Fact=2 3 Fact=3*Fact(2) 1 Fact=1*Fact(0) 2 Fact=2*Fact(1) 0 Fact=2*Fact(1) Fact=1 1 Fact=1*Fact(0) 2 Fact=2*Fact(1) 3 Fact=3*Fact(2) (o) 3 Fact=6 3 Fact=3*Fact(2) (o) 3 (o) Fact=3*Fact(2) (o) Fig.b. Fiecare apel determină introducerea în stivă a valorilor parametrilor reali. în cazul apelului fact(3). C n . În cazul subprogramelor recursive. 2. fact(1)=1*fact(0)=1. unde (○) reprezintă adresa de revenire în punctul de unde a fost efectuat apelul fact(3). eliberîndu-se spaţiul ocupat. fact(0). k ∈ N realizează apeluri recursive directe.1. La momentul execuţiei.1. fact(2)=2*fact(1)=2.b Eliberarea stivei după execuţia determinată de condiţia de terminare Fig. unde n . De exemplu.1. Evoluţia determinată de apelul fact(3) în stivă este ilustrată în figurile 2. dacă este îndeplinită o condiţie terminală. aceste informaţii sînt extrase cu eliminare din stivă. secvenţa de apeluri recursive este: fact(2). mecanismul funcţionează astfel: este generat un număr de apeluri succesive cu ocuparea spaţiului din stivă necesar efectuării apelurilor pînă la îndeplinirea unei condiţii de terminare. fact(1). Schema unui apel recursiv poate fi descrisă astfel: se verifică dacă este îndeplinită cel puţin una din condiţiile terminale. 2. atunci calculul este încheiat şi controlul este returnat unităţii apelante. iar operaţia de inserare în stivă poate produce depăşirea spaţiul de memorie rezervat. a adresei de revenire şi a variabilelor locale.1. fact(3)=3*fact(2)=6. Mecanismul prin care este efectuat apelul unui subprogram se bazează pe utilizarea stivei memoriei calculatorului. apelurile sînt executate în ordinea inversă celei în care au fost generate.a şi 2. calcul care presupune unul sau mai multe apeluri recursive.a Evoluţia în stivă pînă la verificarea condiţiei de terminare n=0 . În continuare execuţia determină fact(0)=1.

x > 8 ⎩ ⎩ funcţiile C pentru calculul h=f◦g◦f pot fi descrise astfel: float f(float x) { if (x<5) return 2*pow(x. unsigned n) { if (!m) return n+1. g (x ) = ⎨ 3 ⎪x − x + 5. } float h(float x) { return f(g(f(x))). g definite prin ⎧2 x 3 + 1. Funcţia Ackermann este definită pentru argumentele m. scanf(“%s“.cuvant). Exemple de aplicaţii cu subprograme recursive Exemplu 20.n numere naturale prin ⎧n + 1. unde f. Pentru rezolvarea problemei se utilizează funcţia recursivă Scrie. a2. } 2.. altfel ⎩ Funcţia C Ackermann calculează valoarea funcţiei a pentru m. în sensul că este efectuat un apel al unui alt subprogram S2 şi S2 iniţiază un apel al lui S1 (recursivitate mutuală). return pow(x.3)+1. x ≤ 1 f (x ) = ⎨ x + 2 . n − 1)).1). n = 0 ⎪a (m − 1. } } Exemplu 21. if (strcmp(cuvant. să se calculeze valorie funcţiei h=f◦g◦f . 5 ≤ x < 8 . m = 0 ⎪ a(m . return Ackermann(m-1.&cuvant). De exemplu. printf( “%s\n“. Exemplu 19. n ) = ⎨a (m − 1..“#“)) { Scrie. } float g(float x) { if (x<=1) return 5*x*x-3*x+2. n parametri naturali daţi. if (x<8) return pow(x. void Scrie() { char cuvant[100]. Calculul valorii funcţiei Ackermann.4)+2.g:R→R sînt funcţii date.6. x < 5 ⎧ 2 ⎪ 4 ⎪5 x − 3 x + 2 . return 3. long Ackermann(unsigned m. ….Ackermann(m. a (m .n-1)). Să se scrie o funcţie C care citeşte o secvenţă oarecare de cuvinte a1.* material didactic pentru ID * 31 Apelurile recursive ale unui subprogram S1 pot fi şi indirecte. if (!n) return Ackermann(m-1. Pentru funcţiile f. x > 1 ⎪3 . } . an terminată cu simbolul # şi afişează anan-1…a1.3)-x+5.1).

long b) { if (a==b) return a. Soluţia recursivă este prezentată în funcţia Hanoi.la fiecare mutare este deplasat unul dintre discurile aflate pe poziţia superioară pe una din tije.c) problema transferului celor n discuri de pe tija a pe tija c. . conform definiţiei următoare. Notînd cu P(n. 3.b − a ). pe tija a sînt plasate n discuri de diametre diferite.unsigned c) { if(n>0) .tija b poate fi folosită pentru deplasări intermediare. atunci discul de diametru maxim care se află încă pe tija a este deplasat pe tija c şi în continuare se rezolvă problema P(n-1.unsigned a.b ). b > a ⎩ Funcţia C cmmdc(a.c). unsigned b.b). Problema poate fi enunţată astfel: fie trei tije a. Tija a 1 2 3 2 3 3 3 Tija b Tija c Mutarea efectuată a⇒c a⇒b c⇒b a⇒c b⇒a b⇒c a⇒c 1 2 1 2 1 2 2 1 3 3 2 3 1 2 3 1 1 #include <stdio.b-a). b. if (a>b) return cmmdc(a-b. Dacă s-a rezolvat problema P(n-1.h> #include <conio. o soluţie a problemei pentru n=3 poate fi descrisă astfel. Se cere ca cele n discuri de pe tija a să fie deplasate pe tija c astfel încît să fie îndeplinite condiţiile: . a > b ⎨ ⎪( a .a. c. . Exemplu presupunînd că discurile sînt numerotate în ordinea crescătoare a diametrelor cu etichetele 1. 2.b.a.b) este long cmmdc(long a.b ) = ⎪( a − b .b). Problema calculului celui mai mare divizor comun dintre două numere naturale a şi b poate fi rezolvată recursiv.oricare din discuri poate fi aşezat numai pe un disc de diametru mai mare. } Exemplu 23. return cmmdc(a. în ordinea descrescătoare a acestora (de jos în sus). Problema turnurilor din Hanoi ilustrează foarte bine avantajele recursivităţii. a = b (a . pentru rezolvarea ei putem raţiona în modul următor.32 Algoritmi în programare Exemplu 22.h> void Hanoi(unsigned n. ⎧a .

a). k = 2 . printf("Transfer disc de pe tija %u pe tija %u\n". clrscr().d. Hanoi(n-1.int n) { if(n) { inssort(v.n-1).a.&n).1. Fiecare problemă intermediară P(k).. Funcţia insera realizează inserarea valorii x în vectorul v în poziţia „corectă”. n este rezolvată aplicînd aceeaşi metodă P(1) este o problemă „gata rezolvată” (condiţie terminală).int *n. atunci: H1 rezultă din patru exemple (copii. void insera(float *v.a.b).int m=n-1.&m. i ≥ 0. clone) de primitive din H0 unite prin segmente de lungime h.b. dacă mulţimea de primitive H0 constă dintr-un punct şi pentru compunere este considerat un segment de lungime h.3).a.float x) { for(int i=0. a2. De asemenea. punctele izolate fiind unite prin segmente de lungime hn=h/2n. Hanoi(n. atunci soluţia problemei P(n) rezultă din soluţia problemei P(n-1) prin inserarea lui an în soluţia problemei P(n-1). Curbele rezultate se numesc curbele Hilbert Hi. } } Exemplu 25.b. } Exemplu 24. } } void main() { unsigned n.2.b).a. Sortarea crescătoare prin inserare Pentru sortarea crescătoare a unei secvenţe de numere reale se poate raţiona astfel: dacă P(n) este problema sortării crescătoare a secvenţei a1. printf("n="). …. o curbă Hn rezultă din patru copii ale unei curbe Hn-1.c. for(int j=*n. } void inssort(float *v... H1 H2 H3 . H2 rezultă din 16 exemple din H0 unite prin 15 segmente de lungime h/2 ş. Funcţia recursivă inssort realizează sortarea vectorului cu n componente prin inserţie.i++).j>=i+1.* material didactic pentru ID * 33 { Hanoi(n-1.v[n-1]).(*n)++. Compunerea constă în repetarea primitivelor considerate şi a rezultatelor obţinute prin rotirea lor într-un sens sau celălalt..m. Generalizînd. realizări.scanf("%u". v[i]=x.c.getch(). Pot fi realizate desene prin compunerea într-o manieră recursivă a unor figuri geometrice primitivă (de bază). Astfel. H2 se poate obţine prin interconectarea a patru copii ale lui H1 rotite cu unghiuri drepte şi prin interconectarea punctelor izolate prin segmente de aceeaşi lungime.j--)v[j]=v[j-1]. insera(v. an şi P(n-1) este problema sortării primelor n-1 componente. instanţe.c.(i<*n)&&(x>v[i]).

B(int).lineto(x. A(i-1).lineto(x.y). C(i-1).y0=x0=h/2. D(i-1).y). A: D ← A↓ A→ B B: C↑B→B↓ A A: D ← A↓ A→ B C: B→C ↑C ← D D: A↓ D ← D↑C Prin executarea următoarei surse C sînt obţinute curbele Hilbert H4 (exemplul a fost scris în mediul Borland C 3. Curba Hilbert obţinută este Exemplu 26.lineto(x. do{ i++. C(i-1). A(i-1). n ≥ 1.y+=h.lineto(x. } } void D(int i) { if (i>0) { A(i-1).lineto(x.11). C.x-=h."D:\BC\BGI"). D şi se reprezintă prin săgeţi rutinele care desenează segmentele care le interconectează. x0+=h/2. setcolor(4). B(i-1). getch().y).x0. initgraph(&gd. toate unghiurile determinate de segmentele care unesc punctele sînt de măsură 900.h> <graphics. setbkcolor(0).h> <stdlib. } void A(int i) { if (i>0) { D(i-1).y+=h. h=h0. B(i-1). closegraph(). int h. } } void B(int i) { if (i>0) { C(i-1).y).y-=h.y+=h. C(i-1). D(i-1).x+=h. } } void C(int i) { if (i>0) { B(i-1). const h0=480. #include #include #include #include <stdio.y).h> <conio. atunci rezultă următoarele scheme recursive.moveto(x. int x.y). D(int).h> B(i-1).lineto(x. int gd=DETECT.y=y0. void void void void A(int). Curba lui Sierpinski pentru n=2 este următoarea: .gm.y-=h. x=x0.y).34 Algoritmi în programare Dacă cele patru părţi ale unei curbe Hilbert Hk sînt notate A.x+=h. void main() { clrscr().y.y).y).lineto(x.h/=2.x-=h.x-=h.lineto(x.x+=h. B.lineto(x. Dacă se consideră ca valori pentru măsurile unghiurilor determinate de aceste segmente 450.y). 900.y). rezultă curbele Sierpinski Sn. int i=0.lineto(x. D(i-1). A(i-1). C(int).y0. În cazul curbelor Hilbert.y). A(i).&gm.y-=h.y0+=h/2. } } const n=5. } while(i<n).lineto(x.lineto(x.y). 1350.

h/=2.y-=h. setcolor(8).C(i-1). x=x0.y0.lineto(x.y). C(i).x+=h.y). int gd=DETECT. } } void D(int i) { if (i>0) { D(i-1). B(i-1). lineto(x. C(i-1). lineto(x.y-=h. D(int).y-=h. setbkcolor(15).y). B(i-1).x-=2*h.y+=h. do{ i++. lineto(x.gm. closegraph(). } const n=4. B(i).y). A(i). y-=2*h. S: A B C D A: A B ⇒ D A B: B C ⇓ A B C C: C D ⇐ B D: D A ⇑ C D unde săgeţile duble indică segmente de lungime 2h.y).y). moveto(x.y).y).y).y-=h.x+=h. D(i-1). lineto(x. .h> <conio.11) #include #include #include #include <stdio.y). int i=0.} while(i!=n). } } void C(int i) { if (i>0) { C(i-1).lineto(x.y). B(i-1). y0=3*h. void void void void A(int). int x. C(int).y=y0.h> <graphics.h> } void B(int i) { if (i>0) { B(i-1). lineto(x.y). C(i-1).lineto(x. h=h0/4.x+=h. x0-=h. lineto(x. D(i-1).x+=h.y).y). D(i). lineto(x.y+=h.x-=h.y.y+=h. A(i-1). A(i-1).y+=2*h. } } lineto(x.x-=h.y+=h.y+=h.y+=h.y).&gm. lineto(x.lineto(x. const h0=412. void main() { clrscr().x+=h.y0+=h.* material didactic pentru ID * 35 Recursia pentru obţinerea curbelor Sierpinski poate fi descrisă astfel.y-=h.x+=h.x-=h.h> <stdlib. initgraph(&gd. Următorul program desenează curbele Sierpinski S4 (exemplul a fost scris în mediul Borland C 3. int h. lineto(x."d:\bc\bgi").y-=h. B(int). lineto(x. x0=2*h.x-=h. D(i-1).x-=h.x+=2*h.y). lineto(x.y). Rezultatul execuţiei programului este prezentat în următoarea figură. A(i-1). getch().x0.x-=h. } void A(int i) { if (i>0) { A(i-1).

11. Pe lîngă rezultatele (oricîte) care pot fi întoarse prin parametrii.i<n. c=(float **)malloc(n*sizeof(float *)). 12.j++) for(k=0.c[i][j]=0.h> #include<conio. 13. Să se scrie un subprogram recursiv pentru determinarea elementului minim şi a elementului maxim dintr-un vector. 10. return c.int n) { float **c.float **b. subprogramele de tip funcţie permit şi întoarcerea unui rezultat de tip simplu prin numele lor. 3.36 Algoritmi în programare Teste de autoevaluare 9. În limbajul C se pot scrie numai subprograme de tip funcţie. Scrieţi un subprogram pentru rezolvarea problemei căutării binare în vectori sortaţi. } float** putere(float **a. for(i=0.**ap. for(i=0. ap=(float **)malloc(n*sizeof(float *)).i. for(i=0.j<n.int n) { int i.h> #include<alloc.int p.k.i++) for(j=0. astfel încît v[poz]=k.c[i][j]+=a[i][k]*b[k++][j]). Să se identifice (dacă există) o valoare poz. Fie v un vector de numere reale sortat crescător şi k un număr real dat. Să se scrie un subprogram recursiv pentru calcularea sumei elementelor unui vector. #include<stdio. Răspunsuri şi comentarii la testele de autoevaluare 1.i++) .i++) *(c+i)=(float *)malloc(n*sizeof(float)).int l. 2. Să se scrie un subprogram recursiv pentru determinarea elementului minim dintr-un vector. ceea ce ofere posibilitatea realizării apelului ca operand într-o expresie.i<n. care va folosi rezultatul apelului pentru evaluare.j.i<n.k<n.h> float** inmultire(float **a. Să se scrie un subprogram recursiv pentru determinarea elementelor şirului lui Fibonacci.m. float **c.

float **a. printf("Dimensiunea vectorului:").p.k). return cauta_binar(v.mij+1.* material didactic pentru ID * 37 *(ap+i)=(float *)malloc(n*sizeof(float)).i++) scanf("%f".j<n.l++) for(m=0.j<n.p. for(i=0.n-1.int ls.m. } scanf("%i".&n).ap[l][m]=c[l][m].i++) for(j=0. getch().j++) printf("%f ". void main() { clrscr(). if(c==-1) printf("Cheia nu a fost gasita"). scanf("%i".l.float k) { if(li>ls) return -1.&p). printf("\n").n.l++) for(m=0. } void main() { int i. scanf("%f".0.k). for(l=0.ap[l][m]=a[l][m].&f).n). *(*(a+i)+j)=f.&n).*((*(ap+i)+j))).li.ap.l<n.j.l<n. } 13. int c=cauta_binar(v.float). if(v[mij]>k) return cauta_binar(v.i<n.m<n.i++) { for(j=0.f. printf("\n n=").h> int cauta_binar(float *.int. int n. printf("Elementele vectorului\n"). for(i=0.m++). a=(float **)malloc(n*sizeof(float *)). if(v[mij]==k) return mij.i<n. for(l=0. #include <stdio.ls.&k).h> #include <conio. } getch(). for(i=0.n).i<n.k). } int cauta_binar(float *v.i++) *(a+i)=(float *)malloc(n*sizeof(float)).&v[i]). clrscr(). ap=putere(a.i<n. for(i=0. for(unsigned i=0.mij-1.int.int li. float k. int mij=(li+ls)/2.m<n.i++) { c=inmultire(a. printf("Cheia de cautare:"). scanf("%i". else printf("Cheia pe pozitia:%i".j++) { scanf("%f ".**ap. } return ap.m++).i<p-1.c). float v[100]. } .

2. Ed. M. vol. 1998 5. Inforec.2. 2007 2. Ghilic-Micu. II. Ştiinţa învăţării unui limbaj de programare. Ed. 1994 4. După încheierea studierii acestei unităţi de învăţare. Roşca. A.3. ASE Bucureşti. Cocianu. Ed. M. I.Limbajele C şi C++ pentru începători. Teorie şi aplicaţii.2. studenţii au cunoştinţele şi abilităţile necesare lucrului cu subprograme în vederea rezolvării problemelor complexe de programare prin rezolvarea separată a subproblemelor componente. B.1. Exerciţii rezolvate şi propuse. C. Gh. ASE Bucureşti. Gh. subbrograme recursive.Programarea calculatoarelor. funcţii cu număr variabil de parametri. C. Bucureşti. transferul datelor între apelator şi apelat.Programarea calculatoarelor. 1996 (paragrafele 2. Uscatu .Metode numerice şi programe Pascal. Stoica.2.2. I. în general şi în limbajul C. prin variabile globale şi prin parametri (prin valoare şi prin simularea transferului prin adresă).2) .2. ASE Bucureşti. Iorgulescu . 2003 3. Liviu Negrescu . 2. I. 4. B. I. Mircea . Stoica. Ed. C. Algoritmi în programare. Roşca & colectiv . Ghilic-Micu. M.1. 4.2. Cluj-Napoca. Cocianu.38 Algoritmi în programare Rezumat În cadrul acestei unităţi de învăţare au fost studiate următoarele aspecte ale programării calculatoarelor cu privire la lucrul cu subprograme: cunoştinţe teoretice despre subprograme. Roşca. pointeri la funcţii şi trimiterea funcţiilor ca parametri către alte funcţii. Microinformatica.Gh. C.Bazele elaborării programelor. Ed. construcţia şi apelul subprogramelor în limbajul C. Uscatu. Bibliografia unităţii de învăţare 1.

.................................................. Fişierul şi articolul ....... utilizarea articolelor interne pentru prelucrarea fişierelor de date...1)..........2............51 3.............................1....................62 Rezumat....... ale cărui noduri sînt asociate componentelor structurii..8.......8...................... Componentele de pe ultimul nivel sînt scalare şi se numesc date elementare sau cîmpuri................................ Nivelul inferior de prelucrare a fişierelor .........41 3................. 3....8....................................5......... Structura sistemului de fişiere sub MS-DOS/Windows .......... Articole cu structuri complexe ........39 3..........................6....... Operaţii de prelucrare a fişierelor ..........................* material didactic pentru ID * 39 3. 3.... ceea ce permite ca această structură să fie construită recursiv..1.................44 3....... datele de grup de pe diverse niveluri au aceleaşi proprietăţi ca şi articolul...................................... studenţii vor avea cunoştinţe teoretice şi abilităţi practice necesare pentru lucrul cu structuri de date interne eterogene şi cu structuri de date externe (fişiere de date)..... denumite date de grup..........45 3..........................................55 Răspunsuri şi comentarii la testele de autoevaluare ...........3... Declararea împreună a tipului articol şi a variabilelor de acest tip se realizează con- .......1............... se vor asimila cunoştinţe şi abilităţi de lucru privind: tipul de dată articol...62 Obiectivele unităţii de învăţare După studierea acestei unităţi de învăţare......... cu acces direct la elementele sale.......... Articolul poate fi reprezentat sub formă de arbore.... metode de organizare a fişierelor şi tipuri de acces la datele conţinute. operaţii generale de prelucrare a fişierelor de date...........................4................ Datele de pe celelalte niveluri..... Concret....................39 Articol: caracteristici generale şi mod de declarare....... între care există o relaţie de ordine ierarhică......................................47 3..........62 Bibliografia unităţii de învăţare ........... 3............................. se constituie prin agregarea datelor de pe nivelurile inferioare....49 3............. Articolul şi fişierul Cuprins Obiectivele unităţii de învăţare ...... prin descompunerea în structuri cu aceleaşi proprietăţi (figura 4..... Referirea articolului şi a elementelor componente......... Nivelul superior de prelucrare a fişierelor ............ Constante de tip articol ...............................46 Metode de organizare a fişierelor şi tipuri de acces ................................................7..................... Conceptual......52 3............................ Data de grup de cel mai înalt nivel (rădăcina arborelui) corespunde articolului în ansamblu................................................................... Articol: caracteristici generale şi mod de declarare Articolul este o structură de date eterogenă......................................... tipurile de fişiere de date........2.................

Dacă lipsesc elementele var1. fără însă a declara şi un tip utilizator nou.b[100]. Cîmpurile unei structuri pot fi variabile simple.i. struct COMPLEX{float r. asemănătoare declaraţiilor de variabile.i. tip_articol este un tip nou de date. Variabilele pot fi declarate şi ca masive.i. unde tip_articol este identificatorul asociat tipului articol.i.}. …. typedef struct {float r. Unele elemente ale declaraţiei pot lipsi (dar nu toate deodată). struct COMPLEX a.b[100]. varn. adăugînd cuvîntul rezervat typedef în faţa declarării (în acest caz nu mai pot fi declarate simultan şi variabile). iar var1.}. utilizabil ulterior la alte declarări. varn. var2. COMPLEX a. Exemple de structuri de articole O variabilă de tip articol poate fi declarată şi ulterior definirii tipului: struct tip_articol var1. DATA ZI LUNA AN a) PERSOANA dată de grup (articol) dată de grup (articol) NUME ADRESA ZI b) DATA NAŞTERII LUNA AN dată de grup (articol) date elementare date elementare Fig.…. Lista cîmpurilor nu poate fi vidă. fiind numai o declarare explicită de tip nou. ale căror elemente sînt de tip articol: var1[dim1][dim2]…[dimn]. struct {float r.} COMPLEX. atunci tip_articol trebuie să fie prezent. var2. a unei variabile simple şi a unui masiv unidimensional cu elemente de acest tip se poate face în oricare din următoarele variante (pentru un număr complex se vor reţine partea reală şi partea imaginară): a) b) c) d) e) f) struct COMPLEX{float r.}.i.1. var2. typedef struct COMPLEX{float r. Este posibilă definirea explicită a unui nou tip de dată.}COMPLEX. Definirea tipului de dată număr complex. .…. var2. Descrierea constituie o definire implicită de un nou tip de dată. varn sînt identificatorii asociaţi variabilelor de tipul articol declarat. atunci trebuie să fie prezentă lista de variabile (nevidă). Dacă lipseşte tip_articol.b[100]. COMPLEX a.b[100].}a.i. caz în care este vorba de o declarare de variabile de tip articol. de forma tip_cimp nume_cimp. COMPLEX a. struct COMPLEX a.b[100]. În continuare. 3.…. iar var1. varn sînt variabile de tipul tip_articol. struct COMPLEX{float r.b[100]. masive sau alte articole.40 Algoritmi în programare form sintaxei: struct tip_articol{lista_cimpuri} var1. Exemplu 1. Lista_cimpuri este o înşiruire de declaraţii de cîmpuri separate prin punct şi virgulă.

Variabilele de tip articol se reprezintă intern ca succesiuni de cîmpuri elementare. Exemplu 2.b[100]. char adresa[50]. În lucrul cu variabilele de tip articol se recomandă declararea identificatorului de tip. } angajat. } data_nasterii. În acest mod. Considerînd declaraţiile anterioare. struct persoana { char nume[30]. arborele se parcurge în preordine (de la rădăcină spre extremităţi şi de la stînga la dreapta).}. identificatorii cîmpurilor din descrierea articolului reprezintă deplasări faţă de începutul acestuia. int an. Pentru exemplele din figura 3. iar sizeof(angajat) are valoarea 90. În procesul de descriere a unui articol. struct tip_data data_nasterii. char luna[3]. char adresa[50]. Din punct de vedere fizic. Referirea pe componente (prin numele lor) este o reflectare a faptului că articolul este . care asigură determinarea lungimii zonei de memorie asociate unei variabile sau unui tip de date. natura.1.* material didactic pentru ID * 41 g) typedef struct COMPLEX{float r. Lungimea zonei de memorie rezervată pentru variabila de tip articol rezultă din însumarea lungimilor cîmpurilor. Aceasta nu poate depăşi 65520 octeţi (ca orice variabilă de tip structurat).2 şi 3. cu reprezentarea internă şi lungimea fizică specifice tipurilor lor. Din punct de vedere practic. } angajat. cu condiţia ca ambele variabile (sursă şi destinaţie) să fie articole de acelaşi tip. expresia sizeof(data_nasterii) are valoarea 8. utilizarea tipului articol este strîns legată de prelucrarea fişierelor. 3. care evidenţiază componentele. astfel: struct tip_data { unsigned zi. Structura arborescentă a articolelor poate fi exprimată sugestiv şi prin machete. int an. Dacă nu ar fi existat declaraţia tipului articol tip_data. identificatorul de tip articol poate fi folosit în definirea mai multor variabile. Adresa fizică a unui cîmp rezultă din însumarea adresei articolului cu deplasarea sa. Exemplu 3. declararea poate fi realizată prin definire recursivă. struct { unsigned zi.i. atunci tipul persoana putea fi scris astfel: struct persoana { char nume[30]. }.3). lungimea declarată şi lungimea fizică ale acestora (figurile 3. Pentru structura unui articol îşi dovedeşte utilitatea operatorul sizeof. COMPLEX a. Referirea globală este permisă numai în operaţia de atribuire.2. Referirea articolului şi a elementelor componente Datele de tip articol pot fi referite în două moduri: global sau pe componente. char luna[3].

respectiv imaginară a variabilei a b[10].adresa. primul caracter din şir.} struct articol pers. Exemplu 4.an=1979. (punct). 3. angajat. Folosind tipul COMPLEX definit anterior. de exemplu.luna=3. regulile specifice acestor structuri.2. asigurarea identificării unice a cîmpurilor se realizează prin asocierea numelui acestora cu numele articolului care le conţine.i .zi. Dacă anumite componente sînt structuri de date de alte tipuri (de exemplu masive sau şiruri de caractere). angajat este identificatorul variabilei articol. În cele ce urmează se are în vedere numai referirea componentelor de tip dată elementară. În lanţul de calificări. Referirea unor componente de tip articol din structura altui articol este posibilă numai în operaţia de atribuire. Cod Magazin Întreg 2 Luna 1 real 4 Vînzări lunare Luna 2 … Real … 4 … Luna 12 real 4 Fig. angajat. Structura de articol pentru exemplul 6 Articolul se declară astfel: .nume.zi=15. a.an În aceste referiri. avem: a.se referă partea reală.r . situate pe ultimul nivel al structurii. numele articolului rădăcină este nume de variabilă. Referirea cîmpurilor unei structuri se face prin calificare. primul calificator fiind numele articolului rădăcină. Construcţiile angajat. strcpy(pers.nume[0].r .2. se scrie: angajat. pers.h> main() { struct articol {char nume[40]. Pta. în referirea elementelor lor se aplică.nume şi angajat. Exemplu 5. Se presupune un articol cu structura din figura 3. zi.luna. luna. Romana 6"). pers.adresa. } În articolele cu structură recursivă se realizează calificarea progresivă cu articolele de pe nivelurile superioare.nume. "Bucuresti.se referă partea reală a celui de-al 11-lea element al vectorului b #include <string. char adresa[30]. …………… strcpy(pers. care sînt şiruri de caractere. Pentru a referi.adresa corespund referirii globale a cîmpurilor respective.42 Algoritmi în programare o structură cu acces direct. pe lîngă calificare. Construcţia rămîne la această formă în cazul în care structura are numai două niveluri: articolul şi cîmpurile elementare ale acestuia.data_nasterii. int an.data_nasterii. celelalte elemente sînt identificatori de cîmpuri. În referirea prin calificare. celelalte fiind nume de cîmpuri ale articolului. "Popescu Ion"). pers. Referirea prin calificare a cîmpurilor articolului angajat de tipul persoana (vezi exemplele anterioare) se realizează astfel: angajat.data_nasterii. Exemplu 6. în condiţiile precizate anterior la referirea globală. folosind operatorul . angajat. angajat.

struct a materii_prime[30].materii_prime[i].…. unsigned char nr_mat. }.norma. #include <stdio... struct { int zi.. Referirea cîmpurilor se realizează astfel: articol.3.} datan. 3. Declararea constantelor presupune definirea anterioară a tipului articol.materii_prime[i].nr_mat. iar referirea cîmpurilor se realizează astfel: articol.}.1. . articol. .. În momentul compilării se rezervă zone de memorie pentru acestea. char adresa[30]. în descrierea articolului se alege valoarea maximă a acestuia: struct a { int cod_mat. Se presupune un articol cu structura din figura 3. //exemplul 3 struct a { int cod_mat. identificarea cîmpului care se iniţializează se face pe niveluri. . Cod produs întreg 2 Număr materii prime întreg 1 Materia primă 1 Cod Norma de consum întreg real 2 4 .cod_mat. } articol.h> void main() { //exemplul 1 struct persoana { char nume[40]. }. float norma. articol. Materia primă 30 Cod Norma de consum întreg real 2 4 Fig. //exemplul 2 struct magazin { int cod_magazin. Exemplu 7.vanzari_lunare[i].. } articol ... Articolul are 50 de octeţi. folosind perechi corespunzătoare de acolade. float vanzari_lunare[12]. }. iar cîmpurile articolelor sînt iniţializate cu valorile precizate de utilizator. float vanzari_lunare[12].. Valoarea iniţială trebuie să fie de acelaşi tip cu cîmpul căruia îi corespunde. Exemple 8.3.cod_magazin articol. articol. struct produs { int cod_produs.11. Structura de articol pentru exemplul 7 Cu toate că numărul de materii prime utilizate poate fi variabil de la un produs la altul. cu i=0. Cînd articolul conţine la rîndul său alt articol. . Constantele de tip articol sînt cu tip şi păstrează caracteristicile acestora.cod_produs. float norma.* material didactic pentru ID * 43 struct magazin { int cod_magazin. an. luna.

4..5.3. //Initializarea articolului din exemplul 2: struct magazin gigel_srl={200.. 2..6. unsigned char nr_mat. conform clasificării tipurilor de date. Declararea tipului reuniune se realizează astfel: union nume_tip { tip_cimp1 cimp1. 3. struct a materii_prime[30].5.z. //sau cu evidentierea structurii de masiv: struct magazin gigel_srl1={200. deoarece zona de memorie rezervată pentru articol este unică. Magheru 14". //sau cu evidentierea structurii data nasterii: struct persoana p1={"Popescu Ion". printf("\n%i"..8.9. Articole cu structuri complexe În activitatea de programare pot fi întîlnite aplicaţii care reclamă utilizarea articolelor cu structură variabilă.11. Magheru 14".4..8. {1. Gestiunea conţinutului respectivei zone de memorie va trebui realizată de către programator. constanta de tip articol se asociază unei singure structuri. } Teste de autoevaluare 1. 1960}}.25.datan.norma). Se presupune un articol cu structura din figura 3. în zona de memorie rezervată articolului nu este memorat decît unul dintre cîmpurile acestuia.12}}. Lungimea zonei de memorie rezervate pentru o variabilă de tip reuniune va fi egală cu maximul dintre lungimile cîmpurilor componente.7.materii_prime[2].44 Algoritmi în programare struct produs { int cod_produs. //Initializarea articolului din exemplul 3 (doar primele 4 materii //prime.4}.gigel_srl1.2f".an).{3251. Nume char[40] Data naşterii zi luna an An de studiu int bursa char zi Forma de învăţămînt id valoare loc de muncă data angajării float char[30] zi lună an Fig.9. Articol cu structură variabilă Declararea şi iniţializarea cîmpurilor unui student la zi pentru structura articolului din . Clasificaţi tipul de date articol. 1960}. Exemplu 9.2}}}.6.. 3.70.11. care se comportă ca şi tipul struct cu o singură diferenţă: la un moment dat al execuţiei programului. tip_cimpn cimpn.4}.12}.. restul de 26 vor fi initializate automat cu valori nule: struct produs z={243.. printf("\n%6.7.2f". tip_cimp2 cimp2.p1.10.vanzari_lunare[10]). "Bucuresti.5. 4. }.. //Initializarea articolului din exemplul 1: struct persoana p={"Popescu Ion".. {2.21}.{51. Pentru acest tip de articol.2. printf("\n%6.7.2.}.3.3. . "Bucuresti.. La iniţializarea cîmpurilor unui astfel de articol..{{2420..8..10.4.{1421. 1..4. limbajul pune la dispoziţia utilizatorilor tipul predefinit reuniune (union).4.

zi. deplasări egale faţă de începutul articolului pentru toate variantele de descriere.1. struct { int zi. } parte_vb. a. dar conţinutul lor nu poate fi modificat pe parcursul programului.valoare).%i. } id. pentru care în momentul compilării se rezervă zone de memorie.forma_inv. a. iar cîmpurile articolelor sînt iniţializate cu valorile precizate de utilizator. Forma de inv. float valoare. Astfel. luna.luna. Constante de tip articol Constantele de tip articol pot fi constante cu tip (variabile iniţializate la compilare) şi constante „obiect”.: %c.datan. Valoarea iniţială trebuie să fie de acelaşi tip cu cîmpul căruia îi corespunde. a.} datan. identificarea cîmpului care se iniţializează se face pe niveluri.parte_vb. Constantele obiect sînt variabile iniţializate la declarare. int an_st. an.} data_ang. } Din punct de vedere fizic.%i.1974} . luna.datan. pentru descrierea din exemplul de mai sus se generează deplasarea 49 faţă de începutul articolului. a. a. printf("\nData nasterii: %i.datan.2f".{4. folosind perechi corespunzătoare de acolade. tip nume_const = {lista_valori}. Constantele cu tip joacă rol de variabile care se iniţializează cu o valoare în faza de compilare. bursa: %6.250. char forma_inv. pentru care se rezervă memorie. }.* material didactic pentru ID * 45 figura 3.4.an. //Initializarea cimpurilor unui student la zi: struct articol a={"Popescu Felix". Teste de autoevaluare 2. Alegeţi tipurile şi dimensiunile potrivite pentru fiecare cîmp. ele putînd să-şi modifice valoarea pe parcursul execuţiei programului. existenţa părţii variabile într-un articol generează. Val. an.5}}. union { struct {char bursa.umană masă lungime lăţime maximă curi nr.h> void main() { //Declararea articolului cu structura variabila: struct articol { char nume[40].1. const tip nume_const = {lista_valori}.4 se realizează astfel: #include <stdio. . cît şi pentru loc_m. roţi animală tip animal nr.} zi.'Z'. struct {int zi.{'D'.zi. la compilare. propulsie viteză nr. struct {char loc_m[30]. Cînd articolul conţine la rîndul său alt articol. animale mecanică tip comconsum cilindree putere bustibil (l/100km) 3. atît pentru cîmpul bursa. lo. Descrieţi în limbajul C tipul de dată vehicul corespunzător următoarei reprezentări tabelare.

46

Algoritmi în programare

Exemplu 10. Constantă cu tip.
#include<stdio.h> void main() { struct persoana { char nume[40]; char adresa[30]; struct { int zi, luna, an;} datan; }; persoana pers={"Popescu Ion", "Bucuresti; Magheru 14", {2, 4, 1960}}; //constanta cu tip pers.datan.zi=4; }

Exemplu 11. Constantă obiect.
#include<stdio.h> void main() { struct persoana { char nume[40]; char adresa[30]; struct {int zi, luna, an;} datan; }; const persoana pers={"Popescu Ion", "Bucuresti; Magheru 14", {2, 4, 1960}}; //constanta obiect // pers.datan.zi=4; genereaza eroare la compilare }

Teste de autoevaluare 3. Iniţializaţi constante cu tip şi constante obiect de tipul vehicul, pe care l-aţi definit la tema de autoevaluare nr. 2. 3.5. Fişierul şi articolul Prelucrarea automată a datelor presupune un sistem de organizare a acestora după metode şi procedee specifice. Organizarea datelor este un proces complex care include identificarea, clasificarea şi descrierea proprietăţilor acestora, gruparea lor în colecţii, reprezentarea pe purtători tehnici, definirea şi realizarea procedurilor de prelucrare etc. Deoarece datele se memorează, de obicei, pe purtători tehnici de informaţii, dar se prelucrează numai cînd sînt prezente în memoria internă, acestea trebuie organizate atît extern cît şi intern. În organizarea externă a datelor se identifică două niveluri de abordare, după cum se are în vedere acest proces din perspectiva utilizatorului sau a purtătorilor fizici externi pe care se înregistrează datele. Cele două niveluri de abordare, numite logic, respectiv fizic, precum şi realizarea trecerii de la unul la celălalt, în condiţiile specifice diverselor sisteme de calcul, se bazează pe o serie de concepte, cum ar fi: fişierul şi articolul, purtătorul tehnic de date, metoda de organizare şi modul de acces, operaţiile de prelucrare etc. Fişierul reprezintă termenul generic care desemnează structurile de date externe. El este o mulţime (colecţie) de date omogene din punct de vedere al semnificaţiei şi al cerinţelor de prelucrare. În purtătorul extern, fişierul are, pe lîngă partea de date, şi alte informaţii de identificare (etichete).

* material didactic pentru ID *

47

Privit din punctul de vedere al prelucrării, un fişier este o colecţie ordonată de date, numite articole. Articolul este constituit dintr-o mulţime ordonată de valori ale unor caracteristici ce aparţin, uzual, unei singure entităţi (obiect, fenomen, proces etc.) din domeniul de activitate abordat. De exemplu, într-un fişier care conţine datele personale ale salariaţilor dintr-o unitate economică, un articol grupează valorile caracteristicilor unei singure persoane. Componentele articolului destinate diverselor caracteristici sînt denumite cîmpuri de date. Depinzînd de natura, ordinul de mărime şi forma de reprezentare externă a valorilor asociate, fiecare cîmp de date are o lungime, exprimată uzual în octeţi. Lungimea unui articol este dată de suma lungimilor cîmpurilor care îl compun. După cum toate articolele dintr-un fişier au sau nu aceeaşi lungime, se face distincţie între fişierele cu articole de lungime fixă sau variabilă. Modul de implementare fizică a celor două tipuri de fişiere diferă de la un sistem la altul şi chiar de la un limbaj la altul. Pe purtătorul fizic extern, partea de date a fişierului se prezintă ca o succesiune de octeţi cu un conţinut binar fără semnificaţie informaţională. În momentul prelucrării, prin descrieri şi operaţii adecvate, din succesiunea memorată extern se „decupează" entităţi (articole, blocuri, linii sau cîmpuri) cu structuri corespunzătoare prelucrării. Tipul entităţii care se „decupează" depinde de tipul fişierului. 3.6. Metode de organizare a fişierelor şi tipuri de acces Principiile şi regulile după care se memorează articolele unui fişier pe purtătorul extern, cu asigurarea protecţiei şi regăsirii acestora, constituie metoda de organizare. În evoluţia organizării datelor externe s-au cristalizat mai multe metode, dintre care, cele mai uzuale sînt secvenţială, relativă şi indexată. Principala diferenţiere între metodele de organizare o reprezintă tipurile de acces admise. Tipul de acces reprezintă modalitatea de regăsire (localizare) a articolelor din fişier. Noţiunea de acces trebuie aplicată atît pentru operaţia de scriere, cît şi pentru cea de citire a datelor. Poziţia din/în care se face citirea/scrierea în cadrul fişierului este indicată de un pointer. Accesul la datele înregistrate pe un purtător tehnic poate fi secvenţial sau direct, în funcţie de modul în care se stabileşte pointerul. Accesul secvenţial este posibil la toţi purtătorii tehnici de date şi presupune înscrierea înregistrărilor în ordinea furnizării lor sau regăsirea în ordinea în care au fost înscrise în suport (figura 3.5).
P(Ak)=f (P(Ak-1)) Traversare

A1

A2

...

Ak-1

Ak

...

An

EOF

Fig. 3.5. Principiul de realizare a accesului secvenţial la articole Pointerul de fişier avansează, în scriere şi citire, de la o entitate (articol, bloc, linie sau cîmp) la alta. Dacă pointerul se exprimă prin deplasare faţă de începutul fişierului, atunci, matematic, acest lucru se poate exprima astfel: P(A1) = 0; P(Ak) = f(P(Ak-1)) = P(Ak-1)+lartk-1; pentru k=2,n;

48

Algoritmi în programare

unde Ak este articolul k şi lartk este lungimea articolului k. O problemă importantă care se pune la consultarea (citirea) în acces secvenţial este controlul ajungerii la sfîrşitul fişierului. După citirea ultimei entităţi (articol, bloc, linie sau cîmp), pointerul indică marcatorul de sfîrşit de fişier - EOF (figura 3.6).
Poziţia pointerului după citirea ultimului articol

A1

A2

...

Ak-1

Ak

...

An

EOF

Fig. 3.6. Pointerul după citirea ultimului articol din fişier În limbajele de programare se regăsesc două modalităţi de sesizare a sfîrşitului de fişier: a) Sesizarea sfîrşitului de fişier în cadrul operaţiei de citire (limbajele FORTRAN, COBOL, C). Sfîrşitul este sesizat la citirea marcatorului de sfîrşit de fişier. Situaţia din figura 5.2 nu este considerată sfîrşit de fişier. Abia la următoarea citire se întîmplă acest lucru (pointerul de fişier avansează după marcatorul de sfîrşit de fişier). b) Sesizarea sfîrşitului de fişier independent de operaţia de citire (limbajele BASIC, PASCAL). În acest caz, dacă pointerul este pe marcatorul de sfîrşit de fişier (după ultimul articol, bloc, linie, cîmp, ca în figura 5.2) se consideră sfîrşit de fişier. Următoarea citire produce eroare de intrare/ieşire (I/E). Proiectarea algoritmilor de prelucrare a fişierelor este determinată de modalitatea în care se sesizează sfîrşitul de fişier. Accesul direct este posibil numai la fişierele care au o anumită organizare, au ca entitate de transfer articolul sau blocul şi sînt memorate pe discuri magnetice. Accesul direct se bazează pe existenţa unui algoritm implementat în sistem care asigură regăsirea (localizarea) articolelor în funcţie de o informaţie de regăsire. Valoarea pointerului este determinată direct, fără să depindă de valoarea sa anterioară: P(Ak)=f(irk), unde Ak este articolul k, iar irk este o informaţie de regăsire a articolului k. În funcţie de algoritmul şi informaţia de regăsire, există două tipuri de acces direct: după cheie şi după numărul relativ al articolului. În cazul accesului direct după cheie, articolul este regăsit prin aplicarea unui algoritm asupra unei informaţii de identificare de tip cheie: P(Ak)=f(cheiek). În cazul accesului direct după numărul relativ - care se mai numeşte, simplu, acces relativ - (figura 3.7), articolul este localizat în fişier prin numărul său, stabilit, în cadrul fişierului, de la valoarea zero: P*(Ak)=(k-1); P(Ak)=P*(Ak)×lart. P*(Ak) reprezintă poziţia exprimată în număr relativ, iar P(Ak) reprezintă poziţia exprimată prin deplasare, în octeţi, faţă de începutul fişierului (la unele sisteme numărul relativ este stabilit de la unu: P*(Ak)=k). La scriere, articolul Ak (numărul relativ k-1) se memorează pe poziţia sa, celelalte k-1 articole anterioare putînd să nu existe (pe suport există însă rezervat loc pentru ele). La citire, articolul Ak (cu numărul relativ k-1, kn) este localizat direct şi conţinutul lui se transferă în memoria internă. Fişierele organizate secvenţial, cu articole de lungime variabilă, admit numai accesul secvenţial. Fişierele organizate secvenţial, cu articole sau blocuri de lungime fixă, admit atît accesul secvenţial cît şi pe cel relativ. Acest lucru derivă din faptul că accesul relativ este realizat de sistem printr-o deplasare secvenţială faţă de începutul acestuia, deplasare care este egală cu valoarea expresiei: număr_relativ × lungime_articol.

căutarea începe de la directorul curent. Daţi exemple de fişiere aflate pe diferite tipuri de suport extern şi specificaţi ce tipuri de acces sînt permise în fiecare caz. COM2. B:.." în locul numelui de fişier. În limbajul curent folosit de practicieni se utilizează noţiunea de director şi în cazul subdirectoarelor.. respectiv caracterele ".". CLOCK$ (dispozitiv pentru ceasul de timp real). Ak-1 k Ak k-1 . Un director (subdirector) poate conţine fişiere şi/sau alte subdirectoare (figura 5. Fiecare nume de director (subdirector) din interiorul căii este precedat de caracterul backslash (\). Fiecare subdirector conţine două intrări speciale marcate prin caracterul ". Calea selectată la un moment dat poate începe de la rădăcină sau de la subdirectorul curent. Un disc DOS conţine un singur director rădăcină. Prin lipsă.7.. care nu pot fi utilizate de programator pentru propriile fişiere: CON. Pentru ca un fişier să fie localizat în cadrul structurii arborescente se foloseşte un identificator extern (specificator) care are următoarea formă sintactică: [n:][cale][\]nume_fişier[. NULL. . nume_fişier este numele extern al fişierului. care la rîndul lui are zero sau mai multe subdirectoare şi/sau fişiere. indicînd faptul că entitatea este subdirector (nu fişier de date). Unitatea de disc. cale (path) este calea de acces de la directorul rădăcină pînă la subdirectorul dorit. PRN. Prima intrare realizează „autopunctarea”. mai puţin unele caractere speciale. COM1. directorul şi subdirectorul în care se lucrează la un moment dat se numesc curente.. cel mai adesea. Structura sistemului de fişiere sub MS-DOS/Windows Sistemul de operare MS-DOS utilizează o formă logică arborescentă de grupare a fişierelor de pe discuri în directoare şi subdirectoare. LPT3. Cînd calea începe cu caracterul backslash (\) căutarea începe de la rădăcină. AUX. . Frunzele arborelui sînt. 3. .extensie] unde n este numele unităţii de disc (A:. . Prin lipsă nu se asumă nici o valoare. Prin lipsă. 3. extensie este formată din maxim trei caractere alfanumerice prin care utilizatorul are posibilitatea să-şi identifice fişiere cu conţinuturi diferite. Există o serie de nume prestabilite. dar pot fi şi subdirectoare vide.. Subdirectoarele pot avea oricîte niveluri de imbricare. LPT2. format din maxim 8 caractere alfanumerice.4). se consideră unitatea curentă..* material didactic pentru ID * 49 Acces direct prin numărul relativ k-1 P(Ak)=k-1 A1 0 Număr relativ A2 1 . Principiul de realizare a accesului direct prin număr relativ Teste de autoevaluare 3... asociate unor dispozitive standard de intrare/ieşire. An n-1 EOF Fig. în caz contrar.7. În construirea căii de acces se poate folosi succesiunea de două puncte pentru a indica subdirectorul părinte.. LPT1. fişiere. a doua intrare „punctează” subdirectorul părinte." \ / : ' > < + = . ). ca: . C: etc). se consideră calea subdirectorului curent.

Formate ASCII: . . . .EXE → program executabil. .GIF .50 Algoritmi în programare Rădăcina (pe discul C:\) F1 D1 D2 D3 F2 F3 F4 F2 D4 D5 F5 F6 F7 F8 F9 Fig. . Producătorii de software au impus unele denumiri de extensii.OBJ → program obiect. 14. Exemplu de structură arborescentă de directori Exemple 12. Pentru structura din figura 3.ZIP → arhivă compactată cu PKZIP sau WINZIP. 16.PAS → program sursă PASCAL.8: C:\F1 → Fişierul F1 din rădăcină C:\D2\F2 → Fişierul F2 din subarborele C:\D2 C:\F2 → Fişierul F2 din directorul rădăcină C:\D2\D4\F9 → Fişierul F9 din subarborele C:\D2\D4 Pentru a indica fişierul F9 se poate folosi una din scrierile: C:\D2\D4\F9 → de oriunde \D2\D4\F9 → de oriunde din unitatea C: .JPG .PNG .CBL → program sursă COBOL.BAT → fişiere de comenzi DOS (prelucrări BATCH).C → program sursă C.COM → program executabil. deşi opţionale. . Standard MS-DOS: .ASM → program sursă ASSEMBLER. Exemple 13. Formate grafice: . . Standarde de firmă: .8. .ARC → arhivă compactată cu PKPAK sau ARC. 3. 15.. care.TXT → fişier text. .SYS → driver de sistem. . oferă posibilitatea simplificării referirii fişierelor în unele comenzi sau aplicaţii.\D4\F9 → din subdirectorul D5 F9 → din subdirectorul D4. .DBF → bază de date DBASE.BAS → program sursă BASIC.

scriere. prioritar. operaţii de acces la date („articole”). în C există un singur tip de fişiere: flux de octeţi (înşiruire de octeţi. metodele de organizare. închiderea fişierului. deschiderea fişierului. numite şi de gestiune. În sistemul de operare MS-DOS sînt incluse funcţii de întrerupere care. linii sau cîmpuri) în vederea prelucrării lor. modificarea valorilor unor cîmpuri din anumite entităţi (modificare).8. . În programele C. care se împart în operaţii la nivel de fişier şi la nivel de articol. Aceste operaţii se regăsesc. Trebuie făcută remarca. prin intermediul BIOS (Basic Input Output System). dacă din punctul de vedere al utilizatorului operaţiile de prelucrare se descriu relativ simplu. în totalitate. Această succesiune poate fi tratată logic ca un fişier de un tip sau altul. Operaţiile la nivel de fişier se referă la aspecte ca: înscrierea fişierului în [sub]directoare. În concluzie. În cazul purtătorilor nereutilizabili. un mod de prelucrare şi mai puţin un mod de memorare.* material didactic pentru ID * 51 3. că din punct de vedere fizic fişierul se reprezintă în suportul extern ca o succesiune de octeţi. Privite sub aspectul semnificaţiei pentru utilizator. validarea şi interzicerea accesului la fişier (deschidere/închidere). Operaţii de prelucrare a fişierelor Asupra unui fişier se pot executa diverse operaţii de prelucrare. citire etc. realizarea efectivă a lor de către sistemul de calcul este complexă. Este sarcina programatorului să asigure „suprapunerea” corectă a fişierului logic peste cel fizic. singurele operaţii care au sens sînt cele de deschidere/închidere a fişierelor. blocuri. Indiferent de limbajul de programare folosit. aceste operaţii se referă la: înscrierea iniţială a entităţilor pe purtătorul tehnic (populare). fără nici un fel de organizare sau semnificaţie). regăsirea entităţilor în vederea satisfacerii unor cerinţe de informare (consultare). lansează anumite operaţii cu un echipament. deosebit de importantă. la prelucrarea fişierelor pe discuri magnetice. asignarea fişierului intern (numele logic) la unul extern (fizic). se disting fişiere text. ştergerea fişierului din [sub]directoare (ştergere) etc. Organizarea acestui flux de octeţi este secvenţială. eliminarea entităţilor care nu mai sînt necesare (ştergere). Din acest punct de vedere se poate spune că prin fişier logic se înţelege. prin apeluri de funcţii. Din punct de vedere al tipurilor de date. Strîns legat de lucrul cu cele două tipuri de fişiere este modul în care se face transferul datelor între memoria principală şi suportul extern: transfer posibil cu conversie (în cazul fişierelor text) şi transfer fără conversie (în cazul fişierelor binare). actualizarea fişierului prin includerea de noi entităţi (adăugare). în care toate datele sînt memorate în forma identică cu cea din memoria principală (MP). Pentru lucrul cu fişiere trebuie identificate tipurile acestora. Operaţiile la nivel de articol se referă la accesul la entităţile de date ale fişierului (articole. în care toate datele sînt sub formă ASCII (un caracter/octet) şi fişiere binare. Din punct de vedere al reprezentării datelor în suportul extern. modurile de acces şi tipurile de articole acceptate. operaţiile necesare pentru prelucrarea fişierelor sînt: descrierea fişierului (crearea tabelei care memorează caracteristicile fişierului). operaţiile de I/E sînt realizate cu ajutorul unei mulţimi de funcţii specializate pentru căutare.

S_IWRITE (scriere). descrierea fişierelor se realizează în corpul programelor. Utilizatorul îl foloseşte pentru a indica sistemului fişierul asupra cărui doreşte să facă prelucrări. Pentru utilizarea acestui nivel. conţinutul fişierului existent se pierde. ne putem referi la fişiere text şi fişiere binare. Nivelul inferior de prelucrare a fişierelor Nivelul inferior de prelucrare este folosit rar. Acestea sînt: fişierul standard de intrare (stdin).h. stat.h. Aceste valori pot fi combinate folosind operatorul | (sau logic pe biţi). dar asupra cărora se poate interveni şi în mod explicit. Acest index este de tip int şi se numeşte manipulator de fişier (handle). Putem spune că din punct de vedere al prelucrării. la acest nivel. iar protecţie defineşte modul de protecţie a fişierului creat (protecţia este dependentă de sistemul de operare). int protecţie).1.h. conform convenţiilor sistemului de operare. la care accesul este numai secvenţial). care sînt gestionate automat de sistem. fişierul fiind referit printr-un index care indică intrarea într-o tabelă de gestiune a resurselor sistemului de operare. Funcţia returnează manipulatorul fişierului nou creat.h şi fcntl. cu excepţia lui stderr care va fi asociat întotdeauna monitorului. În lucrul cu fişiere (sau la orice apel de sistem). prin specificator de fişier se va înţelege un nume extern de fişier. definită în errno. numef este un pointer spre un şir de caractere care defineşte specificatorul de fişier. Fişierele standard pot fi redirectate conform convenţiilor sistemului de operare.8. în caz de eroare în timpul unei operaţii se setează variabila errno. Nu există un tip anume de dată. Efectul produs este ştergerea fişierului existent şi crearea unuia gol.h şi stdlib. Maniera de prelucrare este asemănătoare celei de la nivelul sistemului de operare.52 Algoritmi în programare Accesul la fişiere se poate face secvenţial sau direct (cu excepţia fişierelor standard. Funcţia creat poate fi apelată şi pentru un fişier existent.h sînt definite următoarele valori pentru parametrul protecţie: S_IREAD (citire).h. Valorile posibile sînt definite în stdlib. În . caracteristicile acestora obţinîndu-se din context. fişierul standard de ieşire (stdout). numai în programele de sistem. La acest nivel. Specificatorul de fişier poate să conţină strict numele fişierului sau poate conţine şi calea completă pînă la el.h. cu acelaşi nume. În limbajul C există două niveluri de abordare a lucrului cu fişiere: nivelul inferior de prelucrare (fără gestiunea automată a zonelor tampon de intrare/ieşire) şi nivelul superior de prelucrare (se folosesc funcţii specializate de gestiune a fişierelor). fişierul standard asociat imprimantei cuplate la portul paralel (stdprn). În continuare. fişierul standard asociat portului serial (stdaux). care are următorul prototip: int creat(const char* numef. S_IEXEC (execuţie). stddef. 3. Există fişiere standard. Crearea şi asignarea unui fişier nou se realizează prin apelul funcţiei creat. Funcţiile de prelucrare la nivel superior a fişierelor tratează fluxul de octeţi acordîndu-i o semnificaţie oarecare. Manipulatorul este creat şi gestionat de către sistemul de operare. În bibliotecile limbajului există funcţii predefinite pentru prelucrarea fişierelor. În biblioteca stat. fierul standard pentru scrierea mesajelor de eroare (stderr). în programul C trebuie incluse bibliotecile standard io.

Numărul maxim de octeţi care pot fi citiţi este 65534 (deoarece 65535 – 0xFFF – se reprezintă intern la fel ca -1. nf este manipulatorul de fişier (alocat la crearea sau deschiderea fişierului). nu a fost găsit fişierul) sau EACCES (fişierul nu poate fi accesat).h. void* zonat. Deschiderea unui fişier existent se realizează prin apelul funcţiei open. n este numărul de octeţi care se scriu. trebuie ca la revenirea din funcţia write. unsigned n). În general. zonat este un pointer spre zona tampon din care se face scrierea (aceasta este definită de programator). care defineşte tipul erorii. care are următorul prototip: int write(int nf. nf este manipulatorul de fişier (alocat la crearea sau deschiderea fişierului). unsigned n). iar la eroare se returnează -1 (tipul erorii depinde de sistemul de operare).unsigned mod]). indicatorul de eroare). iar n este dimensiunea zonei receptoare (numărul maxim de octeţi care se citesc). Mod este folosit numai dacă parametrul acces conţine şi valoarea O_CREAT. S_IREAD|S_IWRITE – se permite atît scrierea cît şi citirea din fişier. Funcţia returnează numărul de octeţi scrişi în fişier. void* zonat. La scrierea în fişiere text. dacă este mai mică. numef este pointer spre un şir de caractere care defineşte specificatorul de fişier. S_IREAD – se permite citirea din fişier. În caz de eroare. Cele mai importante sînt: O_RDONLY – fişierul va fi accesat numai pentru citire.* material didactic pentru ID * 53 caz de eroare se returnează valoarea –1 şi se setează variabila globală errno. Fişierul standard de intrare (stdin) are descriptorul de fişier 0. valoarea returnată este -1 şi se setează variabila errno. O_WRONLY – fişierul va fi accesat numai pentru scriere. valoarea returnată să fie egală cu n. Citirea dintr-un fişier se realizează prin apelul funcţiei read. care are următorul prototip: int open(const char *path. constantele care descriu modurile de acces la fişier sînt descrise în fcntl. Fişierul standard de ieşire (stdout) are manipulatorul 1 iar cel de eroare (stderr) are manipulatorul 2. write va scrie în fişier perechea CR/LF. Numărul maxim de octeţi care pot fi citiţi este 65534 (deoarece 65535 – 0xFFF – se reprezintă intern la fel ca -1.int access[. zonat este un pointer spre zona tampon în care se face citirea (aceasta este definită de programator). Funcţia returnează numărul de octeţi citiţi din fişier. indicatorul de eroare). O_RDWR – fişierul va fi accesat atît pentru citire cît şi pentru scriere. dacă în fluxul octeţilor care se scriu apare caracterul LF. Funcţia returnează manipulatorul fişierului. Închiderea unui fişier se realizează prin apelul funcţiei close. Aceste moduri pot fi combinate folosind operatorul |. Valorile obişnuite pentru errno sînt EBADF (manipulator eronat. O_CREAT: fişierul va fi creat ca nou. s-a produs o eroare (probabil discul este plin). care are următorul prototip: . acces este modul de acces la fişier. Scrierea într-un fişier se realizează prin apelul funcţiei write. În cazul citirii sfîrşitului de fişier se va returna valoarea 0 (0 octeţi citiţi). caz în care indică modul de protecţie a acestuia: S_IWRITE – se permite scrierea în fişier. care are următorul antet: int read(int nf.

0). 0l. Apelul vb=lseek(nf. de exemplu pentru fişiere read only). La eroare returnează valoarea -1L. folosind funcţia chmod: int chmod(const char *cale. nf este manipulatorul de fişier. închiderea unui fişier se realizează automat. Permisiunile sînt aceleaşi ca la funcţia open. unde cale este specificatorul de fişier iar mod noile permisiuni. 18. 2). Verificarea atingerii sfîrşitului de fişier se face folosind funcţia eof: int eof(int nf).54 Algoritmi în programare int close(int nf). unde nf este manipulatorul fişierului. realizează poziţionarea la sfîrşitul fişierului (în continuare se poate scrie în fişier folosind write). Funcţia returnează valoarea 0 (închidere cu succes) sau -1 (eroare). int mod). Funcţia returnează poziţia faţă de începutul fişierului. 0l. int start). Poziţionarea într-un fişier se realizează prin apelul funcţiei lseek. Exemple 17. Rezultatul întors de chmod are aceeaşi semnificaţie ca şi unlink. în număr de octeţi. nf este manipulatorul de fişier. Exemplu 19. 0 în caz contrat şi -1 în caz de eroare (nu este găsit fişierul – errno primeşte valoarea EBADF). 1 (poziţia curentă în fişier) sau 2 (sfîrşitul fişierului). care are următorul prototip: long lseek(int nf. dacă programul se termină prin apelul funcţiei exit. long offset. De asemenea. offset este un parametru de tip long (numărul de octeţi peste care se va deplasa pointerul în fişier). Funcţia returnează 0 (ştergere cu succes) sau -1 (eroare). Apelul vb=lseek(nf. #include <sys\stat. Ştergerea unui fişier existent se realizează prin apelul funcţiei unlink. realizează poziţionarea la începutul fişierului. care are următorul prototip: int unlink(const char* numef). iar start este poziţia faţă de care se face deplasarea: 0 (începutul fişierului).h> . În caz de eroare se setează variabila errno cu valoarea ENOENT (fişierul nu a fost găsit) sau EACCES (accesul interzis pentru această operaţie.h> #include <string. Funcţia returnează valoarea 1 dacă pointerul este poziţionat pe sfîrşitul fişierului. numef este un pointer spre un şir de caractere care defineşte specificatorul de fişier. Pentru a putea şterge un fişier read only trebuie întîi schimbate drepturile de acces la fişier.

inclusiv variante ale funcţiilor anterioare. cu un număr de octeţi egal cu numărul de octeţi transferaţi (fără a trece de sfîrşitul de fişier). /* crearea unui fisier */ handle = open("TEST. close(handle). printf("%c". strlen(msg)). 1). Funcţia returnează un pointer spre o structură de tip FILE (în care sînt înscrise date referitoare la fişierul deschis) sau NULL dacă fişierul nu se poate deschide: FILE* fopen(const char* nume_extern.} while (!eof(handle)). un fişier se descrie ca pointer către o structură predefinită (FILE – tabela de descriere a fişierului (FIB)): FILE* f. iar la sfîrşit se găseşte caracterul CTRL-Z). Funcţiile folosite la acest nivel pot fi împărţite în trei categorii: funcţii de prelucrare generale.h> int main(void) { int handle. apărute odată cu dezvoltarea sistemelor de operare. /* pozitionare la inceputul fisierului */ lseek(handle. 0L. indiferent de tipul informaţiei conţinute.8. /* citire cite unui caracter din fisier pina la sfirsitul sau si afisare */ do {read(handle. Nivelul superior de prelucrare a fişierelor La acest nivel.const char* mod). return 0. /* scrierea unor date in fisier */ write(handle.* material didactic pentru ID * 55 #include <stdio. O_CREAT | O_RDWR. Funcţiile care lucrează cu conversie se aplică fişierelor care conţin informaţie de tip text (linii de text. SEEK_SET). din care funcţiile de prelucrare preiau secvenţe pe care le tratează într-un anumit fel (sau în care inserează secvenţe de octeţi). &ch.} Bibliotecile limbajului conţin şi alte funcţii pentru prelucrarea fişierelor la nivel inferior. separate prin perechea CR/LF. char ch. Funcţiile de prelucrare generală se aplică tuturor fişierelor. ch). msg. prelucrarea efectuată de acestea nu are nici un efect asupra conţinutului fişierului. funcţii de citire/scriere cu conversie şi funcţii de citire/scriere fără conversie. .$$$". Fişierul este considerat ca flux de octeţi. 3. Funcţiile care lucrează fără conversie se aplică fişierelor care conţin informaţie binară. Tipul FILE (descris în stdio. Funcţiile de citire/scriere deplasează pointerul de citire/scriere al fişierului.2. spre sfîrşitul acestuia.h> #include <io. char msg[] = "Acesta este un test".h> #include <fcntl. S_IREAD | S_IWRITE). Funcţii de prelucrare generală Deschiderea şi asignarea se realizează prin apelul funcţiei fopen.h) depinde de sistemul de operare.

2. Este permisă numai scrierea. care are următorul prototip: int fclose(FILE* f). Asignarea se realizează prin expresie de atribuire de tipul: nume_intern=fopen(sir_nume_extern. modul considerat depinde de valoarea variabilei _fmode: dacă valoarea este O_BINARY. Sînt permise citiri şi scrieri. FILE* f. mod este un şir de caractere care specifică modul de deschidere a fişierului.1. Zonele tampon alocate automat de sistem sînt eliberate. sînt golite toate zonele tampon asociate lui. Deschide un fişier existent numai pentru citire Suprascrie un fişier existent sau creează unul nou. Tabelul 3. se consideră fişier binar.DAT". Exemplu 19. Revenirea la începutul fişierului se realizează prin funcţia rewind.2. Deschide un fişier existent pentru citire şi scriere Suprascrie un fişier existent sau creează unul nou. se consideră fişier text. w+b Închiderea fişierelor se realizează prin apelul funcţiei fclose. Numai pentru fişiere text. La opţiunile de mai sus se poate adăuga b pentru fişiere binare sau t pentru fişiere text. Modurile uzuale pentru deschiderea fişierelor sînt prezentate în tabelul 3.sir_mod).1. f = fopen("PROD. Funcţia închide fişierul primit ca parametru şi returnează valoarea 0 în caz de succes sau -1. permiţîndu-se numai operaţia de scriere Deschide un fişier existent pentru adăugare la sfîrşit (extindere) sau îl creează dacă nu există. permiţîndu-se atît citiri cît şi scrieri. Numai pentru fişiere text. De obicei implicită este valoarea O_TEXT. Modurile de deschidere a unui fişier Mod a r w a+ r+ w+ Scop Deschide un fişier existent pentru adăugare la sfîrşit (extindere) sau îl creează dacă nu există. în caz de eroare. dacă valoarea este O_TEXT. Înainte de închiderea fişierului."r").56 Algoritmi în programare Parametrul nume_extern constituie specificatorul de fişier iar. Moduri uzuale pentru deschiderea fişierelor Operaţia de gestiune Creare Consultare Actualizare Creare şi actualizare Extindere Fişiere text w r w+ a Fişiere binare wb rb r+b rwb. Dacă nu este prezentă nici litera b nici litera t. Executarea funcţiei are ca efect poziţionarea la începutul fişierului f (care era deschis anteri- . cu prototipul: void rewind(FILE *f). Modurile în care poate fi deschis un fişier sînt prezentate în tabelul 3. Tabelul 3.

Dacă fişierul este binar. returnează poziţia în fişierul f a pointerului de citire/scriere în caz de succes sau -1L în caz contrar.* material didactic pentru ID * 57 or). funcţia îl goleşte. Golirea explicită a zonei tampon a unui fişier se realizează prin apelul funcţiei fflush. Aflarea poziţiei curente în fişier se realizează prin apelul uneia din funcţiile fgetpos sau ftell: int fgetpos(FILE* f. toate încercările de citire vor eşua. iar în caz de eroare valoarea EOF (definită în stdio. Valoarea acestui indicator este setată la fiecare operaţie de citire din fişierul respectiv. Exemplu 21. După atingerea sfîrşitului de fişier. long ftell(FILE* f). care are următorul prototip: int fflush(FILE* f).h). pînă la apelul funcţiei rewind sau închiderea şi redeschiderea fişierului . Valoarea întoarsă este 0 (fals) dacă indicatorul are valoarea sfîrşit de fişier şi diferit de zero (adevărat) în caz contrar. După apelul lui rewind poate urma o operaţie de scriere sau citire din fişier. la adresa poziţie se află poziţia pointerului de citire/scriere din fişierul f. iar în caz de eroare o valoare nenulă şi setează variabila errno la valoarea EBADF sau EINVAL. Dacă fişierul are asociată o zonă tampon de intrare. Înainte de a citi un şir de caractere de la tastatură. Modificarea poziţiei pointerului de citire/scriere se poate face prin poziţionare relativă: int fseek(FILE* f. Dacă fişierul f are asociată o zonă tampon de ieşire. Valoarea returnată poate fi folosită pentru poziţionare cu funcţia fsetpos. Apelul lui feof trebuie să fie precedat de apelul unei funcţii de citire din fişier.int origine). poziţia este dată în număr de octeţi faţă de începutul fişierului. unde deplasare reprezintă numărul de octeţi cu care se deplasează pointerul în fişierul f. se realizează prin apelul macrodefiniţiei feof: int feof(FILE* f).long deplasare. După apel. Macro-ul furnizează valoarea indicatorului de sfîrşit de fişier asociat lui f. Ştergerea se realizează prin apelul: fflush(stdin). Valoarea poate fi folosită pentru poziţionare cu funcţia fseek. ca număr relativ al octetului curent. Primul octet are numărul 0. Testarea sfîrşitului de fişier. În caz de succes returnează valoarea zero. funcţia scrie în fişier toate informaţiile din acesta. zona tampon trebuie golită pentru a preveni citirea unui şir vid (datorită unei perechi CR/LF rămase în zona tampon de la o citire anterioară a unei valori numerice). iar . resetarea indicatorului de sfîrşit de fişier şi a indicatorilor de eroare (se înscrie valoarea 0). la poziţia curentă. În caz de succes funcţia întoarce valoarea 0.fpos_t* poziţie).

Este compatibil cu tipul unsigned. Nu sînt permise wildcard-uri (?. sau remove. Ambele funcţii resetează indicatorul de sfîrşit de fişier şi anulează efectele unor eventuale apeluri anterioare ale lui ungetc asupra acelui fişier.size_t dim. fread returnează numărul de entităţi citite. Ştergerea unui fişier existent se poate realiza prin apelul funcţiei unlink. unde cale reprezintă specificatorul fişierului (trebuie să fie închis). prezentată anterior. size_t este definit în mai multe header-e (între care stdio. unde n_vechi reprezintă vechiul nume al fişierului. care are următorul prototip: int remove(const char* cale). care are următorul prototip: size_t fread(void* ptr. Pointerul de citire/scriere se mută în fişierul f la octetul cu numărul indicat de parametrul poziţie (care poate fi o valoare obţinută prin apelul lui fgetpos). În caz de eroare sau cînd se întîlneşte sfîrşitul de fişier. iar n_nou reprezintă numele nou.const char* n_nou). numele nou nu este obligat să conţină aceeaşi cale. în caz de succes. şi le depune.size_t n.h) şi este un tip de dată folosit pentru a exprima dimensiunea obiectelor din memorie.const fpos_t poziţie). fără a interveni asupra conţinutului sau ordinii octeţilor respectivi. În total se citesc. fiecare de dimensiune dim. care are următorul prototip: int rename(const char* n_vechi. Funcţii de citire/scriere fără conversie Funcţiile efectuează transferuri de secvenţe de octeţi între memoria internă şi un fişier de pe disc.FILE* f). la adresa ptr. În caz de eroare se întoarce -1 şi errno primeşte una din valorile: ENOENT – nu există fişierul. Parametrul origine poate fi: SEEK_SET (0) – poziţionare faţă de începutul fişierului. un număr de n entităţi. Dacă numele vechi conţine numele discului (de exemplu C:). în ordinea citirii. funcţia returnează o valoare negativă sau 0. Citirea dintr-un fişier binar se realizează prin apelul funcţiei fread. Funcţia citeşte din fişierul f. n*dim octeţi. Folosind aceeaşi cale (sau nefolosind calea) se obţine redenumirea fişierului. Poziţionarea absolută se face cu funcţia: int fsetpos(FILE* f. Folosind o altă cale se obţine mutarea fişierului pe disc. EACCES – nu există permisiunea pentru operaţie sau ENOTSAM – dispozitiv diferit (mutarea se poate face doar pe acelaşi dispozitiv). SEEK_CUR (1) – poziţionare faţă de poziţia curentă. numele nou trebuie să conţină acelaşi nume de disc. *) în cele două nume.58 Algoritmi în programare origine reprezintă poziţia faţă de care se deplasează pointerul. Se semnalează eroare prin returnarea unei valori nenule numai în cazul în care f nu este deschis. În caz de succes se întoarce valoarea 0. . SEEK_END (2) – poziţionare faţă de sfîrşitul fişierului. Funcţia returnează valoarea 0 în caz de succes (şi uneori şi în caz de eşec). de la poziţia curentă. Dacă numele vechi conţine o cale. Redenumirea sau mutarea unui fişier existent se poate realiza prin apelul funcţiei rename.

DAT". Să se scrie funcţia care calculează numărul de articole dintr-un fişier binar. struct complex {int x.FILE* f). fwrite(& articol.size_t dim. Exemplu 24.0. } Funcţii de citire/scriere cu conversie Funcţiile efectuează transferuri de secvenţe de octeţi între memoria internă şi un fişier de pe disc.DAT". fseek(f.1. n=ftell(f)/l.1. cunoscînd lungimea în octeţi a unui articol. FILE *pf.y} articol. fwrite returnează numărul entităţilor scrise cu succes. . return n.2)."wb"). int l) { long p. p=ftell(f). începînd cu poziţia curentă. Transferul de caractere se efectuează prin următoarele funcţii: int int int int fgetc(FILE* f). fiecare de dimensiune dim.pf). int nrart(FILE *f. "rb") fread(&articol. putc(int c. Scrierea într-un fişier binar se poate realiza prin apelul funcţiei fwrite. Exemplul anterior creează un fişier binar nou în care scrie o secvenţă de octeţi conţinînd reprezentarea binară a unei date de tip struct complex. struct complex {int x. Exemplu 23.sizeof(articol). convertind secvenţa de la reprezentarea internă (binară) la reprezentarea externă (ASCII) şi invers. int n.p). un număr de n entităţi contigue.* material didactic pentru ID * 59 Exemplu 22. getc(FILE* f). care are următorul prototip: size_t fwrite(const void* ptr.sizeof (articol).size_t n. Prin numele funcţiei se întoarce numărul de articole din fişier. pf=fopen("NR_COMPL. else printf("Fisierul nu poate fi deschis"). if(f_complex=fopen("NR_COMPL. FILE * f_complex.0. FILE *f). În exemplul anterior se deschide un fişier binar din care se citeşte un articol de tip struct complex care se depune în variabila articol. FILE *stream).y} articol. aflate în memorie la adresa ptr. fputc(int c. În caz de eroare se returnează o valoare negativă. Funcţia scrie în fişierul f. fseek(f. Funcţia are ca parametri fişierul şi lungimea în octeţi a unui articol.f_complex).

Funcţia fputs scrie în fişierul f caracterele şirului aflat la adresa s. se adaugă la sfîrşitul şirului din memorie caracterul nul ‘\0’.. altfel se returnează EOF. iar în caz de eroare returnează EOF.…]). În caz de succes fputs returnează ultimul caracter scris. În caz de succes. Transferul de date cu format controlat este realizat prin funcţiile: int fprintf(FILE* f. În locul fişierelor standard. care nu îl reţine). Transferul se încheie atunci cînd s-au citit n-1 caractere sau s-a întîlnit caracterul newline. caracterul nul fiind adăugat după el (spre deosebire de gets. diferenţa constînd în entitatea din/în care se transferă datele. Funcţia fputc şi macrodefiniţia putc scriu caracterul c în fişierul f.…]).60 Algoritmi în programare Funcţia fgetc şi macrodefiniţia getc returnează următorul caracter din fişierul f (după ce îl converteşte la reprezentarea de tip întreg fără semn). Şirul de la adresa s poate fi obţinut prin transfer fără format dintr-un fişier text (pentru sscanf) sau poate urma să fie scris într-un fişier text prin funcţia fputs.const char *format[. Transferul de şiruri de caractere se efectuează prin funcţiile: char* fgets(char* s.FILE* f). Singura diferenţă constă în fişierul în/din care se transferă datele. Cele două funcţii lucrează identic cu printf şi scanf.. Funcţia ungetc pune caracterul c în zona tampon de citire asociată fişierului f. Aceste funcţii lucrează identic cu printf şi scanf. În caz de eroare se returnează valoarea c. acest funcţii folosesc o zonă de memorie de tip şir de caractere. Apelarea funcţiilor fflush. sau rewind şterge aceste caractere din flux.. funcţia va întoarce EOF (valoarea -1). fără să fie citit primul caracter pus în flux. În caz de eroare returnează EOF. fseek.const char* format[. a cărei adresă este furnizată în parametrul s. îl va înlocui pe acesta. pentru fprintf şi fscanf este necesară precizarea explicită a fişierului cu care se lucrează. La întîlnirea sfîrşitului de fişier (fără a fi transferat vreun caracter) sau în caz de eroare fgets returnează NULL.. acesta va fi transferat în memorie. Tot EOF va întoarce şi dacă sînt probleme la citirea din fişier.]). Dacă citirea s-a terminat prin întîlnirea caracterului newline.int n. int fputs(const char* s. fsetpos. În caz de succes returnează adresa şirului citit (aceeaşi cu cea primită în parametrul s). se pot folosi şi funcţiile int sprintf(char *s. int sscanf(const char *s. Deşi nu lucrează cu fişiere în mod direct. Dacă printf şi scanf lucrează cu fişierele standard stdin şi stdoud.const char* format[. Tratarea erorilor Pentru tratarea erorilor se folosesc următoarele funcţii: .]). Funcţia fgets citeşte un şir de caractere din fişierul f şi îl depune la adresa s. Terminatorul de şir (‘\0’) nu este scris şi nici nu se adaugă caracterul newline (spre deosebire de puts). ungetc returnează caracterul c. int fscanf(FILR* f. prin parametrul f..FILE* f). Dacă s-a ajuns la sfîrşitul fişierului. Un al doilea apel al funcţiei ungetc. La terminarea transferului.const char *format[.. La următoarea citire cu fread sau getc acesta va fi primul octet/caracter citit.

fputs(s1. /* se produce eroare la incercarea de citire */ getc(f).h> #include<string.cpp>> tmp.i."). (Exemplul a fost scris în mediul Borlandc 3. Funcţia resetează indicatorii de eroare şi indicatorul de sfîrşit de fişier pentru fişierul f (se înscrie valoarea 0). } . int n. Descrierea funcţiei introduse de la tastatură trebuie să conţină maxim 200 caractere.11.h> int main(void) { FILE *f.ttt\n").h> #include<process. //reseteaza indicatorii de eroare si sfarsit de fisier clearerr(f).h> #include<conio. int ferror (FILE* nume_intern). #include<stdlib. return 0. } fclose(f). fputs(s2. ferror este o macrodefiniţie care returnează codul de eroare al ultimei operaţii de intrare/ieşire asupra fişierului nume_intern (0 dacă nu s-a produs eroare). if(ferror(f)) /* s-a produs eroare de I/E? */ { /* afiseaza mesaj de eroare */ printf("Eroare la citirea din test. strncat(s1.) a) Fişierul 382_a. Exemplu 25.h> #include<stdio. ca subprogram C).txt"). gets(&s1[7]).cpp conţine programul care realizează citirea formei funcţiei.j.NULL)."w").ttt". Exemplul următor creează un fişier nou din care încearcă să citească date #include <stdio.6). apoi compilează şi execută un alt program. care va include subprogramul creat. system("bcc –Id:\borlandc\include -Ld:\borlandc\lib 382_b. operaţiile de intrare/ieşire vor semnala eroare pînă la apelul lui clearerr sau rewind.h> void main() { char s1[213]="return(". execl("382_b ".f)."w"). Funcţia se introduce ca şir de caractere şi poate conţine apeluri de funcţii standard C.* material didactic pentru ID * 61 void clearerr (FILE* f).\r\n}". char s2[]="double f(double x)\r\n\{\r\n". } Exemplu 26. FILE *f. Odată ce indicatorii de eroare au fost setaţi la o valoare diferită de 0.cpp". fclose(f). Să se scrie un program care calculează şi afişează valoarea unei funcţii introduse de la tastatură într-un punct dat.f). /* deschide fisierul pentru scriere*/ f=fopen("test. Programul creează un fişier sursă C (în care este scrisă forma funcţiei. f=fopen("functie. printf("functia f(x)="). compilarea şi execuţia programului care calculează valoarea funcţiei.

Stoica . Articolul este o dată complexă. I. Ghilic-Micu.h> #include<conio. În funcţie de modul de utilizare. cu acces direct la componente.scanf("%lf". Ghilic-Micu.x. indispensabile în cadrul aplicaţiilor de informatică economică. B.2lf". Liviu Negrescu . ASE. C.f(x)). După încheierea studierii acestei unităţi de învăţare. Gh. studenţii sînt au cunoştinţele şi abilităţile necesare lucrului cu structuri de date externe. getch().h> #include<math. Ed. Răspunsuri şi comentarii la testele de autoevaluare 1. printf("f(%7. II. Teorie şi aplicaţii.Bazele elaborării programelor.cpp" void main() { double x. M. Ed. Apostol. 2003 3. M. #include<stdio. Ed. printf("x="). B. C. Ed. Exerciţii rezolvate şi propuse. Roşca. Cocianu. ASE Bucureşti. I. B. calculează valoarea funcţiei în acest punct şi o afişează. M. utilizarea articolelor interne pentru prelucrarea fişierelor externe. Gh. M. Ed. I. 1998 5. 2002 . Ştiinţa învăţării unui limbaj de programare. Cluj-Napoca. eterogenă.Algoritmi în programare. Bibliografia unităţii de învăţare 1. 1994 4. Ce este un fişier? 5. Gh. I. 2007 2. Stoica. operaţii generale de prelucrare a fişierelor de date. Uscatu. Uscatu .62 Algoritmi în programare b) Fişierul 382_b conţine programul care face citeşte punctul x. C. I. Care sînt operaţiile de bază pentru prelucrarea unui fişier de date? 6.Gh. Algoritmi în programare.h> #include"functie. Mircea . Scrieţi un program care preia un text de la tastatură şi îl scrie într-un fişier nou creat în directorul curent. Bucureşti. C.Programarea calculatoarelor.Limbajele C şi C++ pentru începători.2lf)=%7.Programarea calculatoarelor. Roşca & colectiv . Roşca. tipuri de fişiere. Stoica.&x). vol. Rezumat În cadrul acestei unităţi de învăţare au fost studiate următoarele aspecte ale programării calculatoarelor cu privire la articole şi fişiere de date: tipul de dată internă articol. } Teste de autoevaluare 4. Cocianu. C. Ghilic-Micu. Microinformatica. ASE Bucureşti. ASE Bucureşti. un articol poate fi alocat static sau dinamic. Roşca.

................ prin metoda top-down....1. actualizare.... Organizarea datelor în fişiere memorate pe medii magnetice externe presupune proiectarea unor algoritmi specifici operaţiilor de gestiune a acestora...... pe baza analizei problemei...68 Algoritmi de prelucrare a fişierelor binare care necesită actualizare .....5..... Descompunerea se poate realiza în mai multe faze (pe mai multe niveluri)........ 78 4....... relativ şi indexat........6....* material didactic pentru ID * 63 4................. Criteriile ........... Fişierele din cea de-a doua grupă sînt......................6...........95 Răspunsuri şi comentarii la testele de autoevaluare ........1............... Interclasarea fişierelor binare memorate dens ....3.......... Codificarea internă prin numere relative................89 4.. Corespondenţa internă dintre chei şi numere relative .............6.................. fişiere permanente (principale) în aplicaţii de gestiune economică şi au particularităţi de proiectare referitoare..... consultare.................. de obicei.....................2.63 4..............3...... 93 4........... 94 4... fişierele binare se pot grupa în: fişiere care nu sînt actualizate (ţinute la zi) şi fişiere care sînt actualizate............ Prelucrarea masivelor memorate în fişiere binare ...................... Algoritmi de prelucrare a fişierelor de date Cuprins Obiectivele unităţii de învăţare ........................................................ Modularizarea presupune ca.........................4............ la asigurarea ştergerii şi adăugării de articole... De obicei......... în special.63 4.94 4................. Studenţii vor putea să efectueze toate operaţiile de prelucrare a fişierelor de date: creare.......... Sortarea fişierelor binare memorate dens ................... 4...... Prelucrarea matricelor .79 4.........3................................97 Obiectivele unităţii de învăţare După studierea acestei unităţi de învăţare............ fişierele din prima grupă se regăsesc în aplicaţii matematice sau ca fişiere temporare şi de tranzacţii în aplicaţii de gestiune economică.....................2..................... astfel încît fiecare dintre acestea să îndeplinească anumite funcţii.........3..2....................................... studenţii vor avea cunoştinţe teoretice şi abilităţi practice necesare prelucrării fişierelor de date......... numite module.... Prelucrarea vectorilor..... Datorită complexităţii aplicaţiilor care prelucrează fişiere este recomandată aplicarea metodei modularizării algoritmilor şi programelor....96 Bibliografia unităţii de învăţare .1.... denumiţi generic algoritmi de prelucrare a fişierelor de date...................1............ Caracteristici generale ale algoritmilor de prelucrare a fişierelor ........................... 4... 76 4..................................... să se descompună rezolvarea ei în părţi distincte.. Codificarea externă prin numere relative ......76 4....... Caracteristici generale ale algoritmilor de prelucrare a fişierelor Din punct de vedere al operaţiilor de gestiune solicitate de diverse aplicaţii.. Algoritmi de prelucrare a fişierelor binare care nu necesită actualizare ............3............. Vor fi asimilate abilităţile necesare prelucrării fişierelor organizate secvenţial....

concepută modularizat. de intrare/ieşire). obligatorii oricărui algoritm şi care includ: punerea în corespondenţă a fişierelor logice cu fişiere fizice. citirea unui articol din fişierul conducător. indiferent de operaţia de gestiune. Fig. facultative. Ele se referă. algoritmii de prelucrare. Indiferent de numărul fişierelor utilizate. Operaţii iniţiale specifice. de regulă: afişarea variabilelor de total. un fişier nu este conducător dacă prelucrarea articolelor sale este dependentă de existenţa (de citirea) articolului altui fişier. Modulul ÎNCEPUT se realizează o singură dată. de experienţa programatorilor. De cele mai multe. Ordinea celor două operaţii (citire şi prelucrare) depinde de varianta de algoritm aleasă. parcurs secvenţial. în principal: iniţializări de variabile de total. afişări ale antetului. la: omogenizarea funcţiilor. înaintea prelucrării primului articol al fişierului conducător şi cuprinde următoarele grupe de operaţii: Operaţii iniţiale standard. titlului şi/sau a capului de tabel pentru situaţii de ieşire etc. De aceea.şi. în mare măsură. separarea funcţiilor de intrare/ieşire de funcţiile de prelucrare. iniţializarea unei variabile logice pentru sfîrşit de fişier (SF) şi citirea primului articol.64 Algoritmi în programare de descompunere în module depind. 4. pe de altă parte. totalitatea operaţiilor de prelucrare a articolului curent al fişierului conducător . operaţii finale specifice. ori o aplicaţie necesită existenţa mai multor fişiere active simultan. Altfel spus. utilizarea diverselor structuri de date. necesită utilizarea unei structuri repetitive pentru parcurgerea (parţială sau integrală) a fişierului respectiv. în principal. a statisticilor privind operaţiile de gestiune executate. pe de o parte. utilizarea eficientă a resurselor calculatorului (timp UC. memorie internă. pentru anumite variante. Fişierul conducător are proprietatea că articolele lui pot fi citite logic independent de prelucrarea altor fişiere. existenţa lor depinzînd de particularităţile problemei abordate şi care includ. redată în figura 4. după prelucrarea ultimului articol al fişierului conducător şi include următoarele grupe de operaţii: operaţii finale standard. . utilizarea unor module deja existente. şi. corespunzînd închiderii fişierelor implicate în prelucrare. în marea majoritate a algoritmilor. Modulele se implementează în program prin subprograme interne sau externe.1. logica prelucrării este coordonată. Schema logică generală a unui algoritm de prelucrare cu fişier conducător Modulul PRELUCRARE se execută repetitiv şi cuprinde. Algoritmii de prelucrare cu fişier conducător pot fi reprezentaţi prin schema logică generalizată.operaţii specifice fiecărei probleme . de un singur fişier. obligatoriu de intrare. închiderea situaţiilor de ieşire etc. Modulul SFÎRŞIT se execută o singură dată. la un moment dat. cu rol diferit (de intrare.1. Accesul la datele memorate în fişierul conducător se realizează la nivel de articol. numit fişier conducător (sau director). deschiderea fişierelor. care depind de natura problemei şi includ. de ieşire. periferie) etc.

verificînd rezultatul întors de funcţia de citire (fread).2). fişierele nu conţin articole. Pentru aflarea numărului de articole. Prelucrarea unui număr cunoscut de articole.p. Rezultatul poate fi preluat într-o variabilă pentru a fi folosit în condiţia de terminare a prelucrării (figura 4. 4. caz în care testarea sfîrşitului de fişier trebuie să urmeze după o operaţie de citire a unui articol. Lungimea fişierului este egală cu poziţia curentă.SEEK_END). folosind apelul funcţiei fread în expresia (condiţia) care controlează sfîrşitul prelucrării (figura 4. vid sau nevid. prin forme particulare ale condiţiei sfîrşit_de_prelucrare. 4. Detectarea sfîrşitului de fişier (fişier binar) Scheme logice valabile numai pentru fişiere binare Detectarea sfîrşitului de fişier prin operaţia de citire. În funcţie de variantele alese. l=ftell(f). fseek(f. prin determinarea în modulul ÎNCEPUT a numărului de articole din fişierul conducător (figura 4. la o operaţie de citire se citeşte un articol întreg. rezultatul întors de funcţia fread va fi 0. se foloseşte secvenţa următoare: p=ftell(f). În ambele variante fişierul conducător este binar.2. nr=l/sizeof(tip_articol). . deoarece. se pot construi scheme logice valabile pentru toate tipurile de fişiere sau numai pentru fişierele binare.5).3. Detectarea sfîrşitului de fişier (orice fişier) Fig. împărţind lungimea acestuia la lungimea unui articol (ambele măsurate în număr de octeţi).3) sau poate fi verificat direct. uzual. cu macrodefiniţia feof. din punctul de vedere al limbajului. Limbajul C nu oferă o funcţie standard pentru calcularea numărului de articole dintr-un fişier binar. Fig.4). Algoritmul trebuie să conţină o citire iniţială în modulul ÎNCEPUT şi o citire curentă la sfîrşitul modulului PRELUCRARE (figura 4. înseamnă că s-a ajuns la sfîrşitul fişierului. atunci cînd pointerul de citire se află la sfîrşitul fişierului. Din punctul de vedere al utilizatorului. fseek(f. Scheme valabile pentru toate tipurile de fişiere Detectarea sfîrşitului de fişier. Întrucît.* material didactic pentru ID * 65 Modalitatea de detectare/tratare a sfîrşitului de fişier conduce la existenţa mai multor variante ale schemei generale de prelucrare cu fişier conducător. Acest algoritm se poate aplica fişierelor vide sau nevide. Dacă rezultatul este mai mic decît numărul de blocuri de date care trebuie citite. în cazul atingerii sfîrşitului de fişier.0.SEEK_SET). se poate calcula numărul de articol de fişier. cunoscînd dimensiunea unui articol.

Fig. #include<stdio. deci lungimea fişierului măsurată în octeţi). Problema care se pune este detectarea sfîrşitului de fişier.h> #include<conio. Împărţirea se face exact.h> void main() { FILE *f.4. În C. Detectarea sfîrşitului de fişier (fişier binar) Fig. conducător este fişierul f. Crearea şi consultarea unui fişier text care memorează elemente întregi."w+"). macrodefiniţia feof nu face decît să furnizeze valoarea indicatorului de sfîrşit de fişier. de tip long reţine poziţia curentă în fişier. f este fişierul a cărui lungime trebuie calculată. fişier conducător este fişierul standard de intrare. 4. folosind funcţia feof pentru gestionarea sfîrşitului de fişier. f=fopen("numere. int x. tip_articol este tipul articolelor din fişier (din punctul de vedere al utilizatorului).66 Algoritmi în programare unde: variabila p. 4. citirea trebuie să apară înaintea verificării sfîrşitului de fişier. .dat". Forma generală a algoritmului este: <citire articol> while(!feof(f)) { <prelucrare articol citit> <citire articol> } Exemplu 1. în program. care este setat de operaţia de citire. variabila nr va primi ca valoare numărul de articole din fişie. Prelucrarea unui fişier cu dimensiune cunoscută Caracteristica generală a algoritmilor de prelucrare cu fişier conducător este parcurgerea secvenţială a fişierului conducător şi efectuarea unor prelucrări în funcţie de fiecare articol citit din acesta. variabila l reţine poziţia curentă (în număr de octeţi faţă de începutul fişierului.5. La crearea fişierului. long dim. clrscr(). La afişare. deoarece fişierul conţine un număr întreg de articole – utilizarea acestei secvenţe asupra unui fişier care conţine articole de alt tip (sau are conţinut de altă natură) va duce la rezultate incorecte.

sizeof(x). Deoarece aplicaţiile informatice din domeniul economic. temporare."wb+").&x).f). de ieşire. f=fopen("numere. while(!feof(f)) { printf("%d\t". social.sizeof(x). situaţii finale. Această alegere este motivată din următoarele puncte de vedere: descrierea articolelor este apropiată atît descrierii naturale a structurii unei entităţi din lumea reală (formată din cîmpuri cu nume.&x). } fseek(f. ale căror articole sînt date declarate ca structuri (folosind tipul de date struct). scanf("%d". reprezentare internă proprie. fread(&x. Fişierele cu conţinut de tip text sînt recomandate a fi utilizate ca fişiere de ieşire. } fclose(f). populare. de tip listă etc.0. scanf("%d".h> void main() { FILE *f. fişiere cu articole de aceeaşi structură (sau un număr mic de structuri diferite)."%d\n". pentru realizarea de liste.0. consultare şi actualizare).} Exemplu 2. în funcţie de scopul lor: de intrare.1.g. există posibilitatea de a descrie explicit mai multe structuri pentru articolele aceluiaşi fişier (articole cu structură variabilă). de intrare/ieşire.h> #include<conio. folosind fişier binar: #include<stdio.1. pînă la .x). fscanf(f."%d". cît şi descrierii din alte limbaje. alegînd limbajul C. fiind însă variante derivate din schema generală a unui algoritm de prelucrare cu fişier conducător.sizeof(x). scanf("%d". long dim. clrscr(). se poate aprecia că cele mai performante sînt fişierele binare."%d".. utilizează. în general. } fclose(f).x). Aceste caracteristici conduc la algoritmi specifici fiecărei operaţii de gestiune în parte (creare. while(!feof(stdin)) { fwrite(&x. fread(&x.1.&x).* material didactic pentru ID * 67 scanf("%d". int x. } fseek(f. cu predilecţie. datorită lipsei conversiilor la transferul între memoria principală şi memoria externă.SEEK_SET). operaţiile de acces la înregistrări se realizează cu viteză mare.&x). c=getch().f).f).SEEK_SET).&x). while(!feof(f)) { printf("%d\t". fiind rezidente pe disc. Acelaşi exemplu. semnificaţie şi factor de repetabilitate diferite). while(!feof(stdin)) { fprintf(f. rapoarte etc. administrativ etc. fscanf(f.x).dat". lungime. getch().} Fişierele utilizate într-o aplicaţie informatică au rol diferit în procesul prelucrării.&x).

fie prin alte limbaje (Cobol. Explicaţi cum se detectează sfîrşitul de fişier în limbajul C şi ce influenţă are acest aspect asupra algoritmilor de prelucrare a fişierelor. Schema logică a algoritmului de prelucrare este similară celei din figura 4.modulul PRELUCRARE începe cu citirea următorului cîmp. cu semnificaţie de sfîrşit de prelucrare. prin introducerea pentru primul cîmp din articol a unei valori prestabilite. cu rol de sfîrşit de fişier text. Din acest punct de vedere se întîlnesc două modalităţi: Populare densă.2. prin care articolele sînt scrise în casetele (virtuale) ale căror numere relative sînt furnizate explicit de utilizator (acces direct). operaţiile de creare (populare) şi consultare. cu următoarele particularităţi: . prin care articolele se scriu unul după altul. Fortran. introducerea unei date este adesea însoţită de proceduri de validare specifice. Standard. fişierul conducător corespunde mulţimii datelor introduse de la tastatură. fie de la tastatură (popularea interactivă). Algoritmi de prelucrare a fişierelor binare care nu necesită actualizare Asupra fişierelor binare care nu necesită actualizare se realizează. Convenţional. afişarea numelui primului cîmp din articol şi citirea valorii sale. fie prin editoare de texte. Articolele sînt preluate cîmp cu cîmp. neexistînd posibilitatea citirii unei variabile de tip articol şi. În . La populare. În ultimul caz. nr_relativ nu este limitat decît de spaţiul existent pe suportul extern. Teste de autoevaluare 1. Scrierea unui articol se realizează după poziţionarea pe numărul relativ dorit. Fişierele cu conţinut de tip text pot constitui şi sursa de creare a fişierelor binare. O altă problemă a populării fişierelor binare o reprezintă aşezarea articolelor pe suportul extern. sisteme de gestiune a bazelor de date (DBase. urmată de citirea celorlalte cîmpuri (eventual cu validările stabilite) şi se termină cu afişarea numelui primului cîmp şi cu citirea valorii acestuia (pentru articolul următor) (similar operaţiei din modulul ÎNCEPUT). cel mai des întîlnit în practică.2. Pentru un volum mare de date. prin introducerea caracterului CTRL-Z. Pentru fişierele care nu necesită actualizare acesta este tipul recomandat. Oracle etc. cu reintroducerea ei în cazul unei erori. Populare aleatoare. Pascal). Dintre operaţiile de actualizare pot fi realizate. prin consultarea utilizatorului. 4. FoxPro. Basic. de obicei. în ordinea în care au fost furnizate. dacă acestea au fost populate cu date. fără mari complicaţii. varianta prezintă dezavantajul măririi timpului de prelucrare.68 Algoritmi în programare listarea lor la imprimantă.). . fără a se lăsa locuri libere (acces secvenţial).modulul ÎNCEPUT are ca ultime operaţii. în plus. Popularea fişierelor se realizează prin preluarea datelor fie din alte fişiere primare (cu conţinut binar sau de tip text). Sfîrşitul introducerii datelor de la tastatură (şi implicit al procesului de populare a fişierului) poate fi: De tip chestionar. constituind unicul mijloc de compatibilitate directă. Metoda are dezavantajul că necesită evidenţa „articolelor vide”. modificarea şi adăugarea densă de articole. privind continuarea sau nu a introducerii articolelor..

SEEK_SET). cîmp cu cîmp. popularea aleatoare se recomandă numai dacă. printf("Cantitate lunara:\n").cant[i]). 12 Articolele sînt introduse de la terminal.&x. Articolele au următoarea structură logică: Cod produs Denumire Produs Preţ Mediu 1 Cantităţi lunare 2 . char nume_fisier[20].h> typedef struct { int cod. float pret_mediu.luna %d: ". Exemplu 3. scanf("%d". if(!(f=fopen(nume_fisier. scanf("%d".&x. prin introducerea caracterului CTRL-Z. n*sizeof(tip_articol).sizeof(PRODUS). //---Aici se termina operatiile initiale--while(!feof(stdin)) { //---PRELUCRARE ARTICOL--printf("Denumire produs: ").DAT.f).. char denumire[20]. cu informaţii despre producţia cantitativă dintr-un an la o societate comercială."wb"))) printf("\n\nNu poate fi creat fisierul cu numele %s". //---Aici se incheie prelucrarea articolului--printf("\nCod produs: ").nume_fisier).i++) { printf(" . PRODUS x. scanf("%d". } PRODUS. Pentru poziţionarea pe articolul cu numărul relativ n se foloseşte funcţia fseek astfel: fseek(f.denumire). } fwrite(&x. else { printf("\nCod produs: "). #include <stdio.cod).1. gets(nume_fisier). printf("Pret mediu: ").pret_mediu). Terminarea introducerii datelor este marcată standard. int i. void main() { FILE* f. după creare.&x.&x.* material didactic pentru ID * 69 cazul fişierelor care nu necesită actualizare. fflush(stdin).cod). //---INCEPUT--printf("\n\nNumele fisierului: ").i+1).i<12. fişierul este dens. } //---SFIRSIT--- . for(i=0. int cant[12]. unde n este numărul relativ al articolului iar tip_articol este tipul de dată care îi corespunde. Să se creeze cu populare densă un fişier PRODUSE. gets(x.. scanf("%f".

sizeof(PRODUS).cant[i]).f). Consultarea secvenţială presupune regăsirea articolelor în ordinea în care au fost scrise pe suportul tehnic de informaţii. directă sau mixtă.f).i++) printf("%3d ". multiplă.h> typedef struct { int cod. . } //---SFIRSIT--fclose(f). printf("\nDenumire produs:\t%s". cu următoarele diferenţe: cîmpul COD indică numărul relativ al articolului în fişier şi nu va fi memorat (nu va face parte din declaraţia tipului PRODUS). gets(nume_fisier).sizeof(PRODUS). //---INCEPUT--printf("\n\nNumele fisierului: ").denumire). Să se afişeze pe ecran conţinutul fişierului creat la exemplul anterior. Consultarea fişierelor are numeroase variante. scrierea articolului va fi precedată de apelul funcţiei fseek(f. După modul de regăsire a articolelor în cadrul fişierului.1.2f".codt*sizeof(PRODUS). if(!(f=fopen(nume_fisier. După numărul de caracteristici.pret_mediu). float pret_mediu. ea poate fi secvenţială. consultarea secvenţială poate fi: integrală. selecţia poate fi simplă. în funcţie de scopul prelucrării. for(i=0. fiind redundant. După numărul articolelor prelucrate. } PRODUS. int i.1.i<12. printf("\nPret mediu:\t\t %7.x. Exemplu 4.x.SEEK_SET).nume_fisier). //---Aici se incheie prelucrarea articolului--fread(&x. void main() { FILE* f. cînd se prelucrează numai acele articole care au una sau mai multe caracteristici comune (valori identice pentru acelaşi cîmp). char nume_fisier[20]. int cant[12]. dublă."rb"))) printf("\n\nNu poate fi deschis fisierul cu numele %s".x. char denumire[20]. #include <stdio. începînd cu primul şi terminînd cu ultimul. } } Observaţii: dacă se doreşte crearea fişierului de date cu populare în acces direct. cu selecţie. //---Aici se termina operatiile initiale--while(!feof(f)) { //---PRELUCRARE ARTICOL--printf("\n\nCod produs:\t\t%d".x. unde codt este o variabilă independentă în care se citeşte codul de la terminal. PRODUS x. Pentru consultarea secvenţială se poate utiliza oricare din tipurile de algoritmi prezentaţi anterior. programul este similar.cod).70 Algoritmi în programare fclose(f). else { fread(&x. cînd se prelucrează toate articolele fişierului. printf("\nCantitati lunare:\t").

* material didactic pentru ID * 71 } } Teste de autoevaluare 2. se salvează valoarea caracteristicii primului articol din grupă. care are aceeaşi valoare pentru mai multe înregistrări. Raportul final este listat la imprimantă. ca fişier text. Prelucrarea unui fişier sortat după criteriile enunţate. articolele trebuie sortate după caracteristicile de control. condiţia de prelucrare a unei grupe de control conţine. fie direct. Pentru aceasta se stabilesc cîmpuri asociate gradelor de total. se poate extinde pentru oricîte caracteristici şi grade de total. Fişierul poate constitui. scrieţi un program care determină luna (lunile) din an în care producţia valorică a societăţii a înregistrat valoarea maximă. caracteristica de grupare de cel mai înalt nivel. O caracteristică de control este un cîmp al articolului din fişierul de date. cu utilizare minimă de memorie. TOT1 şi TOT2 sînt variabile pentru calculul gradelor de total. executate la schimbarea valorii fiecărei caracteristici de control stabilite: operaţii iniţiale ale unei grupe de control prin care se iniţializează variabila de total specifică grupei. Obţinerea unei situaţii cu mai multe grade de total. unde cîmp_1 şi cîmp_2 sînt caracteristicile de control (cîmpuri din articol). . Folosind fişierul creat la exemplul 3. Pentru prelucrarea fişierului. se cumulează totalul grupei curente la totalul grupei ierarhic superioare. fie creat pe suport magnetic.6 se prezintă structura de principiu a unui program de obţinere a unui raport final. Exemplu 5. val_art e valoarea care interesează din fiecare articol (cîmp al articolului sau valoare calculată pe baza unor cîmpuri ale articolului) iar TOTG. în vederea imprimării ulterioare. toate celelalte condiţii din amonte. articolele care au valoare comună pentru o caracteristică de grupare se pot ordona pe submulţimi.3. Analog. presupune existenţa unor operaţii standard.2 sau 4. alte operaţii finale specifice grupei. formînd o grupă de control. pe lîngă condiţia specifică. operaţii finale ale unei grupe de control prin care se afişează totalul calculat pentru caracteristica ce se schimbă. v1 şi v2 sînt variabile de lucru pentru salvarea caracteristicilor. În figura 4. Între caracteristicile de grupare se stabileşte o relaţie de ordine ierarhică. cu control după două caracteristici şi trei grade de total. Numărul maxim de grade de total este superior cu unu numărului de caracteristici de control stabilite. în ansamblul său. ca fişier de ieşire. Acest tip de prelucrare intră în categoria consultărilor secvenţiale integrale şi urmează algoritmul de principiu din figurile 4. Structura unei pagini a raportului şi controlul trecerii la o nouă pagină trebuie asigurate de programator. pentru care se poate calcula totalul general. numite caracteristici de grupare sau caracteristici de control. Astfel. alte operaţii iniţiale specifice grupei.

În cadrul grupei studenţii vor fi trecuţi în ordine alfabetică. 4. Convenim că vectorul de note conţine valoarea zero acolo nu există notă. matricol int Nume şi prenume char[30] Facultate char[10] An de studiu int Grupa int 1 int Note 2 … int 20 int Datele se referă la situaţia şcolară a studenţilor dintr-o universitate. . Se va calcula media pentru fiecare grupă (ca medie a studenţilor din grupă). Cîte caracteristici de grupare şi cîte grade de total sînt implicate în acest program? (Pentru rezolvare va trebui studiată întîi sortarea fişierelor binare). an de studiu.6. pentru fiecare facultate (ca medie a mediilor anilor de studiu) şi media generală. grupa.72 Algoritmi în programare START Operaţii iniţiale generale TOTG=0 Citeşte articol !feof(f) Da Operaţii iniţiale Grupa 1 TOT1=0 v1=cîmp_1 ! feof(f) şi v1==cîmp_1 Nu Operaţii finale Geupa 1 TOTG+=TOT1 Da Operaţii iniţiale Grupa 2 TOT2=0 v2=cîmp_2 ! feof(f) şi v1==cîmp_1 şi v2==cîmp_2 Da Prelucrare articol Operaţii finale generale Nu Operaţii finale Grupa 2 TOT1+=TOT2 TOT2+=val_art Citeşte articol STOP Nu Fig. cu studenţii grupaţi după următoarele caracteristici: facultate. cu articole avînd următoarea structură: Nr. Scrieţi un program care creează un raport (întrun fişier text). pentru fiecare an din cadrul unei facultăţi (ca medie a mediilor grupelor componente). Schema logică – problema cu grade de total Teste de autoevaluare 3. Pentru fiecare student se înscrie numărul matricol. Fie un fişier binar organizat relativ. numele şi prenumele şi media.

relativ: "). Pentru evitarea situaţiilor în care numărul relativ se află în afara acestui domeniu.i<=ls. scanf("%d". // calculare numar articole din fisier printf("\nLimita inferioara: ").sizeof(tip_articol).* material didactic pentru ID * 73 Consultarea în acces direct presupune regăsirea articolului după numărul relativ.sizeof(tip_articol).r*sizeof(tip_articol). SEEK_SET).1.. metoda poate fi aplicată dacă se doreşte selectarea articolelor dintre două limite ale numerelor relative (limita inferioară . } Consultarea în acces mixt utilizează o combinaţie între accesul direct şi cel secvenţial. // citirea nr. Secvenţa care realizează acest lucru este: fseek(f. în vederea prelucrării unui grup de articole. relativ al ultimului articol // din secventa if((0<li)&&(li<=ls)&&(ls<=nr_art)) { fseek(f. { // citire nume fisier extern f=fopen(nume_fisier. Numărul relativ este furnizat de utilizator şi trebuie să aparţină domeniului 0. Secvenţa generală pentru prelucrarea în acces direct. fread(&art. 1.&ls). Secvenţa generală pentru prelucrarea în acces mixt. //citirea numarului relativ al articolului while(!feof(stdin)) { if(r>=nr_art) printf("\n Articol inexistent !"). f).sizeof(tip_articol). "rb"). se va include în program validarea apartenenţei numărului relativ la intervalul acceptat. Pentru fişierele binare. // -----------------------//PRELUCRARE ARTICOL //-----------------------} printf("\nNr. după care parcurgerea fişierului poate fi realizată prin orice tip de structură repetitivă. .&r). { // citire nume fisier extern f=fopen(nume_fisier. Exemplu 6. Întrucît fişierele sînt considerate ca fluxuri de octeţi. trebuie calculată poziţia articolului dorit în fişier ca produs între numărul său relativ şi dimensiunea unui articol în octeţi (nr*sizeof(tip_articol)).f). memorate contiguu în fişier şi selectabile printr-o condiţie.i++) { fread(&art. "rb").nr*sizeof(tip_articol). scanf("%d".dimensiune fişier-1 (dimensiunea calculată ca număr de articole).li*sizeof(tip_articol). Algoritmul de consultare în acces direct a unui fişier are un alt fişier conducător (de exemplu tastatura). relativ al primului articol // din secventa printf("\nLimita superioara: ").&r). Exemplu 7.SEEK_SET). } fclose(f).ls). Algoritmul trebuie să verifice relaţia 0≤li≤ls≤dimensiune fişier. Relativ (sau CTRL-Z): "). // calculare numar de articole din fisier printf("\nNr. for(i=li.&li).f).1. // citirea nr. scanf("%d".li şi limita superioară .SEE_SET). fread(&art. else { fseek(f. scanf("%d".

"rb").SEEK_END). { // citire nume fisier extern f=fopen(nume_fisier. după poziţionarea pe marcatorul de sfîrşit de fişier.74 Algoritmi în programare // ----------------------// Prelucrare articol // ----------------------} } else printf(" Nu este indeplinita conditia de limite"). binar etc. Secvenţa generală pentru adăugarea datelor la sfîrşitul fişierului. "rb+ "). Operaţia se realizează similar populării în acces secvenţial. while(!feof(stdin)) // adaugarea mai multor articole { // ----------------------------------// Preluare de la tastatura a celorlalte . } fclose(f) } Inserarea unor articole. astfel: se caută (cu un algoritm secvenţial. noul articol va fi inserat între două articole.cimp_1). { // articolele fisierului sînt in ordinea crescatoare a valorii cimpului 1 // citire nume fisier extern *) f=fopen(nume_fisier. în poziţia k. în general. Exemplu 9. Pentru o corectă exploatare ulterioară. scanf("%d ". toate articolele de la sfîrşitul fişierului pînă la articolul cu numărul relativ k. // pozitionare dupa ultimul // articol scris printf("Cimp 1: ").cimp_1).&art_nou. Se aplică în cazul în care articolele sînt scrise în fişier în ordinea crescătoare (descrescătoare) a valorilor unui anumit cîmp. apelînd funcţia fseek: fseek(f. while(!feof(stdin)) { // ----------------------------------------------// Preluare de la tastatura a celorlalte // cimpuri din articol // ----------------------------------------------printf("Cimp 1: ").SEEK_END).cimp_1). similar operaţiei de populare.0. scanf("%d ". // introducerea cimpului dupa care // sint sortate articolele scanf("%d ". se scrie în acces direct noul articol. Acest lucru poate fi realizat astfel: Adăugare la sfîrşit (extindere). // calculare numar de articole din fisier printf("\nCimp 1: ").&art.0. fseek(f. glisînd cu o poziţie spre dreapta. cu tranzacţii de la terminal. Exemplu 8.) poziţia k în care trebuie inserat noul articol. fclose(f) } Adăugarea de articole se realizează. În acest caz. Secvenţa generală pentru inserarea articolelor pe anumite poziţii. adăugarea trebuie să fie densă. se copiază. după ultimul articol scris.&art.

//articolul se va insera in pozitia k for(i=nr_art-1.art. eventual prin conversie. cîmpului respectiv i se va atribui valoarea citită în variabila independentă. // introducerea cimpului dupa care // sint sortate articolele scanf("%d ".cimp_1<art_nou.f). fwrite(&art_nou.". // -----------------------------// cautare articol de modificat // -----------------------------. Altfel. O variantă simplă este afişarea vechii valori. În aplicaţii se prevede posibilitatea modificării numai pentru acele cîmpuri a căror modificare este normală din punctul de vedere al problemei care se rezolvă.1. orice cîmp poate fi modificat. În cazul în care şirul introdus este vid (s-a apăsat numai ENTER). nu se modifică. } } else k=nr_art. se repoziţionează pe articolul respectiv cu fseek(f. se scrie articolul modificat.sizeof(tip_articol). pentru cîmpurile numerice. // articolul se adauga la sfirsitul fisierului fseek(f. cu funcţia fwrite.cod). O problemă importantă rămîne selectarea cîmpurilor care se modifică. printf("\nCimp 1: ").* material didactic pentru ID * 75 // cimpuri din articolul de adaugat // ----------------------------------// secventa de cautare a pozitiei // in care se va insera articolul } fseek(f. while((!feof(f))&&(art_existent. respectivul cîmp. în general. prin convenţie.SEK_SET).rite('Cimp 1: ') } fclose(f).SEK_SET). Din punct de vedere logic însă.&art_nou.sizeof(tip_articol).k*sizeof(tip_articol).f).sizeof(tip_articol). de la tastatură.1.cimp_1) fread(&art_existent.f).1.sizeof(tip_articol).f).sizeof(tip_articol). //afisare vechea valoare .i--) { fseek(f.ftell(f)-sizeof(tip_articol). Secvenţa generală pentru modificarea unui articol. // pozitionare pe inceput de fisier fread(&art_existent. . urmată de introducerea noii valori.sizeof(tip_articol).1. Din punct de vedere tehnic. fwrite(&art_existent. nu e nevoie sau e chiar imposibil să modificăm unele cîmpuri.f). } Modificarea valorii unor cîmpuri din articol se realizează în mai multe etape: se citeşte articolul care se modifică (fseek şi fread).*) fread(&art. Exemplu 10.1. printf("Codul: %d fflush(stdin). se modifică (în zona articol din memoria principală) cîmpurile cu valorile dorite.SEEK_SET).f).0. fread(&art_existent.cimp_1). în variabile independente de tip şir de caractere. introduse.i*sizeof(tip_articol). if(!feof(f)) { k=ftell(f)-sizeof(tip_articol).SEEK_SET).1. pentru fiecare articol în parte.i>=k.

fiecare articol este înscris în poziţia indicată de numărul său relativ predefinit. } O altă variantă se poate realiza prin folosirea unei machete de ecran în care se afişează valorile actuale ale fiecărui cîmp de modificat. Nomenclatorul este elaborat extern (automat sau neautomat). caz în care se menţine actuala valoare. Datele se preiau de la tastatură iar terminarea prelucrării este marcată standard.sizeof(tip_articol). La crearea iniţială. în continuare.1. 4. se poziţionează succesiv cursorul la începutul fiecărui cîmp. // afisare vechea valoare gets(dens). Fără a epuiza multitudinea soluţiilor de rezolvare a problemelor de gestiune a fişierelor care necesită actualizare.SEEK_SET).3. cu două răspunsuri posibile ale utilizatorului: <ENTER>.ftell(f)-sizeof(tip_articol). Pentru aceasta. Fie fişierul creat la exemplul 3.art. limitările de regăsire prin acces secvenţial sau relativ a articolelor în fişier reduc aria folosirii limbajului în probleme de gestiune. se prezintă cîteva soluţii posibile. trebuie proiectate structuri particulare de articole şi concepute operaţii de gestiune specifice. reprezentînd primul caracter al noii valori. 4. Teste de autoevaluare 4. Scrieţi un program care înregistrează în fişier producţia realizată de societatea comercială într-o anumită lună a anului.den). // rescriere articol modificat fwrite(&art. Marele inconvenient îl constituie lipsa accesului după cheie.". În cele ce urmează se analizează trei tipuri de probleme: probleme în care se utilizează asocierea externă a numărului relativ la articolul corespunzător (codificare prin număr relativ). // copiere noua valoare // ---------------------------------// Introducerea celorlalte cimpuri // din articol // ---------------------------------// repozitionare pe articol feek(f. Programul trebuie să ofere posibilitatea repetării operaţiei pentru mai multe produse şi diverse luni. cel care corespunde cel mai bine gestiunii în sistemele reale.art. Orice operaţie de regăsire în acces relativ presupune introducerea din exterior a numărului relativ.3.den). Codificarea externă prin numere relative Nomenclatorul de articole conţine numărul relativ al fiecăruia dintre ele. pentru un anumit produs.1. cods este de tip sir if(strlen(cods)) { art. probleme în care se utilizează asocierea internă a numărului relativ la articolul corespunzător iar acesta poate emana extern (se generează nomenclatoare după fiecare actualizare de fişier). Asigurarea ştergerii şi adăugării . probleme în care se utilizează extern coduri (chei) şi intern numere relative. // citire noua valoare. respectiv o tastă diferită de <ENTER>. // citire noua valoare if(strlen(dens) strcpy(dens.f).cod=atoi(cods). Algoritmi de prelucrare a fişierelor binare care necesită actualizare Prelucrarea fişierelor binare care necesită actualizare trebuie să asigure posibilitatea ştergerii articolelor şi să elimine riscul de suprascriere a articolelor adăugate. În orice situaţie.76 Algoritmi în programare gets(cods). // conversie din ASCII in binar printf("Denumire: %s .

pînă la întîlnirea primului articol cu IS=1 sau pînă se ajunge la sfîrşit de fişier. . Într-un sistem de programe. ajungîndu-se la forma din figura 4. orice operaţie de scriere a unui nou articol se tratează ca adăugare. citirea din fişier este permisă numai pentru articolele cu IS=1. Primul articol are structura char a[max]. În funcţie de valoarea lui nr se disting următoarele situaţii: . dacă vechiul articol din această poziţie are IS=1.dacă nr<dimensiune fişier. Preformarea presupune deschiderea fişierului ca nou (crearea unui fişier nou) şi scrierea unui număr de articole (la limită. zero) cu IS=0. Datorită faptului că fişierul se deschide ca existent.. Fiecărui articol din fişier îi corespunde cîte un octet în primul articol: articolului cu numărul relativ i îi corespunde octetul a[i]. scrieţi un program care creează şi populează un fişier organizat relativ cu articole referitoare la studenţii universităţii. Ea verifică dacă IS=1. Noul articol se scrie pe poziţia nr. Includerea operaţiei de preformare conduce la dispariţia distincţiei dintre populare şi adăugare. Structura articolului care include indicatorul de stare Indicatorul de stare (notat IS) poate lua una din cele două valori posibile (de exemplu 0 pentru articol inactiv – inexistent sau şters. are loc extinderea fişierului cu preformarea articolelor cu numerele relative cuprinse în domeniul dimensiune fişier. Scrierea în acces secvenţial se face fără verificare de existenţă.dacă nr>=FileSize(f). IS Articol propriu-zis Fig. unde max este o constantă care indică numărul maxim de articole pe care le poate avea fişierul pe durata . Teste de autoevaluare 5. Ea presupune citirea articolului şi dacă ştergerea este permisă (IS=1). Rescrierea realizează scrierea unui articol în poziţia ftell(f)-sizeof(tip_articol). Articolele cu IS=0 sînt ignorate. deschiderea cu modul wb a unui fişier se realizează o singură dată. Procedura face IS=1. Citirea în acces secvenţial analizează articolele începînd cu cel de la pointerul curent. operaţiile de acces la articole se realizează în următoarele condiţii: scrierea în fişier este permisă numai pentru articolele cu IS=0. Folosirea articolului zero ca tabelă de ocupare în fişier. 4. Fie structura logică de articol de la tema de autoevaluare 3. se citeşte articolul respectiv din fişier şi adăugarea este permisă numai dacă IS=0. Citirea în acces direct presupune furnizarea numărului relativ (nr). Considerînd numărul matricol cheie relativă. Ştergerea se realizează în acces direct. Scrierea în acces direct presupune furnizarea numărului relativ (nr) al articolului. Utilizarea ei se recomandă numai la popularea densă. Scrierea are loc în poziţia dată de pointerul curent. Cu această convenţie.* material didactic pentru ID * 77 controlate poate fi făcută în diverse moduri.7. Se remarcă faptul că scrierea în acces direct permite preformarea iniţială cu zero articole. se modifică indicatorul de stare (IS=0) şi se scrie articolul pe vechiul loc.nr-1. în procedura de preformare. Extinderea articolelor logice cu un indicator de stare (un octet). 1 pentru articol prezent).7.

Dacă articolul i este prezent.3. înainte de închiderea fişierului trebuie rescris articolul zero în fişier. nal este numărul articolelor libere şi al[i]. cu i=1. chiar cea iniţială. că se acceptă o codificare automată. În acest fel numărul maxim de articole ce pot fi adăugate în fişier se măreşte de 8 ori. asociindu-se astfel intern un număr relativ fiecărui articol.2. respectiv după ultimul articol (extindere). 4.. Ştergerea articolului i presupune verificarea existenţei sale (a[i]=1) şi realizarea operaţiei a[i]=0. al[nal] WORD WORD WORD .7. adică în alţi termeni. Adăugarea unui articol se realizează în condiţii diferite. Articolelor din fişier li se asociază structura din figura 4. dacă nu mai există articole libere în interior. a[i]=0.nal. În programele care realizează ştergeri sau/şi adăugări. De exemplu. Preformarea. Adăugarea unui articol i presupune verificarea inexistenţei lui (a[i]=0). a[i]=1.8. consultarea.. Dintre soluţiile posibile pot fi menţionate: Folosirea articolului zero pentru colectarea numărului articolelor libere. Numărul de articole libere ce pot fi gestionate în acest mod este limitat de descrierea articolelor principale ale fişierului.3. eventual la sfîrşit (extindere). modificarea şi ştergerea sînt similare celor prezentate în §4. constă în modul de realizare a adăugării secvenţiale.. pentru gestiunea articolelor libere Articolul 0 În această soluţie. operaţiile de acces la articole se realizează în următoarele condiţii: scrierea în fişier a articolului cu numărul relativ i este permisă numai dacă a[i]=0. citirea din fişier a articolului cu numărul relativ i este permisă numai dacă a[i]=1. Structura articolului zero. condiţiile de realizare sînt determinate de modul de acces folosit: secvenţial sau direct. Se pot concepe algoritmi prin care în tabela de ocupare în fişier fiecărui articol îi corespunde un bit. Această variantă presupune existenţa unei soluţii de gestiune a articolelor libere (în urma preformării sau a ştergerii logice). nal al[1] al[2] . dacă articolul . caz în care se adaugă noul articol în prima poziţie găsită disponibilă (IS=0). Adăugarea în acces secvenţial poate fi utilizată în două variante: Cu verificarea existenţei de articole libere. în loc de un octet. În ambele situaţii. Datorită restricţiei impuse pentru numărul de articole din fişier.1.78 Algoritmi în programare existenţei sale. după cum această operaţie are loc printre articolele existente. acest model de gestiune a articolelor este ineficient pentru multe probleme. reprezintă poziţiile relative ale articolelor libere. înscrierea articolului şi realizarea operaţiei a[i]=1.. se face în primele articole inactive. Deosebirea esenţială între această soluţie şi cea prezentată anterior. Adăugarea în acces secvenţial se bazează pe presupunerea că utilizatorul nu impune o corespondenţă prestabilită între conţinut şi numărul articolului. dacă articolul i este inactiv (inexistent sau şters).8. într-o structură de forma celei din figura 4. Codificarea internă prin numere relative Înscrierea articolelor în fişiere. Cu această structură. 4. WORD Fig. Utilizarea acestei modalităţi necesită încărcarea iniţială în memoria principală a articolului cu numărul relativ zero.. Soluţia prezintă avantajul timpului redus de căutare şi de atribuire a unei poziţii pentru noul articol. După fiecare sesiune de actualizare va trebui listat nomenclatorul de coduri interne (numere relative).

iar al[nal] primeşte ca valoare numărul relativ al articolului şters.. numit cheie. Ea poate fi asociată cu preformarea cu zero articole.3. Fără verificarea existenţei de articole libere.. pentru primul articol se poate accepta şi o dimensiune diferită – mai mare – dar nu cu mult mai mare şi oricum este o dimensiune stabilită de la început. Căutarea secvenţială a primului articol liber.. fără organizarea unei gestiuni a acestora. 4. Problema corespondenţei între chei şi numerele relative ale articolelor din fişierul de date se poate rezolva prin intermediul unui fişier binar suplimentar. pal ual 0 au 0 au . iar fiecare articol punctează pe următorul (au). Folosirea articolului zero ca început al unei liste simple (sau dublu) înlănţuite a articolelor libere. operaţia fiind posibilă la oricare din capetele listei. aceasta este soluţia cea mai simplă sub aspectul programării. La ştergerea logică a unui articol se realizează incrementarea valorii lui nal. eliminînd necesitatea unei structuri distincte a articolului zero şi operaţiile legate de întreţinerea colecţiei sau listei articolelor libere. într-o structură de principiu de forma celei din figura 4. caz în care articolul este adăugat direct la sfîrşit (extindere).3. se verifică dacă există articole libere (pal<>0) şi dacă există. 4. .9. care nu mai poate fi mărită la nevoie).9. Corespondenţa internă dintre chei şi numere relative Majoritatea aplicaţiilor de gestiune economică utilizează fişiere de date în care articolele trebuie regăsite după valorile unui cîmp de identificare. Această variantă este avantajoasă cînd are loc introducerea de la început a majorităţii articolelor. este de preferat ultima variantă atunci cînd timpul de căutare nu este prohibitiv. această soluţie permite gestionarea a 63 de articole libere (dacă se impune pentru articolul zero aceeaşi lungime ca şi pentru celelalte articole.* material didactic pentru ID * 79 principal are 128 de octeţi. O astfel de organizare se numeşte indexată.1. cu rol de tabelă de indexuri.3. Deşi mai costisitoare ca timp de căutare. . actualizîndu-se componenta articolului zero. fiecare sesiune de adăugare de noi articole fiind realizată prin extinderea fişierului. Numărul articolelor libere care pot fi gestionate în acest mod este oarecare.. articolul zero punctează pe primul (pal) şi pe ultimul (ual) articol liber. La ştergerea unui articol.10. Articolele fişierului tabelă de indexuri au structura din figura 4. În concluzie. Adăugarea în acces direct presupune o codificare anterioară (preluarea numărului relativ din nomenclatorul editat după fiecare creare/ adăugare secvenţială) şi se realizează identic cu operaţia de scriere directă prezentată în §4. se atribuie primul articol din listă articolului de adăugat. La adăugarea unui nou articol. Gestionarea articolelor libere prin liste În această soluţie. trebuie asigurată includerea sa în lista articolelor libere. 0 au Fig.

Eroarea de cheie invalidă poate apărea la tentativa de scriere a unui articol a cărui cheie este mai mică sau egală decît ultima înscrisă în fişierul de date. gestionată automat de funcţiile unei biblioteci specializate şi care este netransparentă utilizatorului (bibliotecă utilizator. sortate crescător după valorile cîmpului cheie. urmată de exploatarea în acces secvenţial. închiderea. Modificarea unor cîmpuri din articol se realizează în următoarele etape: citirea articolului de modificat (în acces secvenţial sau direct). Eroarea de cheie invalidă apare în cazul tentativei de rescriere a unui articol a cărui cheie este diferită de cea a articolului citit anterior. în orice moment.3. Adăugarea de articole se realizează utilizînd funcţia de scriere în acces direct. Articolele tabelei de indexuri sînt. . selecţie realizată pe baza valorilor cheii primului şi ultimului articol din grupul dorit.10. Procedurile realizează. cu procedura de rescriere. a cîmpurilor dorite. Cheie este un cîmp în care se memorează valoarea cheii articolului existent logic în fişierul de date. pe lîngă operaţiile asupra fişierului de date şi gestionarea automată a tabelei de indexuri: formarea numelui său extern. modificarea. Ea se realizează cu ajutorul funcţiei de citire în acces secvenţial. O parte dintre acestea nu-şi regăsesc corespondent în tabela de indexuri. Structura articolului din tabela de indexuri Indicatorul de stare (IS) are rol identic cu cel prezentat în §4. în zona articol corespunzătoare. Eroarea de cheie invalidă apare la tentativa de scriere a unui articol a cărui cheie este egală cu una din cele prezente în fişier. Orice operaţie de acces la articolele fişierului de date se realizează numai prin intermediul tabelei. deschiderea ca fişier nou sau vechi. rescrierea articolului. pînă la găsirea cheii ultimului articol dorit. Articolele sînt scrise cu ajutorul funcţiei de scriere în acces secvenţial. La rîndul ei. Operaţiile de gestiune ce se pot realiza cu fişierele de date astfel organizate sînt: Crearea în acces secvenţial presupune furnizarea articolelor sortate strict crescător după valorile cîmpului ales drept cheie. cu excepţia cîmpului cheie. asignarea numelui fizic la numele logic. Operaţiile de acces la nivel de fişier sînt deschiderea şi închiderea fişierului de date.80 Algoritmi în programare IS Cheie Număr relativ (nr) Fig. articolele fiind furnizate în orice ordine. Eroarea de cheie invalidă apare la tentativa de scriere a unui articol a cărui cheie este egală cu una deja existentă. nu face parte din limbaj). memorate logic contiguu în fişier. fiind considerate şterse. Accesul mixt presupune poziţionarea pe primul articol prin citirea în acces direct (sau poziţionare şi citire în acces secvenţial). deschiderea poate fi pentru creare (ca fişier nou) sau pentru consultare şi întreţinere (ca fişier vechi). sau pînă la sfîrşitul fişierului. Articolele pot fi furnizate în orice ordine a valorilor cheii. Articolele din fişierul de date sînt memorate aleator. Consultarea în acces secvenţial presupune regăsirea articolelor în ordinea strict crescătoare a valorilor cheii. care detectează şi sfîrşitul fişierului de date. Crearea în acces direct se realizează cu funcţia de scriere în acces direct.1. 4. al cărui număr relativ corespunzător este memorat în cîmpul nr. Consultarea în acces mixt permite selectarea unui grup de articole.

Operaţia nu trebuie precedată de citirea articolului şi returnează eroare în cazul furnizării unei chei inexistente în fişier. cu prototipul void new_index(char *nume). char den[35]. tabela de indexuri. cu prototipul void open_index(char *nume). float cant. cu extensia . accesibilă tuturor subprogramelor. şi deschide ca existentă tabela de indexuri. În aplicaţii. În general. Funcţia primeşte ca parametru numele extern al fişierului de date (nume) şi creează un fişier nou.11. fişierul de date este format din articole cu următoarea structură: cheie denumire preţ cantitate Fig.idx. Procedura returnează eroare în cazul tentativei de ştergere după ultimul articol existent. este preferabilă ştergerea în acces secvenţial. Exemplul următor descrie funcţii. //fisierul index //numele extern al fisierului index Pentru implementarea operaţiilor de gestiune specifice unui fişier organizat indexat sînt necesare următoarele subprograme: Funcţia de deschidere a tabelei index ca fişier nou. cu extensia . //tipul articol din fisierul de date typedef struct{ char is. Tipurile definite sînt următoarele: typedef struct{ char cheie[7]. vizualizarea articolului şi luarea unei decizii în condiţii de siguranţă. deoarece permite (datorită citirii care o precede). float pu. Exemplu 11.idx. } ART_INDEX. tipuri de date şi variabile publice pentru prelucrarea unui fişier organizat indexat. . În acest exemplu. char cheie[7]. Funcţia primeşte ca parametru numele extern al fişierului de date (nume). //tipul articol din tabela de indexuri FILE* ind.* material didactic pentru ID * 81 Ştergerea în acces secvenţial elimină articolul curent din fişierul de date. char nume_index[20]. long nr_rel. modificînd corespunzător structura articolului. articolul trebuie mai întîi identificat printr-o citire (în acces secvenţial sau direct) sau prin poziţionare. Ştergerea în acces direct elimină articolul a cărui cheie este precizată. 4. } ARTICOL. Funcţia de deschidere a tabelei de indexuri. Structura articolului din tabela de indexuri Exemplul poate fi adaptat pentru orice altă structură. pentru consultare şi întreţinere. fişierul index va fi o variabilă globală. Pentru aceste exemplu.

este returnată valoarea 0. Funcţia pentru scrierea în acces secvenţial a unui articol în fişierul de date. cu prototipul void closeindex(). în caz contrar. cu prototipul int WriteKey(fisier f. Funcţia pentru citirea în acces direct a unui articol din fişierul de date. de la poziţia curentă a pointerului de fişier şi apoi este citit articolul cu numărul relativ dat de cîmpul nr_rel al articolului citit din fişierul de indexuri.0. În cazul în care cheia este mai mică sau egală cu a ultimului articol din tabelă. Funcţia realizează închiderea tabelei de indexuri asociate fişierului de date. precum şi cheia articolului care va fi citit şi returnează .articol *a. în caz contrar. dacă acest lucru es- .0. dacă acest lucru este posibil. articolul ce va fi scris. Prin apelul repetat al funcţiei ReadSec. dacă acest lucru este posibil. Funcţia pentru scrierea în acces direct a unui articol în fişierul de date.0. cu prototipul int ReadSec(fisier f.1. Dacă. Funcţia are ca parametri numele intern al fişierului. Dacă cheia este găsită. în caz contrar. Funcţia are ca parametri numele intern al fişierului de date.articol a). şi returnează .articol a).char *Key). altfel returnează valoarea 0. dacă citirea a fost posibilă. Funcţia are ca parametri numele intern al fişierului de date şi adresa unde se depune articolul citit. cu prototipul int ReadKey(fisier f. Funcţia apelează modulul de căutare binară în tabela de indexuri a cheii Key. . dacă tabela de indexuri are poinetrul plasat înaintea primului articol. în tabela de indexuri.articol *a). dacă acest lucru este posibil şi returnează . . citeşte articolul cu numărul relativ corespunzător articolului din tabela de indexuri şi returnează valoarea 1. adresa unde se depune articolul citit. Funcţia adaugă un articol în fişierul de date. a cărei cheie este mai mare decît cele existente. . dacă citirea a fost posibilă.1. Funcţia pentru citirea în acces secvenţial a unui articol din fişierul de date. SeekKey. Funcţia are ca parametri numele intern al fişierului de date şi articolul ce va fi scris. atunci citirea nu este posibilă şi funcţia returnează valoarea 0. pointerul de fişier indică sfîrşitul de fişier. dacă scrierea a fost posibilă. corespunzătoare situaţiei în care scrierea nu este posibilă. cu prototipul int WriteSec(fisier f. concomitent cu extinderea tabelei de indexuri cu o nouă înregistrare.82 Algoritmi în programare Funcţia de închidere a tabelei de indexuri. astfel: este citit un articol din tabelă.1. sînt obţinute articolele din fişierul de date în ordinea strict crescătoare a valorii cheii. Citirea unui articol din fişierul de date este realizată prin intermediul tabelei de indexuri.

a. Funcţia pentru ştergerea în acces direct a unui articol. Funcţia realizează sortarea articolelor tabelei de indexuri. indicatorul de stare este setat pe 0 şi apoi se elimină articolul din tabelă. Iniţial. Funcţia primeşte ca parametru de intrare cheia articolului care va fi şters şi returnează . Funcţia adaugă un articol la sfîrşitul fişierului de date. . Iniţial. Funcţia returnează valoarea 0. dacă articolul a fost găsit. . în caz contrar.* material didactic pentru ID * 83 te posibil. prin apelul funcţiei Sort.0.1. analog ştergerii în acces secvenţial. Cheia acestuia. Funcţia pentru căutarea articolului cu cheie dată. Căutarea cheii în tabela de indexuri pentru stabilirea posibilităţii scrierii este realizată prin apelul funcţiei SeekKey.cheie. Funcţia returnează valoarea 0. Funcţia şterge logic articolul curent din fişierul de date. cu prototipul void Sort(). poate avea orice valoare (care nu există deja în tabela de indexuri). În cazul în care cheia articolului de scris este deja prezentă în tabela de indexuri. corespunzătoare situaţiei de eroare. cu prototipul int SeekKey(char *Key) Funcţia primeşte ca parametru de intrare cheia articolului căutat şi returnează . dacă scrierea a fost posibilă.0. corespunzătoare situaţiei de eroare. şi returnează . cu prototipul int DeleteSec(). tabela se extinde cu un nou articol şi apoi este reordonată (prin apelul funcţiei Sort). crescător după cîmpul cheie. Funcţia pentru ştergerea în acces secvenţial a unui articol. în caz contrar.1. cu prototipul int DeleteKey(char *Key). dacă pointerul curent al tabelei de indexuri indică marcatorul de sfîrşit de fişier. . cu eliminarea articolelor cu stare 0. articolul nu este scris în fişier şi funcţia returnează valoarea 0. Funcţia şterge logic din fişierul de date articolul a cărui cheie este primită ca parametru.1. dacă ştergerea a fost posibilă. dacă ştergerea a fost posibilă. Ştergerea este realizată fizic din tabela de indexuri. Funcţia returnează . . în caz contrar.0.1. Ştergerea se realizează fizic în tabela de indexuri. precum şi ştergerea fizică a tuturor articolelor cu indicator de stare 0. dacă Key nu este regăsită în tabela de indexuri. sînt utilizate următoarele funcţii auxiliare: Funcţia pentru sortarea tabelei de indexuri. Pentru implementarea funcţiilor descrise mai sus. Căutarea este realizată prin apelul funcţiei SeekKey.

0. for(j=i+1.j<n.SEEK_SET).b.cpp).i*sizeof(a).sizeof(a). Dacă articolul cu cheia Key este găsit.ind). Funcţia realizează căutarea binară în tabela de indexuri. rewind(ind). fread(&a.j++) { fseek(ind1.sizeof(a).sizeof(a).1.j*sizeof(a). fisier ind1. while(!feof(ind1)) { if(a.idx".0. în caz contrar.ind1).ind). fseek(ind1. if(strcmp(a. ind=fopen(nume_index. float cant.i<n-1.1.1. } ART_INDEX.SEEK_SET). după cîmpul cheie.cheie. for(i=0.h> #include <string."wb+").ind1).i*sizeof(a). fread(&a.1. ind1=fopen("temp. long nr_rel. fread(&a. long n=ftell(ind1)/sizeof(a).is)fwrite(&a.sizeof(a).sizeof(a).ind1). fwrite(&a. while(!feof(ind)) { if(a. fread(&a. } fclose(ind).i++) { fseek(ind1.1. } .j*sizeof(a).ind1).1."wb+"). funcţia lasă pointerul de fişier pe acel articol (o citire secvenţială ulterioară determinînd obţinerea articolului corespunzător din fişierul de date). } } } rewind(ind1).is)fwrite(&a. char cheie[7]. fread(&b. //fisierul index //numele extern al fisierului index void Sort() { ART_INDEX a.sizeof(a). aceste text va fi considerat salvat în fişierul index1.1.ind1).SEEK_END). //tipul articol din fisierul de date typedef struct{ char is.1.84 Algoritmi în programare . long i. } fclose(ind1). fread(&a.sizeof(a).ind1). char nume_index[20]. #include <stdio.sizeof(a).SEEK_SET).b.j.cheie)>0) { fseek(ind1. float pu.sizeof(a).SEEK_SET).1.idx").h> #define fisier FILE* typedef struct{ char cheie[7]. char den[35].ind). //tipul articol din tabela index fisier ind.sizeof(a). fseek(ind1. Exemplu Textul sursă care implementează toate aceste funcţii C este prezentat în continuare (în exemplele care urmează.1. } ARTICOL. remove("temp. fwrite(&b.ind1).

SEEK_SET). if(SeekKey(Key)) { fread(&a1. return gasit.sizeof(a1).* material didactic pentru ID * 85 /* cautarea articolului cu cheia Key si plasarea pointerului de fisier in tabela de indexuri pe articolul respectiv*/ int SeekKey(char *Key) { long ls=0. else { fseek(f.ind).ARTICOL *a.m*sizeof(a). ld=n-1.Key)==0) gasit=1. . fseek(ind.idx"). n.nr_rel*sizeof(*a).1. } void open_index(char *nume) { strcpy(nume_index. ind=fopen(nume_index. } int WriteSec(fisier f.1."rb+"). strcat(nume_index.ARTICOL a) { ART_INDEX a1.1. } void close_index() { fclose(ind). if(feof(ind))r=0.1.sizeof(a).sizeof(*a). while((ls<=ld)&&(!gasit)) { m=(ls+ld)/2. if(strcmp(a.SEEK_SET).cheie. m. else ls=m+1.ind). fread(&a1. ind=fopen(nume_index. } return r. int r."wb+"). nl.0.".sizeof(*a). } void new_index(char *nume) { strcpy(nume_index.nume). r=1. r=1.nr_rel*sizeof(*a). fseek(ind. } int ReadSec(fisier f. ai.char *Key) { ART_INDEX a1. n=ftell(ind)/sizeof(ART_INDEX). fread(a.ind).idx"). fread(a. } if(gasit) fseek(ind.nume). ART_INDEX a.a1. int gasit=0. } int ReadKey(fisier f. ld.sizeof(a1). int r.Key)>0) ld=m-1.SEEK_SET).m*sizeof(a).ARTICOL *a) { ART_INDEX a1. } else r=0. fseek(f.f).cheie.SEEK_END).a1. return r. strcat(nume_index.". fread(&a. else if(strcmp(a. int r.f).SEEK_SET). long n.1.

else { ai. else r=0.pos.sizeof(a1). } int WriteKey(fisier f.sizeof(ai).nr_rel=n1.1.sizeof(a1).(n-1)*sizeof(a1).ind). strcpy(a1.a.1.sizeof(a).SEEK_END). if(SeekKey(Key)) r=0. fwrite(&a.0.cheie.a. r=1. r=1. fwrite(&a1. fwrite(&a. fseek(ind. fread(&a1.sizeof(a).is=0.f).1.86 Algoritmi în programare fseek(ind.ind).is=1.sizeof(a1). n=ftell(ind)/sizeof(a1).1. Scrieţi programul C pentru crearea în acces direct unui fişier binar cu articole avînd structura: cheie denumire preţ cantitate .ind). } int DeleteSec() { ART_INDEX a1.1.1. } } else r=0.0.1. return r. if(strcmp(a1. r=1. } return r. if(n>0) { fseek(ind. fwrite(&ai.SEEK_END). if(SeekKey(Key)) r=DeleteSec(). return r.SEEK_END).ARTICOL a) { char Key[7]. if(feof(ind)) r=0. ART_INDEX a1.is=1. } return r. fseek(f.cheie. } int DeleteKey(char *Key) { int r.0.0. Sort().sizeof(a1). ai. fread(&a1. long pos=ftell(ind).SEEK_END). a1.nr_rel=n. strcpy(ai.ind). } Exemplu 12. else { fseek(ind. else { a1. n1=ftell(f)/sizeof(a). strcpy(Key.cheie.cheie).0. a1.SEEK_SET). fwrite(&a1. fseek(ind.f).cheie)>0) r=0. fseek(f. Sort(). long n.SEEK_END).ind).a.SEEK_SET).cheie).cheie).a. n=ftell(f)/sizeof(a).

char r. fflush(stdin).cant).cheie). clrscr().nume1[20]. if(WriteKey(f. getch(). fisier f."wb+"). Se presupune creat şi populat fişierul de date din exemplul anterior.&a. gets(nume).* material didactic pentru ID * 87 Datele sînt preluate de la tastatură pînă la apăsarea combinaţiei CTRL/Z pentru cîmpul cheie. fflush(stdin). else printf("Exista articol"). int i. new_index(nume).". char x[7]. Scrieţi programul C pentru ştergerea acelor articole ale căror chei sînt introduse de la tastatură. } else open_index(nume). f=fopen(nume1. close_index(). clrscr(). gets(a.cpp" #include <conio. while(!feof(stdin)) { printf("Denumire produs:"). fisier f. .cpp" #include <conio. gets(a. #include "index1. char nume[20]. } fclose(f). getch().&a. fflush(stdin). } Exemplu 13.nume). printf(" numele fisierului de date in care adaugati:"). #include "index1.h> void main() { ARTICOL a. char nume[20].pu). fflush(stdin). printf("Introduceti cheia:"). Încheierea introducerii datelor este marcată standard.a)) printf("Articol adaugat"). printf("\nAdaugarea in acces direct dupa cheie\n")."rb+"). strcpy(nume1. scanf("%f". scanf("%f". if(f==NULL) { printf("Fisierul va fi creat"). printf("Cantitate:"). f=fopen(nume1. clrscr(). Fişierul creat este organizat indexat. printf("Pret produs:").den). char Key[7]. gets(a. strcat(nume1.h> void main() { ARTICOL a. printf("Introduceti cheia:").nume1[20].dat").cheie).

} else printf("Nu exista articol"). if(f==NULL) printf("Fisierul nu exista!!"). } else printf("Stergerea nu a fost efectuata"). printf("\nDenumire produs:").den). . strcat(nume1.cheie).den). if(f==NULL) printf("Fisierul nu exista!!")."rb+").2f\n".dat"). r=getch(). fisier f.Key)) { printf("Articolul:\n"). gets(Key).cpp" #include <conio. printf("Introduceti cheia:"). else { open_index(nume). fflush(stdin). fflush(stdin). #include "index1. fflush(stdin).&a)) { printf("Cheie:"). Scrieţi programul C pentru afişarea informaţiilor memorate în fişierul creat mai sus. f=fopen(nume1. char nume[20]. gets(a. printf("Doriti stergerea?(D/Altceva)"). printf("\nStergerea in acces direct dupa cheie\n").nume).cant). gets(nume).&a. else { open_index(nume). close_index(). } fclose(f). clrscr().cheie).88 Algoritmi în programare } printf(" numele fisierului de date din care stergeti:").pu). printf("Cantitate:%8. puts(a.h> void main() { ARTICOL a. if(r=='D') { i=DeleteKey(Key). printf("Introduceti cheia:"). strcpy(nume1. while(!feof(stdin)) { if(ReadKey(f.nume1[20]. getch().".a. gets(nume). printf(" numele fisierului de date care este consultat:"). printf("Stergere efectuata"). while(ReadSec(f. printf("Denumire:%20s\n". fflush(stdin). f=fopen(nume1.dat"). clrscr().".nume). char x[7].2f\n\n". Articolele sînt afişate în ordine crescătoare a cîmpului cheie. puts(a. } Exemplu 14. strcat(nume1. strcpy(nume1.a.a. getch()."rb+"). printf("Pret:%7.

Metoda se poate aplica numai fişierelor de dimensiune mică. getch(). presupune memorarea întregului fişier întrun vector de articole. Exemplu 15. În cazul în care cheia de sortare este formată dintr-un singur cîmp din cadrul articolului. Compararea pentru sortare se va realiza pe cîmpul cheie de sortare. Indiferent de modul utilizat. forma canonică a cheii de sortare după nume şi prenume este dată de lungimea efectivă a fiecărei date de tip şir. în forma canonică. prin inserţie etc. printf("%8. float medie. close_index(). recrearea fişierului pe disc. într-o structură internă de date (vector.pu). declarate de tip şir de caractere. getch(). în care se construieşte cheia de sortare. alcătuind forma canonică a cheii de sortare. numită cheie de sortare.2%f\n". alcătuind. sortarea poate fi realizată printr-unul din algoritmii cunoscuţi pentru masivele de date: sortare prin interschimbare.a. printf("Cantitate:").2f\n\n".cant). Sortarea unui fişier se poate realiza cu aducerea lui integrală în memorie (sortare în memorie) sau cu aducerea în memorie a cîte unui articol (sortare "direct pe disc"). Dacă pentru sortarea simplă cheia de sortare poate fi însuşi cîmpul din articol. Această metodă poate fi implementată în mai multe variante: Sortarea cu vehicularea întregului articol. Atenţie: datorită dimensiunii mari a fişierelor de date şi dimensiunii limitate a memoriei.4. Juxtapunerea cîmpurilor (nu neapărat adiacente în cadrul articolului) se realizează pe lungimea efectivă a lor. operaţia se numeşte sortare simplă. char nume_student[30]. Sortarea în memorie este o metodă rapidă şi presupune: citirea întregului fişier în memoria principală.a. aceasta metodă este contraindicată. printf("7. Sortarea cu vehicularea întregului articol. prin juxtapunere. } fclose(f). De exemplu. dacă NUME şi PRENUME sînt două cîmpuri distincte. pentru cea multiplă este necesară o zonă auxiliară de memorie. . operaţia nu este necesară dacă se foloseşte arbore de sortare). } } 4. } STUDENT. typedef struct { char grupa. sortarea efectivă după cheia de sortare (în cazul folosirii unui vector.* material didactic pentru ID * 89 printf("Pret produs:"). Sortarea fişierelor binare memorate dens Operaţia de sortare a unui fişier binar presupune aranjarea articolelor în ordinea crescătoare (descrescătoare) a valorilor unei zone. însă interschimbarea se realizează la nivelul întregului articol. cheia de sortare. arbore bina de sortare). Sortarea multiplă presupune aranjarea articolelor după valorile a două sau mai multe cîmpuri. prin selecţie.

g=fopen("STUDENTS.i++) fwrite(&x[i].i++) { fread(&y.j. rezultînd în final ordinea în care articolele vor fi scrise în fişier.j<n. //rescrierea fiierului pe disc for(i=0. STUDENT x[250].DAT". //sortarea for(i=0. fseek(f. float medie.90 Algoritmi în programare void main() { FILE* f.i<n. n:=ftell(f)/sizeof(STUDENT).i<n. presupune memorarea într-un vector numai a valorii cheii de sortare. Deoarece articolele. // citirea fisierului initial in memorie for(i=0. } rewind(f).sizeof(STUDENT).medie. // ----. Exemplu 16.i++) fread(&x[i]. int i. } CHEIE. //nu se poate folosi atribuirea mereu x[y]:=aux. în acces secvenţial. f=fopen("STUDENT. void main() { FILE *f. Cheile de sortare şi indexurile pot fi memorate în vectori distincţi sau într-unul singur. împreună cu numărul relativ al articolului din fişierul iniţial (indexul).1. int i. char nume_student[30].n. fseek(f."rb").f).DAT"."rb+").index=i. rewind(f). typedef struct { char grupa. rewind(f). fişierul sortat va fi creat cu un alt nume fizic. aux. } Sortarea cu vehicularea cheii şi indexului. preluînd articolele din fişierul iniţial. f=fopen("STUDENT.j.i++) for(j=i+1.medie=y. } STUDENT. Sortare cu vehicularea cheii şi indexului. sînt rezidente pe disc.*g.0. STUDENT y.i<n.medie>x[j].f). CHEIE aux. cu elemente de tip articol. x[i]. n:=ftell(f)/sizeof(STUDENT).0. în întregime. Interschimbarea se va realiza la nivelul cheii de sortare.medie) { aux:=x[i]. fclose (f).sizeof(STUDENT).SEEK_END). x[i]. //interschimbarea articolelor x[i]:=x[j].j++) if(x[i].sizeof(STUDENT). în acces direct.1.1.incarcare chei si indexuri in memorie ----for(i=0."wb").n. int index.x[250]. } .i<n-1.SEEK_END).f).DAT". typedef struct { float medie.

j<n.j++) if(x[i]>x[j]) { aux=index[i]. } fclose(f).f).DAT".g).j.DAT".SEEK_SET). dar interschimbarea se efectuează numai pentru indexuri.f). prin eliminarea interschimbării valorilor cheii de sortare (mai ales cînd aceasta este multiplă). n:=ftell(f)/sizeof(STUDENT). index[i]=i.i<n.medie>x[j].j++) if(x[i]. // -----------------------------------------for(i=0. float x[250].* material didactic pentru ID * 91 // ----.i<n-1. void main() { FILE *f.1."wb").i<n.i++) for(j=i+1. int index[250]. rewind(f). x[j]:=aux } // ----."rb"). Sortare cu vehicularea indexului.index*sizeof(STUDENT).x[i]. fwrite(&y.i++) { fread(&y. g=fopen("STUDENTS. STUDENT y.0. x[i]:=x[j].medie. rename("STUDENTS.i<n-1. typedef struct { char grupa. Exemplu 17. unlink(f). fread(&y.sortare vector de chei -------------------for(i=0. Comparaţiile se realizează pentru valorile cheii.j<n.DAT").i++) { fseek(f.n."STUDENT. f=fopen("STUDENT. Valorile cheii de sortare şi numărului relativ corespunzător indexului se memorează în vectori distincţi. sortat ----------------for(i=0. index[j]=aux } . float medie. int i.*g.1. Se va crea un alt fişier fizic.sizeof(STUDENT). char nume_student[30]. fseek(f. } Sortarea numai cu vehicularea indexului este o metodă mai bună decît precedenta.creare fisier nou.sizeof(STUDENT).sizeof(STUDENT).medie) { aux=x[i]. x[i]=y. } // -----------------------------------------for(i=0. index[i]=index[j].SEEK_END).1. fclose(g). }STUDENT.i++) for(j=i+1. deoarece micşorează timpul de execuţie.DAT".

1. } while(vb). if(x.f).f).index[i]*sizeof(STUDENT).1.sizeof(STUDENT).f).medie>y.j<nr_art.SEEK_SET). fread(&y.sizeof(STUDENT)*i.sizeof(STUDENT). Timpul de prelucrare va fi substanţial mai mare decît la metodele de sortare în memorie.1.SEEK_SET). for(i=0.sizeof(STUDENT)*i.1. fread(&y.medie) { fseek(f.sizeof(STUDENT)*j. } fclose(f). fwrite(&y.i++) { fseek(f.sizeof(STUDENT)*j. if(x. } } } Teste de autoevaluare 6.1.1. (Acest test trebuie rezolvat înainte de a putea rezolva testul 3) .f). deoarece operaţiile de intrare/ieşire sînt costisitoare din punct de vedere al resursei timp calculator.medie>y.i<n. alfabetic. Scrieţi un program care sortează fişierul descris la testul de autoevaluare 3 după următoarele criterii: facultate. În acest caz.i<nr_art-1. fseek(f. Se poate aplica oricare din algoritmii de sortare cunoscuţi.SEEK_SET).SEEK_SET). fwrite(&y. fread(&x. fclose(g) } Sortarea pe disc se aplică fişierelor mari. for(i=0.1.sizeof(STUDENT). Exemplu 19. fread(&y.sizeof(STUDENT). operaţia se va realiza "direct" pe mediul magnetic.1. vb=1.sizeof(STUDENT).sizeof(STUDENT).sizeof(STUDENT)*i.f).g). fwrite(&y. cu aducerea în memorie doar a două articole (pentru comparaţii) şi scrierea în acces direct în acelaşi fişier.sizeof(STUDENT)*i. la care este imposibilă aducerea în memoria principală chiar şi a minimului de informaţii necesare sortării.i++) { fseek(f.f).f).1. grupă.i++) { fseek(f.1. fwrite(&x. cu menţiunea că indicii i şi j vor fi utilizaţi pentru controlul numărului relativ al articolelor în fişier. Sortare prin selecţie.f).sizeof(STUDENT).SEEK_SET). fwrite(&x. Exemplu 18.sizeof(STUDENT).sizeof(STUDENT).f). do { vb=0.j++) { fseek(f. fread(&x.SEEK_SET).SEEK_SET).i<nr_art-1. Sortarea prin interschimbare.sizeof(STUDENT).medie) { fseek(f. for(j=i+1.92 Algoritmi în programare // ------------------------------------------for(i=0. prin utilizarea numărului relativ al articolelor prelucrate. an de studiu.

Cîmpul poartă denumirea de cheie de interclasare. Fişier 2 Interclasare 3 …………………………………………. din două sau mai multe mulţimi ordonate. pe baza căruia se va realiza. în final. Fişier final Fişier n-1 Fig. Fişier 1 Interclasare 2 ……………………….g). . "wb"). h=fopen(nume_fisier_iesire. reunirea acestora într-unul singur. Condiţia apriori interclasării este ca toate fişierele parţiale să fie sortate după valorile aceluiaşi cîmp.12).. while((!feof(f)&&(!feof(g))) if(art_1. g=fopen(nume_fisier_intrare_2. operaţia de interclasare. se obţine o nouă mulţime. 1 2 3 4 n Interclasare 1 ………. Interclasarea fişierelor binare memorate dens Interclasarea este operaţia prin care.c>art_2.. "rb")... considerate populate dens.. { //--------------------------------//citire nume externe ale fisierelor //--------------------------------f=fopen(nume_fisier_intrare_1. Interclasarea a n fişiere se poate realiza simplu prin aplicarea de n-1 ori a operaţiei de interclasare a două fişiere (figura 6. prin comparări succesive. Se prezintă structura principială a unui program pentru interclasarea a două fişiere binare.h). fread(&art_1. 4. .. "rb"). celelalte (împreună cu fişierele iniţiale) se şterg.. create simultan pe submulţimi de mai mulţi utilizatori şi necesitînd.sizeof(tip_articol).1. ordonată după acelaşi criteriu. Interclasare 3 …………………………………………………………………………………………………………. Interclasarea fişierelor apare ca necesitate în aplicaţiile economice. din care numai ultimul se păstrează.c) { fwrite(&art_1.12. Fişier 3 .* material didactic pentru ID * 93 4. Dimensiunea fişierului rezultat este suma dimensiunilor fişierelor iniţiale. Interclasarea a n fişiere Se obţin astfel n-1 fişiere intermediare (fişier i). fread(&art_2.. mai ales în faza de post-populare a fişierelor mari de date. fie în finalul procesului. Exemplu 20.5. fie la sfîrşitul fiecărei etape intermediare (recomandat).1. Interclasarea a două fişiere este similară operaţiei aplicate pentru doi vectori. Cheile de interclasare se află în cîmpul c aparţinînd articolelor art_1 şi art_2.sizeof(tip_articol).sizeof(tip_articol). corespunzătoare fişierelor de intrare f şi g.f)..1.

94

Algoritmi în programare

fread(&art_1,sizeof(tip_articol),1,f); } else { fwrite(&art_2,sizeof(tip_articol),1,h); fread(&art_2,sizeof(tip_articol),1,g); } while(!feof(f)) { fwrite(&art_1,sizeof(tip_articol),1,h); fread(&art_1,sizeof(tip_articol),1,f); } while(!feof(g)) { fwrite(&art_2,sizeof(tip_articol),1,h); fread(&art_2,sizeof(tip_articol),1,g); } fclose(f); fclose(g); fclose(h) }

4.6. Prelucrarea masivelor memorate în fişiere binare Una din aplicaţiile des întîlnite în lucrul cu fişiere este memorarea masivelor de date de dimensiuni foarte mari, care fac imposibilă aducerea lor integrală în memoria internă. Problema principală a prelucrării masivelor (vectori, matrice etc.) memorate în fişiere binare, o constituie determinarea poziţiei unui anumit element de masiv în cadrul fişierului. Indiferent de numărul de dimensiuni ale masivului şi de modalităţile de memorare a elementelor sale în cadrul fişierului, legătura între elementul de masiv care se referă şi numărul relativ al articolului care îl conţine se realizează pe baza funcţiei rang. În cazul masivelor memorate în fişiere, prelucrarea acestora depinde de unele caracteristici particulare: numărul de dimensiuni ale masivului; ordinea de memorare în fişier (în ordine lexicografică sau invers lexicografică); modul de memorare (dens sau nedens); ordinea de parcurgere a masivului. 4.6.1. Prelucrarea vectorilor De regulă, vectorii se memorează dens. Numărul relativ al articolului depinde de rangul elementului în cadrul vectorului, astfel: nr_relativ = rang(xi)+1 = i+1, pentru i=0..n-1, dacă articolul cu numărul relativ 0, fie nu este utilizat (caz în care dimensiunea vectorului este n = dimensiune fişier-1), fie memorează numărul efectiv de componente ale vectorului; nr_relativ = rang(xi) = i, pentru i=0..n, dacă vectorul se memorează începînd cu primul articol (caz în care dimensiunea vectorului este n =dimensiunea fişierului). Exemplu 21. Să se determine media aritmetică a elementelor unui vector foarte mare, memorat într-un fişier binar.
#include<stdio.h> void main() { FILE* vector; float element, medie; long i,n; vector=fopen("VECTOR.DAT","rb"); fseek(vector,0,SEEK_END); n=ftell(f)/sizeof(float);

* material didactic pentru ID *

95

rewind(f); medie=0; for(i=0;i<n;i++) { fread(&element,sizeof(float),1,vector); medie+=element; } medie/=n; printf("\nMedia: %7.3f",medie); fclose(vector); }

4.6.2. Prelucrarea matricelor O matrice poate fi memorată într-un fişier binar nedens (similar memorării în MP) sau dens, în ordine lexicografică sau invers lexicografică. Numărul relativ al elementului aij se determină pe baza funcţiei rang, astfel: rang(aij) = i * nr_coloane + j, în cazul memorării lexicografice, unde nr_coloane este fie numărul coloanelor efective (populare densă), fie numărul coloanelor rezervate (populare nedensă); rang(aij) = j * nr_linii + i, în cazul memorării invers lexicografice, unde nr_linii este fie numărul liniilor efective (populare densă), fie numărul liniilor rezervate (populare nedensă). Fie m şi n numărul liniilor, respectiv coloanelor efective şi mr şi nr numărul liniilor, respectiv coloanelor rezervate (mr şi nr corespund dimensiunilor maxime din declaraţia unui masiv aflat în memoria principală). Pentru ca fişierul să conţină informaţii complete despre matrice, trebuie să memoreze, pe lîngă elementele ei, şi: m (sau n), în cazul memorării dense. Cînd se memorează m, n se determină împărţind dimensiunea fişierului (mai puţin primul articol, unde se află m) la m; cînd se memorează n, m se determină împărţind dimensiunea fişierului (mai puţin primul articol, unde se află n) la n. Funcţia rang depinde de m sau n, după cum matricea este memorată invers lexicografic sau lexicografic; n şi nr, în cazul memorării nedense în ordine lexicografică. m se determină împărţind dimensiunea fişierului (mai puţin primele două articole, unde se află n şi nr) la nr, iar mr nu are relevanţă. Funcţia rang depinde de nr; m şi mr, în cazul memorării nedense în ordine invers lexicografică. N se determină împărţind dimensiunea fişierului (mai puţin primele două articole, unde se află m şi mr) la mr, iar nr nu are relevanţă. Funcţia rang depinde de mr. Funcţia rang se calculează şi se utilizează numai dacă problema de rezolvat implică parcurgerea matricei în altă ordine decît cea în care este memorată în fişier, deci consultarea acestuia se realizează în acces direct. Exemplu 22. Să se afişeze elementul maxim de pe fiecare coloană a unei matrice de dimensiuni m x n, memorate dens, într-un fişier binar, în ordine lexicografică. Primul articol conţine numărul de coloane. Observaţie: primul articol are dimensiune diferită de celelalte: numărul de coloane este de tip întreg iar elementele matricei sînt reale. Din dimensiunea totală a fişierului, primii sizeof(int) octeţi sînt ocupaţi de numărul de coloane, restul constituie matricea propriu zisă. La calcularea poziţiei unui element în matrice trebuie ţinut cont de faptul că matricea nu începe la începutul fişierului ci după sizeof(int) octeţi.
#include<stdio.h> void main() { FILE *f;

96

Algoritmi în programare

float max, element; long i,j,r,m,n; f=fopen("MATRICE.DAT", "rb"); fread(&n,sizeof(int),1,f); //citire numar de coloane fseek(f,0,SEEK_END); m=(ftell(f)-sizeof(int))/(sizeof(float)*n); for(j=0;j<n;j++) { //pozitonare pe primul element din coloana j fseek(f,j*sizeof(float)+sizeof(int),SEEK_SET); fread(&element,sizeof(float),1,f); max=element; for(i=1;i<m;i++) { r=i*n+j; //rangul elementului in matrice fseek(f,r*sizeof(float)+sizeof(int),SEEK_SET); fread(&element,sizeof(float),1,f); if(element>max) max=element; } printf("\Maximul pe coloana %2d este %7.3f",j,max); } fclose(f); }

Exemplu 23. Să se determine elementul maxim de pe fiecare coloană a unei matrice de dimensiuni m x n, memorată nedens într-un fişier binar, în ordine lexicografică. Primele două articole conţin numărul de coloane efective şi, respectiv, numărul rezervat de coloane. Rezolvarea este similară cu exemplul anterior, cu următoarele modificări: din dimensiunea totală a fişierului, primii 2*sizeof(int) octeţi sînt ocupaţi de dimensiunile matricei (număr de coloane efectiv respectiv rezervat), iar restul constituie matricea propriu zisă. La calcularea poziţiei unui element în matrice trebuie ţinut cont de faptul că matricea nu începe la începutul fişierului ci după 2*sizeof(int) octeţi. La calcularea rangului unui element (şi implicit a poziţiei sale în fişier) se foloseşte numărul de coloane rezervare, nu numărul de coloane efective. Teste de autoevaluare 7. Scrieţi un program care determină produsul scalar dintre doi vectori cu elemente de tip float memoraţi în două fişiere binare. 8. Scrieţi un program care determină produsul dintre un vector şi o matrice, ambele masive fiind memorate în fişiere binare (ambele au elemente de tip float). 9. Scrieţi un program care determină produsul dintre două matrice cu elemente de tip float, memorate în fişiere binare. Teme 1. Alegeţi o structură de date care să descrie cît mai bine produsele aflate în vînzare într-un magazin de dimensiuni mici. Definiţi tipul articol corespunzător în limbajul C. 2. Folosind structura logică de date de mai sus, scrieţi o aplicaţie care să realizeze toate operaţiile necesare de gestiune a datelor referitoare la produse. Datele vor fi păstrate într-un fişier binar organizat relativ. 3. Folosind structura logică de la tema 1 şi exemplul 11 de mai sus, scrieţi o aplicaţie care să realizeze toate operaţiile necesare de gestiune a datelor referitoare la produse. Datele vor fi păstrate într-un fişier binar organizat indexat. Răspunsuri şi comentarii la testele de autoevaluare 2. Trebuie calculate 12 sume, corespunzător celor 12 luni ale anului. Se iniţializează

se adaugă producţia valorică a fiecărei luni la suma corespunzătoare. Uscatu. 5. B. C. căutare. Rezolvarea se face conform schemei logice din figura 4. Stoica. Ed. au fost studiate metode de organizare a masivelor în fişiere de date şi algoritmi de prelucrare a lor. Stoica. Microinformatica. 4. Mircea . adăugînd încă un nivel de grupare – universitatea e compusă din facultăţi. I.Limbajele C şi C++ pentru începători. vol. Bibliografia unităţii de învăţare 1. Se cere de fapt realizarea operaţiei de modificare a datelor din fişier. Ed. Teorie şi aplicaţii. ASE Bucureşti. în cadrul fiecărui an sînt organizate mai multe grupe. Gh. II. ASE Bucureşti. relativ şi indexat. B. Roşca. ajungînd la structura fizică a articolelor din fişier. Apostol.Algoritmi în programare. Ed. Se caută produsul în fişier. Ghilic-Micu. Algoritmi în programare. Cluj-Napoca. Ed. Cocianu. Cocianu. Au fost analizaţi algoritmi de prelucrare a fişierelor care nu necesită actualizare precum şi a fişierelor care necesită actualizare. Roşca. C. Sînt trei caracteristici de grupare şi patru grade de total. 2007 2. Bucureşti. Gh. ASE.* material didactic pentru ID * 97 cele 12 sume cu zero. apoi se parcurge secvenţial fişierul şi. Ghilic-Micu. Algoritmii studiaţi descriu modul de rezolvare a prelucrărilor pentru fişierele organizate secvenţial. La terminarea parcurgeri fişierului se determină valoarea maximă dintre cele 12 sume. Structura logică a articolului trebuie extinsă prin adăugarea unui cîmp cu rol de indicator de stare. M. C. Liviu Negrescu . Ştiinţa învăţării unui limbaj de programare. 1998 4. Gh. I. Ed.Gh. Roşca. consultare. M. I. Ea va indica luna (lunile) cerute. ştergere. De asemenea. la fiecare articol. Ghilic-Micu. 2003 3. modificare.6.Programarea calculatoarelor. 3. B. M. Stoica . I. ASE Bucureşti. după codul său şi se modifică valoarea corespunzătoare lunii introduse de la tastatură. M. I. secvenţial. Roşca & colectiv . C.Programarea calculatoarelor. 2002 5.Bazele elaborării programelor. Uscatu . Rezumat În cadrul acestei unităţi de învăţare au fost studiaţi algoritmii pentru efectuarea tuturor operaţiilor de prelucrare a fişierelor de date: creare. 1994 . C. Exerciţii rezolvate şi propuse. o facultate lucrează pe mai mulţi ani de studiu. Prin combinarea operaţiilor individuale se pot construi algoritmi complecşi care să rezolve toate problemele de gestiune a volumului mare de date aferent aplicaţiilor de informatică economică.

Roşca. Ghilic-Micu. 2007 2. Uscatu. 1996 (paragrafele 2. Teorie şi aplicaţii. Ed.2) 4. Exerciţii rezolvate şi propuse.2. I. vol. Bucureşti. Gh.3. 1998 5. ASE Bucureşti. Cocianu. I. Stoica.Programarea calculatoarelor. C. Ghilic-Micu.2.1. Bucureşti. 4.1. II. Gh. B. ASE Bucureşti. Stoica. M. 2003 3. Algoritmi în programare.Metode numerice şi programe Pascal. B. 4. 2. ASE. Apostol.2. Ed. Ed.Programarea calculatoarelor. Ştiinţa învăţării unui limbaj de programare. Cocianu. I. Roşca & colectiv .Algoritmi în programare. 2002 6. Stoica . 2. Inforec. B.2. C. ASE Bucureşti. Iorgulescu .2. I.Bazele elaborării programelor. Gh. M. Liviu Negrescu . Roşca. A. Ed. C. Cluj-Napoca. C. M. M. Roşca.2.98 Algoritmi în programare Bibliografie 1. Uscatu .Gh.Limbajele C şi C++ pentru începători. Ed. 1994 . Ed. Ghilic-Micu. C. I. Mircea . Microinformatica.

Sign up to vote on this title
UsefulNot useful