204

Bazele_programarii_calculatoarelor

Embed Size (px)

DESCRIPTION

Bazele Programarii Calculatoarelor

Citation preview

Page 1: Bazele_programarii_calculatoarelor
Page 2: Bazele_programarii_calculatoarelor

ANDREI BĂUTU PAUL VASILIU

Academia Navală „Mircea cel Bătrân” Constanţa, 2009

BAZELE PROGRAMĂRII CALCULATOARELOR

Referenţi ştiinţifici: Conf. univ. dr. Ion Colţescu Conf. univ. dr. Ioan Popoviciu

Editura Academiei Navale „Mircea cel Bătrân” Str. Fulgerului nr.1, 900218, Constanţa Tel. 0241/626200/1219, fax 0241/643096 Email: [email protected]

Descrierea CIP a Bibliotecii Naţionale a României © Editura Academiei Navale „Mircea cel Bătrân”, 2009, pentru prezenta ediţie.

Page 3: Bazele_programarii_calculatoarelor

1

PPPRRREEEFFFAAAŢŢŢĂĂĂ

Programarea calculatoarelor este una dintre disciplinele din domeniul informaticii care are un loc bine definit în planurile de învăţământ ale universităţilor cu profil tehnic, cu impact real în formarea viitorilor ingineri.

Limbajul de programare C este folosit pe scară largă atât de programatorii de sistem pentru scrierea şi dezvoltarea sistemelor de operare (Windows, Unix, Linux), cât şi de programatorii de aplicaţii din diverse domenii. Datorită caracteristicilor sale (portabilitate, controlul modern al fluxurilor, setul bogat de operatori, multitudinea funcţiilor de bibliotecă) limbajul poate fi utilizat pe orice platformă şi asigură accesul programatorului la toate resursele calculatorului.

Cui se adresează cartea? Cartea se adresează studenţilor din universităţile

cu profil tehnic care parcurg programa cursului de programarea calculatoarelor şi limbaje de programare, celor care doresc să programeze în limbajul C, dar şi celor care au deja experienţă în programare.

2

Conţinutul cărţii urmează programa cursului „Programarea calculatoarelor şi limbaje de programare” predat de autori studenţilor din anul întâi la Academia Navală “Mircea cel Bătrân” din Constanţa.

Notaţii folosite în carte Carte conţine următoarele notaţii pentru a înlesni

urmărirea explicaţiilor şi secvenţelor de program: • secvenţele de program sunt scrise cu font

proporţional simplu: printf("Bine aţi venit!").

• dacă secvenţa de program este mai mare, atunci va fi scrisă într-un paragraf separat, cu o linie de marcare în partea stângă:

puts("Programarea"); puts("Calculatoarelor in"); puts("Limbajul C");

• rezultatele execuţiei programelor sunt scrise cu font proporţional îngroşat:

Bine aţi venit! • dacă rezultatele sunt mai multe, atunci vor fi scrise

într-un paragraf separat, într-o casetă cu chenar întrerupt:

Programarea Calculatoarelor in Limbajul C

• observaţiile şi informaţiile suplimentare legate de diverse noţiuni vor fi marcate printr-un paragraf special, astfel:

Page 4: Bazele_programarii_calculatoarelor

3

i Acest paragraf vă oferă informaţii suplimentare referitoare la noţiunile prezentate anterior.

Mulţumiri Mulţumim familiilor noastre care ne-au susţinut în

realizarea acestei cărţi şi au „îndurat” orele târzii de muncă. Mulţumim colegilor care au făcut observaţii pertinente privind conţinutul acestei cărţi şi colectivului Editurii Academiei Navale „Mircea cel Bătrân” pentru sfaturile şi suportul tehnic acordat.

De asemenea, mulţumim anticipat celor care vor aduce sugestii pentru îmbunătăţirea acestei cărţi.

Autorii. Aprilie 2009

Page 5: Bazele_programarii_calculatoarelor

5

CCCUUUPPPRRRIIINNNSSS

1. Algoritmi şi limbaje algoritmice...................................11 1.1. De la problemă la program..................................11

1.1.1. Specificarea problemei .................................11 1.1.2. Proiectarea algoritmului de rezolvare a problemei................................................................13 1.1.3. Codificarea algoritmului ................................13 1.1.4. Testarea şi validarea programului.................14

1.2. Noţiunea de algoritm ...........................................14 1.3. Reprezentarea algoritmilor ..................................16

1.3.1. Scheme logice ..............................................16 1.3.2. Limbajul pseudocod......................................19

1.4. Exemple ..............................................................24 1.5. Exerciţii................................................................36

2. Unităţi lexicale............................................................39 2.1. Mulţimea caracterelor..........................................39 2.2. Comentarii...........................................................41 2.3. Tipuri de date primare .........................................42 2.4. Constante............................................................43

2.4.1. Constante de tip caracter..............................44 2.4.2. Constante de tip şir de caractere ..................45 2.4.3. Constante de tip întreg .................................46 2.4.4. Constante de tip flotant.................................47

2.5. Cuvinte cheie.......................................................47 2.6. Identificatori.........................................................48

Algoritmi şi limbaje algoritmice

6

2.6.1. Variabile........................................................48 2.6.2. Masive ..........................................................49

2.7. Exemple ..............................................................50 2.8. Exerciţii................................................................51

3. Expresii. Operanzi. Operatori.....................................53 3.1. Expresii ...............................................................53 3.2. Operanzi..............................................................54 3.3. Conversii implicite de tip......................................54 3.4. Operatori .............................................................55

3.4.1. Operatori de adresare...................................59 3.4.2. Operatori unari..............................................60 3.4.3. Operatori multiplicativi ..................................62 3.4.4. Operatori aditivi.............................................63 3.4.5. Operatori pentru deplasare...........................64 3.4.6. Operatori relaţionali ......................................65 3.4.7. Operatori de egalitate ...................................65 3.4.8. Operatori pe biţi ............................................66 3.4.9. Operatori logici..............................................67 3.4.10. Operatorul condiţional.................................67 3.4.11. Operatori de atribuire..................................68 3.4.12. Operatorul virgulă .......................................69

3.5. Exemple ..............................................................70 3.6. Exerciţii................................................................77

4. Instrucţiuni .................................................................79 4.1. Instrucţiuni expresie ............................................79 4.2. Instrucţiuni de decizie..........................................80

4.2.1. Instrucţiunea if ..............................................80 4.2.2. Instrucţiunea switch ......................................81

4.3. Instrucţiuni de ciclare...........................................83 4.3.1. Instrucţiunea do-while...................................83 4.3.2. Instrucţiunea while ........................................84 4.3.3. Instrucţiunea for ............................................84

4.4. Instrucţiuni de salt ...............................................86 4.4.1. Instrucţiunea break .......................................86

Page 6: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

7

4.4.2. Instrucţiunea continue...................................87 4.4.3. Instrucţiunea goto .........................................87

4.5. Exemple ..............................................................88 4.6. Exerciţii..............................................................104

5. Funcţii standard de intrare/ieşire..............................107 5.1. Funcţii pentru caractere.....................................107 5.2. Funcţii pentru şiruri de caractere.......................108 5.3. Funcţii cu formatare...........................................109

5.3.1. Codurile de format ale funcţiei printf ...........110 5.3.2. Codurile de format ale funcţiei scanf...........114

5.4. Funcţii cu formatare pentru şiruri de caractere..117 5.5. Exemple ............................................................118 5.6. Exerciţii..............................................................122

6. Tipuri de date compuse ...........................................123 6.1. Masive...............................................................123 6.2. Structuri.............................................................125 6.3. Uniuni................................................................128 6.4. Câmpuri.............................................................129 6.5. Exemple ............................................................130 6.6. Exerciţii..............................................................147

7. Funcţii ......................................................................149 7.1. Declararea şi definirea funcţiilor ........................149

7.1.1. Prototipul funcţiilor ......................................151 7.1.2. Transferul parametrilor ...............................152

7.2. Apelul funcţiilor ..................................................152 7.3. Transmiterea parametrilor .................................154

7.3.1. Transferul prin parametri.............................154 7.3.2. Transferul prin variabile globale..................159

7.4. Recursivitate......................................................161 7.5. Exemple ............................................................163 7.6. Exerciţii..............................................................202

8. Pointeri şi gestiunea dinamică a memoriei...............205 8.1. Pointeri ..............................................................205 8.2. Operatorii &, * şi ->............................................206

Algoritmi şi limbaje algoritmice

8

8.3. Aritmetica pointerilor..........................................207 8.4. Legătura între tablouri şi pointeri .......................209 8.5. Gestiunea dinamică a memoriei........................209 8.6. Funcţii pentru operaţii cu blocuri de memorie....212 8.7. Exemple ............................................................214 8.8. Exerciţii..............................................................222

9. Funcţii pentru şiruri de caractere..............................225 9.1. Şiruri de caractere.............................................225 9.2. Funcţii pentru şiruri de caractere.......................226 9.3. Funcţii pentru clasificarea caracterelor..............231 9.4. Exemple ............................................................233 9.5. Exerciţii..............................................................248

10. Funcţii matematice.................................................251 10.1. Exemple ..........................................................255 10.2. Exerciţii............................................................267

11. Funcţii pentru gestiunea fişierelor ..........................269 11.1. Structura FILE .................................................269 11.2. Fişiere standard de intrare/ieşire .....................270 11.3. Deschiderea şi închiderea fişierelor ................270 11.4. Funcţii pentru caractere...................................271 11.5. Funcţii pentru şiruri de caractere.....................273 11.6. Funcţii cu formatare.........................................274 11.7. Funcţii pentru blocuri de date ..........................275 11.8. Funcţii pentru acces aleator în fişiere..............277 11.9. Alte funcţii pentru fişiere şi directoare..............278 11.10. Exemple ........................................................279 11.11. Exerciţii..........................................................293

12. Metode de programare ..........................................295 12.1. Metoda “Divide et Impera”...............................295 12.2. Exemple ..........................................................296 12.3. Metoda Backtracking.......................................301 12.4. Backtracking nerecursiv ..................................306 12.5. Exemple ..........................................................308 12.6. Backtracking recursiv ......................................320

Page 7: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

9

12.7. Exemple ..........................................................323 12.8. Metoda Greedy................................................336 12.9. Exemple ..........................................................340 12.10. Metoda programării dinamice........................350 12.11. Exemple ........................................................352

13. Întrebări de autoevaluare.......................................365 13.1. Unităţi lexicale .................................................365 13.2. Expresii. Operanzi. Operatori ..........................366 13.3. Instrucţiuni.......................................................368 13.4. Funcţii standard de intrare/ieşire .....................370 13.5. Tipuri de date compuse...................................371 13.6. Funcţii..............................................................372 13.7. Pointeri şi gestiunea dinamică a memoriei ......374 13.8. Funcţii pentru şiruri de caractere.....................375 13.9. Funcţii matematice ..........................................377 13.10. Funcţii pentru gestiunea fişierelor..................378

14. Anexa 1 – Mediul de programare Dev-C++ ...........381 14.1. Instalare ..........................................................382 14.2. Configurare......................................................384

14.2.1. Prima pornire ............................................384 14.2.2. Editorul......................................................385 14.2.3. Tastele rapide...........................................386 14.2.4. Alte instrumente........................................387

14.3. Utilizare ...........................................................388 14.3.1. Scrierea unui program C...........................388 14.3.2. Compilarea codului sursă .........................389 14.3.3. Rularea programului .................................389 14.3.4. Depanarea................................................390

15. Anexa 2 – Mediul de programare Raptor ...............393 16. Anexa 3 – Setul de caractere ASCII ......................397

Page 8: Bazele_programarii_calculatoarelor

11

111... AAALLLGGGOOORRRIIITTTMMMIII ŞŞŞIII LLLIIIMMMBBBAAAJJJEEE AAALLLGGGOOORRRIIITTTMMMIIICCCEEE

1.1. De la problemă la program Rezolvarea unei probleme începe cu specificarea

acesteia şi se încheie cu obţinerea unui program concret şi corect. Programele sunt scrise pentru a instrui maşinile să lucreze cu sarcini specifice sau să rezolve probleme specifice. O procedură (descrisă pas cu pas) asociată unei sarcini se numeşte algoritm. Programarea este activitatea de codificare a algoritmilor în calculatoare. Procesul de programare are (în general) patru paşi: 1. Specificarea problemei; 2. Proiectarea unui algoritm pentru rezolvarea sa; 3. Codificarea algoritmului într-un limbaj de programare; 4. Testarea programului.

1.1.1. Specificarea problemei În prima fază, procesul de rezolvare constă în

analiza problemei – care are drept scop elaborarea unui enunţ complet şi precis al problemei, ţinând în acelaşi timp seamă de condiţiile de realizare şi execuţie ale problemei.

Algoritmi şi limbaje algoritmice

12

Din enunţul problemei trebuie să rezulte clar funcţiile programului. Pentru aceasta, trebuie să se delimiteze clar informaţiile de intrare (care trebuie prelucrate de program) şi rezultatele aşteptate de la program.

Pentru referirea la datele de intrare şi cele de ieşire se foloseşte noţiunea de variabile de intrare, respectiv variabile de ieşire. Trebuie să privim variabilele ca notaţii simbolice pentru valorile concrete pe care le prelucrează algoritmul. Tot acum, în această etapă, trebuie precizată maniera de reprezentare şi organizare a datelor de intrare şi de ieşire. Rezultatul acestei etape este specificaţia (sau cerinţa) programului.

De exemplu, ne propunem să scriem un program care să primească de la tastatură coeficienţii reali a, b, c ai unei ecuaţii de grad 2 şi să calculeze soluţiile reale ale acesteia, dacă există. Din enunţ, obţinem următoarele informaţii despre problema noastră:

• Date de intrare: a, b, c – numere reale • Date de ieşire: x1, x2 – numere reale • Funcţionalitate: dacă există, atunci calculează

rădăcinile reale (x1 şi x2) ale ecuaţiei de grad 2 (specifică prin a, b, c), altfel afişează mesajul „Ecuaţia nu are rădăcini reale”.

• Organizarea şi reprezentarea datelor de intrare şi de ieşire: datele de intrare se citesc de la tastatură şi rezultatele se afişează pe monitor.

Page 9: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

13

1.1.2. Proiectarea algoritmului de rezolvare a problemei

În această etapă, programatorul trebuie să specifice o listă de comenzi care să descrie secvenţa de operaţii executate de calculator pentru a rezolva problema. În sens general, un program reprezintă scrierea unui algoritm într-un limbaj de programare.

Această etapă este evident cea mai dificilă etapă din procesul de programare. În general, procesul de scriere al unui algoritm este unul iterativ, descrierea algoritmului realizându-se la diferite nivele de detaliere. Acest mod de specificare pas cu pas a rezolvării se mai numeşte proiectare descendentă.

De asemenea, pentru problemele mai complicate este posibilă descompunerea lor în subprobleme care să se rezolve separat mai uşor. Acest proces se numeşte modularizare şi uşurează modificarea programelor, deschizând calea către programe mai uşor de reutilizat.

1.1.3. Codificarea algoritmului După elaborare, algoritmul trebuie codificat cu

ajutorul unui limbaj de programare astfel încât să poată fi „înţeles” de către calculator (altfel spus să poată fi tradus mai departe într-un limbaj executabil pe calculator). Codificarea algoritmului este facilitată de utilizarea unor simboluri şi reguli speciale cum sunt schemele logice sau limbajul pseudocod.

Algoritmi şi limbaje algoritmice

14

1.1.4. Testarea şi validarea programului Programul obţinut prin implementarea

algoritmului trebuie verificat în scopul eliminării erorilor de sintaxă, dar mai ales al eliminării erorilor de logică. Este posibil ca în urma execuţiei programului să se obţină rezultate incorecte, adică sunt eliminate erorile de sintaxa şi programul poate fi executat pe calculator, dar acesta nu rezolvă corect problema de la care s-a plecat (datorită unei greşeli de proiectare sau implementare a algoritmului). De aceea, testarea programului trebuie să se facă cu seturi de date de intrare care să acopere cât mai multe dintre cazurile posibile. Această etapă poartă numele de validarea programului.

Activitatea de scriere a unui program nu se încheie o dată cu validarea acestuia, ci urmează etapă de întreţinere. În această etapă este posibil să fie efectuate modificări ale programului, datorate fie unor corecturi, fie schimbării unor cerinţelor. De asemenea, o etapă importantă o constituie realizarea documentaţiei programului – explicaţii despre cum trebuie utilizat programul (e.g. Help) care să permită atât modificări ulterioare ale acestuia, dar şi utilizarea programului de către alte persoane decât cele care l-au creat.

1.2. Noţiunea de algoritm Noţiunea de algoritm ocupă un loc central în

informatică. Calculul algoritmic forţează precizia gândirii, algoritmizarea unei metode matematice presupune

Page 10: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

15

detalierea tuturor etapelor ei, urmărirea pas cu pas a fiecăreia dintre aceste etape, deci o înţelegere profundă a întregii metode.

Ca şi alte noţiuni matematice, noţiunea de algoritm se caracterizează printr-o mare generalitate. Înţelesul mai larg al noţiunii de algoritm este acela de procedeu, metodă, reţetă. Astfel, prin algoritm se înţelege o mulţime ordonată şi finită de reguli care descriu o succesiune finită de operaţii necesare rezolvării unei probleme, care pornind de la date (intrări) produce rezultate (ieşiri).

Fiecare propoziţie din descrierea unui algoritm este de fapt o comandă care trebuie executată de calculator. Comanda specifică o acţiune care se aplică unor date determinând modificarea acestora.

Principalele proprietăţi pe care ar trebui să le posede un algoritm sunt următoarele:

• să fie bine definit – adică operaţiile cerute să fie specificate riguros şi fără ambiguitate, astfel încât să poată fi executat pe o maşină programabilă;

• să fie efectiv – adică să se termine întotdeauna după un număr finit de operaţii;

• să fie universal – adică să permită rezolvarea unei clase de probleme (nu o instanţă a unei probleme). Un algoritm trebuie să fie determinist, adică

executat de mai multe ori pentru acelaşi set de date de intrare, să producă aceleaşi rezultate.

Algoritmi şi limbaje algoritmice

16

1.3. Reprezentarea algoritmilor Odată cu elaborarea unui algoritm, acesta trebuie

prezentat într-o formă cât mai precisă şi mai clară, pentru a putea fi transpus, în continuare, sub forma unui program înţeles de calculator. Pentru reprezentarea algoritmilor se folosesc diverse forme de descriere caracteristice (denumite limbaje algoritmice), deoarece limbajul natural nu permite o descriere suficient de riguroasă a algoritmilor. În plus, pe măsură ce complexitatea problemelor creşte, creşte şi complexitatea descrierii acestora în limbaj natural.

În general, notaţia folosită pentru reprezentarea algoritmilor trebuie să satisfacă următoarele cerinţe:

• să fie uşor de învăţat şi de folosit; • să permită exprimarea cât mai naturală a

raţionamentelor umane; • să reflecte caracteristicile limbajelor de programare

pentru a uşura codificarea algoritmilor. Având în vedere scopurile propuse, se dovedesc

potrivite două forme de reprezentare a algoritmilor: scheme logice şi limbajul pseudocod.

1.3.1. Scheme logice O schemă logică poate fi interpretată ca o

reprezentare grafică a algoritmului, formată din blocuri şi săgeţi. Blocurile marchează operaţii care trebuie efectuate, iar săgeţile indică ordinea acestora. În scheme logice se folosesc următoarele tipuri de blocuri:

• bloc de stare marchează o etapă în timpul execuţiei algoritmului a cărui nume este specificat

Page 11: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

17

într-o elipsă. Două stări speciale sunt Start care marchează începutul algoritmului, şi End (sau Stop) care marchează sfârşitul algoritmului.

End Start

• bloc de intrare: marchează operaţia de citire a

datelor de intrare. Lista de variabile a căror valori trebuie citite este scrisă într-un trapez cu baza mare în sus sau într-un paralelogram „înţepat” în partea stângă de o săgeată. Pentru a reduce complexitatea schemei, blocul de intrare poate să includă un mesaj ce va fi afişat utilizatorului:

lista de variabile

"Introduceţi lungimea"

GET lung

• bloc de ieşire marchează operaţia de generare a rezultatelor. Lista de variabile sau mesajele ce trebuie afişate fiind scrise într-un trapez cu baza mare în jos sau într-un paralelogram din care porneşte o săgeată către dreapta. Uneori, semnul paragraf (¶) se foloseşte pentru a anunţa explicit că mesajele care vor urma după acest bloc de ieşire trebuie să înceapă de pe o linie nouă.

lista de variabile

PUT "Acesta este un bloc de ieşire"¶

Algoritmi şi limbaje algoritmice

18

• bloc de calcul marchează operaţia de prelucrare a informaţiilor prin intermediul variabilelor în care sunt stocate. Acesta permite efectuarea unor calcule şi prelucrări de date, şi memorarea rezultatului obţinut într-o variabilă pentru a fi folosit ulterior.

variabilă ← expresie

• bloc de decizie sau selecţie marchează alegerea unei căi de execuţie în funcţie de context: se verifică o condiţie; în cazul în care condiţia este adevărată, se urmează ramura marcată cu Da (sau Yes), iar în caz contrar se urmează ramura marcata cu Nu (sau No). Într-un final, cele două ramuri de execuţie vor ajunge într-un punct comun, dar în timpul rulării, numai una dintre ele se execută.

condiţie No Yes

• blocul de repetare marchează o zonă de cod care

este rulată de 0, 1 sau mai multe ori până când o condiţie logică devine adevărată. Unii autori nu consideră acest bloc ca fiind bloc fundamental al schemelor logice deoarece el poate fi simulat folosind celelalte blocuri definite anterior. Din acest motiv, simbolul folosit pentru acesta nu este la fel de bine definit ca în celelalte cazuri. În această

Page 12: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

19

carte vom folosi următorul simbol pentru a reprezenta blocuri de repetare:

condiţie Yes

No

Loop

1.3.2. Limbajul pseudocod Limbajul pseudocod permite specificarea

algoritmilor cu ajutorul a două tipuri de enunţuri: standard şi nestandard. Enunţurile standard exprimă operaţii cu corespondent direct în limbajele de programare. Enunţurile nestandard sunt fraze în limbaj natural care pot fi utilizate de programator în schiţarea formelor iniţiale ale algoritmilor. În general, dezvoltarea unui algoritm se realizează iterativ, trecându-l prin mai multe nivele de detaliu, enunţurile nestandard fiind înlocuite treptat cu enunţuri standard. De exemplu, pentru precizarea datelor iniţiale se scrie: Citeşte a,b,c, iar pentru precizarea rezultatelor: Scrie x1, x2 sau Scrie „Ecuaţia nu are soluţii reale”.

Algoritmi şi limbaje algoritmice

20

Atât enunţurile standard, cât şi ce nestandard pot fi ataşate unor structuri de control. Cele mai utilizate structuri de control sunt:

• secvenţa – reprezintă o succesiune de comenzi pentru prelucrarea datelor. De exemplu:

citeşte a, b x = b + a scrie x

• decizia – alegerea unei operaţii sau a unei secvenţe dintre două alternative posibile:

dacă test atunci secvenţa A altfel secvenţa B sfârşit

În limbaj natural, execuţia poate fi descrisă astfel: se evaluează condiţia test; dacă rezultatul este valoarea logică adevărat, atunci se execută secvenţa A, altfel (daca rezultatul este fals), se execută secvenţa B. Exprimată cu o schemă logică, decizia arată astfel:

test

secventa A secventa B

No Yes

De asemenea, există şi decizia cu o cale vidă,

exprimată în pseudocod astfel (lăsăm exprimarea în schemă logică ca exerciţiu pentru cititor):

Page 13: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

21

dacă test atunci secvenţa A sfârşit

test

secventa A

No Yes

• ciclul (sau iteraţia) cu test iniţial – asigură

executarea unei secvenţe în mod repetat, în funcţie de o anumită condiţie testată apriori.

cât timp test repetă secvenţa sfârşit

Exprimat cu o schemă logică, ciclul cu test iniţial arată astfel:

test

secventa A

No

Yes

Loop

! test

secventa A

Yes

No

Loop

Algoritmi şi limbaje algoritmice

22

Execuţia parcurge următoarele etape: se evaluează expresia test; dacă rezultatul este adevărat, se execută secvenţa şi se repetă secvenţa de ciclare, altfel se termină secvenţa de ciclare şi se trece la următoarea secvenţă.

i Dacă valoarea testului e falsă de la început, ciclul nu se execută niciodată.

i De remarcat că în schema logică din partea dreaptă a figurii anterioare am folosit expresia !test care reprezintă negarea valorii de adevăr a expresiei test. Acest lucru este necesar pentru a exprima două acţiuni identice: în limbaj pseudocod, bucla se execută cât timp condiţia este adevărată; în schema logică, bucla se execută până când condiţia devine falsă.

• ciclul (sau iteraţia) cu test final – asigură executarea unei secvenţe în mod repetat, în funcţie de o anumită condiţie testată aposteriori.

repetă secvenţa cât timp test

Execuţia parcurge următoarele etape: se execută secvenţa, se evaluează expresia test; dacă rezultatul este adevărat, se repetă secvenţa de ciclare, altfel se termină secvenţa de ciclare şi se trece la următoarea secvenţă. Exprimat cu o schemă logică, ciclul cu test final arată astfel:

Page 14: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

23

secventa A

! test Yes

No

Loop

secventa A

test No

Yes

Loop

i Ciclul se execută măcar o dată, deoarece testarea

se realizează la sfârşit.

• Ciclul cu contor – asigură executarea unei secvenţe în mod repetat, pentru fiecare element al unei mulţimi.

pentru contor=start la sfarsit pas pas repetă secvenţa sfârşit

Execuţia parcurge următoarele etape: variabila contor primeşte pe rând valorile start, start+pas, start+2*pas, …, sfarsit, şi pentru fiecare din aceste valori se execută se secvenţa specificată. Ciclul cu contor este un caz particular al ciclului cu test iniţial (lăsăm cititorului ca exerciţiu exprimarea unui ciclu cu contor folosind un ciclu cu test iniţial şi structuri secvenţiale). Ciclul cu contor poate fi exprimat în schemă logică aşa cum se observă în figura următoare.

Algoritmi şi limbaje algoritmice

24

i În mod similar cu exemple anterioare, partea din dreapta a figurii are negată expresia logică (în corespondenţă cu etichetele Yes/No ale ramurilor). De această dată însă, în locul condiţiei !(contor<=sfarsit) am folosit forma mai tradiţională contor>sfarsit, ambele exprimând acelaşi lucru.

contor ← start

contor>sfarsit

secventa

contor ← contor + 1

Yes

No

Loop

contor ← start

contor<=sfarsit

secventa

contor ← contor + 1

No

Yes

Loop

1.4. Exemple 1. Scrieţi un algoritm care să primească la intrare o temperatură în grade Celsius şi să calculeze temperatura corespunzătoare în grade Farenheit şi Kelvin.

Din enunţul problemei extragem următoarele cerinţe:

Page 15: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

25

• date de intrare: c – număr real • date de ieşire: f, k – numere reale • funcţionalitate: calculează f = 32 + 9*c/5 şi k=c-

273,15. Soluţia, în pseudocod şi schemă logică este

următoarea: citeşte c f = 32 + 9*5/c k = c + 273,15 scrie f,k

2. Scrieţi un algoritm pentru rezolvarea ecuaţiei ax+b=0, unde a şi b sunt numere reale citite de la tastatură.

Algoritmi şi limbaje algoritmice

26

Din enunţul problemei extragem următoarele cerinţe:

• date de intrare: a, b – numere reale • date de ieşire: x – număr real • funcţionalitate: dacă a=0 şi b=0, atunci afişează

mesajul „Orice număr real este soluţie”; dacă a=0 şi b≠0 atunci afişează mesajul „Nu există soluţii reale”; altfel afişează soluţia ecuaţiei de grad 1 (x=-b/a). Soluţia, în pseudocod şi schemă logică este

următoarea:

citeşte a,b dacă a=0 atunci dacă b=0 atunci scrie "Orice număr real este soluţie" altfel scrie "Nu există soluţii reale" sfârşit

Page 16: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

27

altfel x = -b/a scrie x sfârşit

3. Scrieţi un algoritm pentru rezolvarea ecuaţiei ax2+bx+c=0, unde a, b şi c sunt numere reale citite de la tastatură.

Din enunţul problemei extragem următoarele cerinţe:

• date de intrare: a, b, c – numere reale • date de ieşire: x1, x2 – numere reale • funcţionalitate: dacă a=0, atunci (pentru simplitate)

afişează mesajul "Ecuaţia nu este de gradul 2", altfel calculează d=b2-4ac; dacă d<0, atunci afişează mesajul "Ecuaţia are soluţii complexe", altfel afişează soluţiile ecuaţiei de grad 2 (x1=(-b-sqrt(d))/(2a) şi x2=(-b+sqrt(d))/(2a), unde sqrt reprezintă funcţia radical). Soluţia, în pseudocod şi schemă logică este

următoarea: citeşte a,b,c dacă a=0 atunci scrie "Ecuaţia nu este de gradul 2" altfel d = b*b-4*a*c dacă d<0 atunci scrie "Ecuaţia are soluţii complexe" altfel x1=(-b-sqrt(d))/(2a) x2=(-b+sqrt(d))/(2a) scrie x1, x2 sfârşit sfârşit

i În scheme logice şi pseudocod putem exprima expresia matematică xy prin expresia x**y sau x^y.

Algoritmi şi limbaje algoritmice

28

Astfel, valoarea variabilei d (delta) putea fi calculată şi cu expresiile b^2–4*a*c sau b**2–4*a*c.

4. Scrieţi un algoritm care determină cel mai mare divizor comun a două numere naturale.

Din enunţul problemei extragem următoarele cerinţe:

• date de intrare: a, b – numere naturale

Page 17: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

29

• date de ieşire: c – număr natural • funcţionalitate: vom calcula cel mai mare divizor

comun folosind algoritmul lui Euclid. Pentru aceasta, mai întâi se determină restul împărţirii lui a la b (notat cu r=a%b); dacă restul este nenul, variabile a şi b se modifică (devenind a=b şi b=r) şi se repetă operaţia anterioară. O soluţie în pseudocod este următoarea.

citeşte a citeşte b repetă r = a % b a = b b = r cât timp r≠0 scrie b

O metodă alternativă pentru determinarea celui mai mare divizor comun (metoda originală propusă de Euclid) constă în scăderi repetate a variabilei cu valoarea mai mică din variabila cu valoarea mai mare, până când valorile celor două variabile devin egale. Soluţia în pseudocod în acest caz este următoarea: citeşte a citeşte b cât timp a≠b repetă dacă a>b atunci a = a – b altfel b = b – a sfârşit sfârşit scrie b

Soluţiile în pseudocod sunt prezentate pentru ambii algoritmi în figura următoarea.

Algoritmi şi limbaje algoritmice

30

Page 18: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

31

Algoritmi şi limbaje algoritmice

32

5. Scrieţi un algoritm care determină dacă un număr este prim sau nu.

Din enunţul problemei extragem următoarele cerinţe:

• date de intrare: a – număr natural • date de ieşire: mesajul "prim" sau "compus" • funcţionalitate: Fie d numărul de divizori ai lui a

(adică numere naturale din intervalul [2, a/2] pentru la care a se împarte fără rest). Dacă d este 0, atunci numărul este prim, altfel este compus. Soluţia, în pseudocod şi schemă logică este

următoarea: citeşte a d = 0 pentru i=2 la a/2 pas 1 repetă dacă a % i = 0 atunci d = d + 1 sfârşit sfârşit dacă d = 0 atunci scrie "prim" altfel scrie "compus" sfârşit

Page 19: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

33

Algoritmi şi limbaje algoritmice

34

6. Fie n un număr natural şi a1,…,an, respectiv, b1,…,bn, doi vectori de n numere reale. Scrieţi un algoritm care determină produsul scalar al vectorilor a şi b.

Din enunţul problemei extragem următoarele cerinţe:

• date de intrare: n–număr natural, a1,…,b1,… numere reale

• date de ieşire: p – număr real • funcţionalitate: determină valoarea produsului

scalar p=<a,b>= a1* b1+a2*b2+…+ an*bn. Soluţia, în pseudocod şi schemă logică este

următoarea: citeşte n pentru i = 1 la n repetă citeşte a[i], b[i] sfârşit p = 0 pentru i = 1 la n repetă p = p + a[i]*b[i] sfârşit scrie p

Valoarea produsului scalar putea fi calculată în acelaşi timp cu citirea elementelor vectorului, pseudocodul fiind: citeşte n p = 0 pentru i = 1 la n repetă citeşte a[i], b[i] p = p + a[i]*b[i] sfârşit scrie p

Schemele logice sunt prezentate în figurile următoare.

Page 20: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

35

Algoritmi şi limbaje algoritmice

36

1.5. Exerciţii 1. Scrieţi un algoritm care determină perimetrul şi aria unui dreptunghi cunoscând lungimile laturilor. 2. Scrieţi un algoritm care determină consumul de combustibil la o sută de kilometri al unui vehicul ştiind consumul total pe parcursul unei anumite distanţe. 3. Scrieţi un algoritm care determină maximul a trei numere reale citite de la tastatură. 4. Scrieţi un algoritm care, cunoscând trei valori naturale, determină dacă acestea pot reprezenta laturile unui triunghi. În caz afirmativ, să se afişeze dacă triunghiul este oarecare, isoscel sau echilateral. 5. Scrieţi un algoritm pentru rezolvarea sistemului:

a*x + b*y = c d*x + e*y = f

unde a, b, c, d, e, f sunt numere reale date. 6. Scrieţi un algoritm care determină divizorii unui număr natural nenul. 7. Scrieţi un algoritm care determină factorialul unui număr natural. 8. Scrieţi un algoritm care transformă un număr din baza 10 în baza 2. 9. Scrieţi un algoritm care transformă un număr din baza 2 în baza 10. 10. Fie n un număr natural şi a1,…,an numere reale. Scrieţi un algoritm care determină media aritmetică a numerelor a1,…, an. 11. Fie n un număr natural şi a1,…,an numere reale. Scrieţi un algoritm care determină suma valorilor pozitive din mulţimea a1,…,an. 12. Fie n un număr natural şi a1,…,an numere reale. Scrieţi un algoritm care determină valoarea minimă din mulţimea a1,…,an.

Page 21: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

37

13. Fie n un număr natural şi m, a1,…,an numere reale. Scrieţi un algoritm care afişează toate valorile i pentru care ai=m. 14. Fie n un număr natural şi a1,…, an numere reale. Scrieţi un algoritm care ordonează crescător numerele a1,…, an.

Page 22: Bazele_programarii_calculatoarelor

39

222... UUUNNNIIITTTĂĂĂŢŢŢIII LLLEEEXXXIIICCCAAALLLEEE

Unităţile lexicale sunt atomii unui program C, aşa cum un roman este format din fraze, iar acestea din cuvinte. Scopul acestui capitol este să vă prezinte aceste noţiuni fundamentale. Orice program C este alcătuit din cel puţin o funcţie (funcţia main). Orice funcţie conţine zero, una sau mai multe expresii. O expresie este construită din operanzi conectaţi prin operatori conform anumitor reguli sintactice. În plus, textul programelor poate conţine comentarii care să ajute la înţelegerea acestuia.

2.1. Mulţimea caracterelor Unităţile lexicale ale unui program C sunt

alcătuite din caractere, fiecare caracter având un anume înţeles pentru compilatorul C, folosirea incorectă a acestora fiind semnalată prin mesaje de eroare.

Mulţimea caracterelor ce pot fi folosite în programe C este împărţită în următoarele submulţimi:

• caractere alfanumerice: literele mici şi mari din alfabetul englez, şi cifrele;

Unităţi lexicale

40

• caractere speciale: caracterele prezentate în Tabel 2.1

• caractere de spaţiere: caracterele prezentate în Tabel 2.2

• alte caractere: celelalte caractere tipăribile din setul de caractere ASCII (vezi Anexa 3 – Setul de caractere ASCII)

Tabel 2.1 Caractere speciale

Caracter Denumire Caracter Denumire , virgulă . punct : două puncte ; punct şi

virgulă ? semnul întrebării ! semnul

exclamării ' apostrof " ghilimele ( paranteză rotundă

stânga ) paranteză

rotundă dreapta

[ paranteză dreaptă stânga

] paranteză dreaptă dreapta

{ acoladă stânga } acoladă dreapta

< mai mic > mai mare ^ săgeată sus = egal + plus - minus * asterisc / slash % procent \ backslash | bară verticală ~ tildă

Page 23: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

41

_ underscore # diez & ampersand

Tabel 2.2 Caractere de spaţiere

Cod ASCII

Denumire Semnificaţie

32 spaţiu un spaţiu 9 tab grup de spaţii (tabulator

orizontal) 10 line feed trecere la linia următoare (new

line) 13 carriage

return revenire la începutul liniei curente (folosit în special la imprimante)

11 tab vertical tabulator vertical (folosit în special la imprimante)

12 form feed trecere la pagina următoare (folosit în special la imprimante)

2.2. Comentarii Un comentariu este o porţiune de text care este

ignorată de către compilatorul C. Un comentariu pe o singură linie începe după o secvenţă slash-slash // şi se termină la sfârşitul liniei curente. Un comentariu pe una sau mai multe linii începe după o secvenţă slash-star /* şi se termină la prima secvenţă star-slash */. Pentru a uşura înţelegerea programelor se recomandă folosirea comentariilor cât mai detaliate.

Unităţi lexicale

42

De exemplu, o secvenţă de program care calculează suma a două numere poate fi comentată astfel: suma = a + b; // calculam suma lui a si b

sau suma = a + b; /* calculam suma lui a si b */

i Comentariile nu pot fi imbricate (i.e. nu putem introduce un comentariu în alt comentariu) şi nu pot fi scrise în interiorul constantelor caracter sau şir de caractere.

2.3. Tipuri de date primare Un tip de date este o informaţie asociată unei

expresii pe baza căreia compilatorul limbajului C determină în ce context poate fi folosită expresia respectivă. De exemplu, cu un număr întreg se pot efectua operaţii aritmetice. Orice expresie validă în limbajul C are asociat un tip de date. Limbajul C defineşte patru tipuri de date fundamentale: caracter (char), întreg (int), flotant (sau real) simplu (float) şi flotant dublu (double), unele dintre acestea având mai multe variante (denumite subtipuri).

În funcţie de compilator, tipurile char şi int reprezintă valori cu sau fără semn, dar programatorul poate să folosească modificatorii signed şi unsigned pentru a selecta un anume subtip. De asemenea, implicit tipul int poate fi scurt sau lung, dar programatorul poate să folosească modificatorii short şi long pentru a

Page 24: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

43

selecta subtipul dorit. În anumite situaţii (foarte rare), tipul flotant dublu (double) este echivalent cu flotant simplu (float). Caracteristicile tipurilor şi subtipurilor de bază sunt prezentate în tabelul următor.

Tabel 2.3 Tipuri şi subtipuri fundamentale de date

Tip/subtip Mărime (octeţi)

Domeniu de valori

unsigned char 1 0…255 signed char 1 -128…127 char 1 depinde (cu sau fără semn) short signed int 2 -32768…32767 short unsigned int

2 0…65535

short int 2 depinde (cu sau fără semn) long signed int 4 -2147483648…2147483647 long unsigned int

4 0… 4294967296

long int 4 depinde (cu sau fără semn) signed int 2 sau 4 depinde (cu semn) unsigned int 2 sau 4 depinde (fără semn) int 2 sau 4 depinde (cu sau fără semn) float 4 -3,40×1038...3,4×1038 double 8 -1,79×10308...1,79×10308

2.4. Constante Constantele reprezintă cel mai simplu tip de

operanzi, mai exact o valoare care nu poate fi modificată

Unităţi lexicale

44

şi care are asociat un tip (stabilit în funcţie de modul de scriere al valorii constantei).

2.4.1. Constante de tip caracter O constantă de tip caracter se scrie în program

între apostrofi (e.g. ’C’, ’0’, ’.’, ’!’). Tipul constantelor caracter este întotdeauna char. Pentru a scrie constantele caracter apostrof, backslash (\) sau un caracter care nu apare pe tastatură se folosesc secvenţe escape. O secvenţă escape este compusă din caracterul backslash (\) urmat de un caracter normal sau un număr (în baza 8 sau 16). În acest context, semnificaţia caracterului sau a numărului de după backslash devine una specială. Cele mai utile secvenţe escape sunt prezentate în Tabel 2.4. De exemplu, dacă vrem să scriem constanta caracter apostrof, atunci vom scrie ’\’’.

Tabel 2.4 Secvenţe escape valide în limbajul C

Secvenţă escape

Semnificaţie

\a alert – produce un sunt în difuzorul calculatorului

\n new line – trecerea la rândul următor \r carriage return – revenirea la capătul rândului \t horizontal tab – deplasarea pe orizontală cu

un tab \v vertical tab – deplasarea pe verticală cu un

tab (la imprimantă)

Page 25: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

45

\f form feed – trecerea la pagina următoare (la imprimantă)

\b backspace – deplasarea înapoi a cursorului cu un caracter

\\ backslash – caracterul backslash \’ single quote – caracterul apostrof \” double quote – caracterul ghilimele \ooo octal – caracterul cu codul ASCII ooo (unde

ooo este un număr în baza 8, vezi Anexa 3 – Setul de caractere ASCII)

\xhh hexazecimal – caracterul cu codul ASCII hh (unde hh este un număr în baza 16, vezi Anexa 3 – Setul de caractere ASCII)

2.4.2. Constante de tip şir de caractere O constantă de tip şir de caractere este un grup

de caractere şi secvenţe escape scrise între ghilimele (e.g. “acesta este un sir”). Daca o constantă de tip şir de caractere este prea lungă, ea poate fi scrisă pe mai multe linii în program fie prin terminarea fiecărui rând (cu excepţia ultimului) cu caracterul backslash, fie prin scrierea fiecărui rând între ghilimele. De exemplu, următoarele trei şiruri sunt identice: “Acesta este un sir” “Acesta este \ un sir” “Acesta este “ “un sir”

Tipul constantelor şir de caractere este char[], adică un vector de caractere. La sfârşitul fiecărui şir de

Unităţi lexicale

46

caractere compilatorul adaugă automat caracterul NUL (caracterul cu codul ASCII 0), ca marcator pentru sfârşitul şirului.

Pentru a include caractere ghilimele într-un şir de caractere se foloseşte secvenţa escape \”. Funcţiile pentru operaţii cu caractere şi şiruri de caractere vor fi discutate în detaliu în capitolul 8.

2.4.3. Constante de tip întreg O constantă de tip întreg este un număr întreg

scris în baza 8, 10 sau 16. Dacă numărul este scris în baza 8, atunci trebuie să fie prefixat de cifra zero. Dacă numărul este scris în baza 16, atunci trebuie să fie prefixat de zero urmat de litera x.

Subtipul constantei este ales astfel încât domeniu acestuia să conţină valoarea constantei. Programatorul poate scrie sufixele u (de la unsigned) şi/sau l (de la long) pentru a specifica un subtip anume (vezi secţiunea 2.3). Dacă valoarea constantei nu poate fi memorată ca subtip long signed int sau long unsigned int, atunci constanta se consideră constantă de tip flotant.

De exemplu, constanta de tip întreg 12 poate fi scrisă: 014 (în baza 8), 12 (în baza 10) sau 0xC (în baza 16). Dacă scriem 12l, constanta are tot valoarea 12, dar tipul este întreg lung, iar dacă scriem 12ul, tipul ei este întreg lung fără semn.

i În limbajul C, codificarea valorilor de tip întreg cu semn se face folosind codul complementar faţă de 2. În acest caz, bitul cel mai din stânga al numărului

Page 26: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

47

codifică semnul, iar restul biţilor valoarea. Bitul de semn este 0 pentru numere pozitive şi 1 pentru numere negative. În plus, valoarea în baza 10 a numerelor negative se calculează negând biţi de valoare şi adunând 1 la rezultat.

2.4.4. Constante de tip flotant O constantă de tip flotant este un număr real scris

în baza 10, cu sau fără zecimale, cu sau fără exponent. Exponentul este alcătuit din litera e urmată de un număr întreg n, cu semnificaţia că valoarea constantei este numărul dinaintea exponentului înmulţit cu 10n. Separator pentru zecimale este punctul. Dacă numărul nu are zecimale sau exponent, atunci trebuie urmat de un punct pentru a fi considerat real.

Tipul implicit al constantei este tipul double, dar dacă aceasta este urmată de litera f (de la float), tipul implicit va fi float (vezi secţiunea 2.3).

De exemplu, constanta cu valoarea -12,5 şi tip double se poate scrie: -12.5 sau -1.25e1. Dacă scriem -12.5f, atunci tipul este float.

2.5. Cuvinte cheie Un cuvânt cheie este o secvenţă caractere

alfanumerice, care are o semantică predefinită în limbajul C. Cuvintele cheie ale limbajului C (standardul ANSI) sunt: auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, int, long, register, return, short, signed,

Unităţi lexicale

48

sizeof, static, struct, switch, typedef, union, unsigned, void, volatile şi while.

2.6. Identificatori Un identificator este un nume pe care

programatorul îl dă unei entităţi (aceasta poate fi o variabilă – vezi secţiunea următoare, funcţie – vezi capitolul 7, sau tip de date – vezi capitolul 6) pentru a putea lucra mai uşor cu aceasta. Un identificator este o secvenţă de caractere alfanumerice şi underscore (_), care începe cu o literă sau underscore. Identificatorii trebuie să fie diferiţi de cuvintele cheie. De exemplu, identificatorii varsta, greutate, total1980, _secret sunt identificatori corecţi, dar 1980total, 1_secret nu sunt.

2.6.1. Variabile O variabilă este un identificator care are asociat

un tip, o adresă de memorie şi o valoare (i.e. informaţia din memorie de la adresa asociată variabilei). Înainte de a fi folosită în program, orice variabilă trebuie declarată, adică se specifică numele şi tipul variabilei. Tipul variabilei determină ce operaţii se pot efectua cu valoarea variabilei. Adresa de memorie unde va fi stocată valoarea variabilei se stabileşte automat. Valoarea variabilei poate fi specificată în faza de declarare (operaţie denumită iniţializare) sau ulterior (operaţie denumită atribuire).

Page 27: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

49

De exemplu, pentru a defini o variabilă de tip flotant dublu cu numele PI şi valoarea 3,1415926, şi două variabile de tip întreg fără semn cu numele varsta şi greutate, din care varsta este iniţializată cu valoarea 26 vom scrie: double PI = 3.1415926; unsigned int varsta = 26, greutate;

2.6.2. Masive Masivele (sau tablourile de date) sunt blocuri de

variabile de acelaşi tip, grupate în memorie, accesibile printr-un nume comun şi coordonatele lor în interiorul grupului. Declararea unui masiv şi accesarea elementelor acestuia se realizează cu ajutorul caracterelor paranteze pătrate (denumite în acest context operator de indexare). Indexarea masivelor se face în limbajul C, pornind de la 0. Masivele şi proprietăţile acestora sunt prezentate în capitolul 6. Deocamdată vom prezenta doar câteva exemple: float v[10]; // v este un masiv de 10 valori reale char c[65]; // c este un masiv de 65 caractere int mat[5][5]; // mat este un masiv de 5x5 valori intregi v[3]=0; // al 4-a valoare din v devine 0

i În limbajul C, o variabilă de tip şir de caractere cu maxim n caractere se declară ca un vector de tip char cu dimensiune n+1. Şirurile de caractere sunt discutate în capitolul 9.

Unităţi lexicale

50

2.7. Exemple 7. Programul următor exemplifică noţiunile discutate în acest capitol. Instrucţiunea de preprocesare din prima linie cere folosirea bibliotecii de funcţii stdio.h, care conţine funcţii standard de intrare/ieşire (vezi capitolul 4) precum printf. Urmează definiţia funcţiei main, punctul de pornire al oricărui program C. În corpul funcţiei main (între cele două acolade), se declară variabilele PI, raza şi aria, prima fiind şi iniţializată. Funcţia printf afişează pe ecran mesajul "Introduceţi raza", iar valoarea introdusă de utilizator este memorată în variabila raza cu ajutorul funcţiei scanf. Instrucţiunea următoare calculează aria unui cerc şi atribuie rezultatul variabilei aria. În final, funcţia printf afişează rezultatul calculelor. Codurile %g din mesajul funcţiei vor fi înlocuite cu valorile corespunzătoare parametrilor raza şi aria. Instrucţiunea return din final este opţională, dar scrierea ei este o practică bună, valoarea 0 simbolizând terminarea fără erori a programului. #include <stdio.h> #include <conio.h> int main() { float PI = 3.1415926, raza, aria; printf("Introduceti raza:"); scanf("%f", &raza); aria = PI * raza * raza; printf("Aria unui cerc cu raza %g este %g.\n", raza, aria); getch(); return 0; }

i Când este pornit din interfaţa grafică din Windows, fereastra programului dispare de pe ecran după rulare. Pentru a suspenda execuţia programului până când utilizatorul apasă o tastă am folosit

Page 28: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

51

instrucţiunea getch() din biblioteca conio.h conform indicaţiilor din secţiunea 14.3.1.

i Pentru a compila şi rula acest program, consultaţi Anexa 1 – Mediul de programare Dev-C++.

Rezultatul rulării programului este următorul: Introduceti raza:3.1 Aria unui cerc cu raza 3.1 este 30.1907.

8. Programul următor afişează suma a două numere întregi. #include <stdio.h> #include <conio.h> int main() { int a, b; printf("Introduceti a si b"); scanf("%i %i", &a, &b); printf("%i + %i = %i", a, b, a+b); getch(); }

2.8. Exerciţii 15. Determinaţi tipul unităţilor lexicale din exemplele anterioare. 16. Care dintre următoarele secvenţe sunt constante de tip caracter: 'a', a, "a", \a şi 'aa'? 17. Care dintre următoarele secvenţe sunt constante de tip şir de caractere: 'a', a, "a", "a"a", "a\"a", "a\\"a" şi "aa"? 18. Care dintre următoarele secvenţe sunt constante de tip întreg: a, 0xa, 0a, 10, 10.1, 10ul şi 10.? 19. Care dintre următoarele secvenţe sunt constante de tip flotant (simplu sau dublu): 10, 10.1, 10ul, 10.f şi 10.? 20. Care dintre următoarele secvenţe pot fi folosite ca identificator: test, test10, _test10, test_10, 10test, _10test şi _10?

Unităţi lexicale

52

21. Determinaţi tipul, numele şi valoarea următoarelor variabile: int test=10, real=010, fictiv=0x10; float greutate=1.3e2, densitate=3.2; char cod='a', adresa[30]="Constanta";

22. Modificaţi exemplul 7 astfel încât să calculeze volumul unui cilindru. 23. Scrieţi un program C care afişează pe ecran următorul desen folosind funcţia printf: ,-. _.---._ | `\.__.-'' ". \ _ _ ,. \ ,+++=.________________)_||______|_|_|| | (_.ooo.==================||======|=|=|| | ~~' | ~' "~' o o / \ /~'\ o o / `~' `-.____.-'

24. Scrieţi un program C care afişează pe ecran următorul desen folosind funcţia printf: . | . * | . . . . /-\ : |"""| . * |>~ /"""""\ . /\| _ __ ___|# # # #| ___ ___/<>\ |: _|~~| |'''|# # # #||"""| __|"""|^^| |: :|''|~~~~,|# # # #||"""|-|::|"""|''|_| :|''|""""||# # # #||"""|t|::|"""|''|""" |''|""""||# # # #||"""|||::|"""|''""""

Page 29: Bazele_programarii_calculatoarelor

53

333... EEEXXXPPPRRREEESSSIIIIII... OOOPPPEEERRRAAANNNZZZIII... OOOPPPEEERRRAAATTTOOORRRIII...

3.1. Expresii O expresie este o secvenţă de operanzi şi

operatori. Un operand poate fi o constantă, o variabilă sau o altă expresie. O expresie este caracterizată de o valoare şi un tip. Valoarea şi tipul expresiei sunt determinate de operatorii şi operanzii care formează expresia.

Regulile de asociativitate şi precedenţă a operatorilor sunt în general aceleaşi ca cele din matematică cu unele excepţii, dar o expresie poate conţine paranteze rotunde pentru schimbarea priorităţii de evaluare subexpresiilor sale. De exemplu, fie expresia: a = (a*b-c/d)*(a-b);

În această expresie a este primul operand al operatorului de atribuire =, iar (a*b-c/d)*(a-b) este al doilea operand. Al doilea operand (a*b-c/d)*(a-b), este o expresie în care expresia (a*b-c/d) este primul operand al operatorului de înmulţire *, iar expresia (a-b) este al doilea operand. Similar, expresia a*b este primul

Expresii. Operanzi. Operatori.

54

operand al operatorului de scădere -, iar expresia c/d este al doilea operand, ş.a.m.d.

Dacă variabilele a, b, c, d au valorile respectiv 2, 3, 6, 3, atunci expresia (a*b-c/d)*(a-b) are valoarea -4. Astfel variabilei a i se atribuie valoarea -4. În plus, în limbajul C, valoare unei expresii de atribuire este valoarea variabilei destinaţie după atribuire, deci valoarea întregii expresii va fi -4.

Valoarea logică de adevăr a expresiei a=(a*b-c/d)*(a-b) este “adevărat”, deoarece expresia are valoare nenulă.

i În limbajul C, o expresie are valoarea logică de adevăr „adevărat” dacă are valoare nenulă şi are valoarea logică de adevăr „fals” dacă are valoare nulă.

3.2. Operanzi Un operand poate fi: o constantă, o constantă

simbolică, un identificator de variabilă simplă, un identificator de tablou, un element de tablou, un identificator de structură, un membru al unei structuri, numele unei funcţii, un apel de funcţie sau o expresie.

3.3. Conversii implicite de tip În unele situaţii, în timpul evaluării unei expresii,

tipul unei subexpresii poate să fie schimbat automat în alt tip cu domeniul de valori mai mari. Această operaţie se numeşte conversie implicită de tip şi realizează astfel:

Page 30: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

55

• orice operand de tipul char se converteşte la tipul int;

• orice operand de tipul short se converteşte la tipul int. În plus, unii operatori prelucrează informaţiile

stocate în doi operanzi. Dacă operanzii au acelaşi tip, atunci rezultatul operatorului are acelaşi tip cu al operanzilor. Altfel, unul dintre operanzi se converteşte la tipul celuilalt, conform următoarelor reguli:

• dacă un operand este de tipul double atunci şi celălalt operand se converteşte la tipul double;

• dacă un operand este de tipul float atunci şi celălalt operand se converteşte la tipul float;

• dacă un operand este de tipul unsigned long atunci şi celălalt operand se converteşte la tipul unsigned long;

• dacă un operand este de tipul long atunci şi celălalt operand se converteşte la tipul long.

3.4. Operatori Limbajul C dispune de un set puternic de

operatori. Operatorii se pot clasifica după aritate, după asociere sau după prioritatea la evaluare într-o expresie. Din punct de vedere al arităţii (adică numărul de operanzi), operatorii sunt unari, binari şi ternari. Operatorii unari se scriu în general înaintea operandului lor (de exemplu: -5, !a, ~x, etc.). Excepţie de la această regulă fac operatorii de incrementare şi decrementare postfixaţi şi operatorul sizeof, a căror sintaxă este x++, x-- şi sizeof(expresie). Operatorii binari se scriu între

Expresii. Operanzi. Operatori.

56

operanzii lor (de exemplu: a+b, x<<3, etc.). Singurul operator ternar al limbajului C este operatorul condiţional.

Din punct de vedere al asociativităţii, operatorii pot fi asociativi de la stânga la dreapta sau de la dreapta la stânga. Cu excepţia operatorilor de atribuire, operatorului condiţional şi operatorilor unari, restul operatorilor au asociativitate de la stânga la dreapta.

În limbajul C, sunt definite 15 niveluri de precedenţă; nivelul 1 este cel mai prioritar, iar nivelul 15 este cel mai puţin prioritar. Operatorii aflaţi pe un anumit nivel de prioritate au aceeaşi prioritate. Prezentăm în ordine descrescătoare a precedenţei toţi operatorii limbajului C.

Tabel 3.1 Operatorii limbajului C

Ni-vel

Categorie Opera-tor

Aritate (asociati-vitate1)

Semnificaţie

( ) – Apel de funcţie sau schimbare prioritate

[ ] 2 (SD) Operator de indexare

. 2 (SD) Operator de selecţie directă

1 Operatori de adresare

-> 2 (SD) Operator de 1 SD – asociativitate de la stânga la dreapta, DS – asociativitate de la dreapta la stânga.

Page 31: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

57

selecţie indirectă

! 1 (DS) Negare logică ~ 1 (DS) Negare la

nivel de bit + - 1 (DS) Operator de

semn ++ -- 1 (DS,

SD) Incrementare, decrementare

& 1 (DS) Operator adresă

* 1 (DS) Operator de adresare indirectă

sizeof 1 (DS) Dimensiune în octeţi a operandului

2 Operatori unari

(tip) 1 (DS) Conversie explicită de tip (cast)

* 2 (SD) Înmulţire / 2 (SD) Împărţire

3 Operatori multiplicativi

% 2 (SD) Modulo + 2 (SD) Adunare 4 Operatori

aditivi - 2 (SD) Scădere << 2 (SD) Deplasare la

stânga 5 Operatori de

deplasare >> 2 (SD) Deplasare la

dreapta

Expresii. Operanzi. Operatori.

58

< 2 (SD) Mai mic <= 2 (SD) Mai mic sau

egal > 2 (SD) Mai mare

6 Operatori relaţionali

>= 2 (SD) Mai mare sau egal

== 2 (SD) Egal 7 Operatori de egalitate != 2 (SD) Diferit

8 & 2 (SD) ŞI pe biţi 9 ^ 2 (SD) SAU exclusiv

pe biţi 10

Operatori pe biţi

| 2 (SD) SAU pe biţi 11 && 2 (SD) ŞI logic 12

Operatori logici || 2 (SD) SAU logic

13 Operator ternar

?: 3 (DS) Operatorul condiţional

= 2 (DS) Atribuire simplă

*= 2 (DS) Înmulţire şi atribuire

/= 2 (DS) Împărţire şi atribuire

%= 2 (DS) Modulo şi atribuire

+= 2 (DS) Adunare şi atribuire

-= 2 (DS) Scădere şi atribuire

14 Operatori de atribuire

&= 2 (DS) ŞI pe biţi şi

Page 32: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

59

atribuire ^= 2 (DS) SAU exclusiv

pe biţi şi atribuire

|= 2 (DS) SAU pe biţi şi atribuire

<<= 2 (DS) Deplasare la stânga şi atribuire

>>= 2 (DS) Deplasare la dreapta şi atribuire

15 Operator secvenţial

, 2 (SD) Evaluare listă expresii

3.4.1. Operatori de adresare Operatorul de apelare „( )” este folosit ca

separator al listei de parametri a funcţiilor sau ca modificator de prioritate în evaluarea expresiilor. Operatorul de indexare „[ ]” este folosit pentru accesarea valorilor din tablouri de date şi va fi discutat în capitolul dedicat tablourilor. Operatorii de selecţie directă „.” şi de selecţie indirectă „->” se folosesc în gestiunea structurilor şi uniunilor şi vor fi discutaţi în capitolul 6.

De exemplu, instrucţiunea printf("Elementul 4 din vectorul v este %d", v[3]);

Expresii. Operanzi. Operatori.

60

foloseşte funcţia printf pentru a afişa un mesaj. Parametrii funcţiei sunt scrişi în operatorul de apelare, unul dintre aceştia fiind al patrulea element din vectorul v.

3.4.2. Operatori unari Operatorul de negare logică are rezultatul 1 dacă

valoarea operandului este nulă şi 0 dacă valoarea operandului este nenulă. Ca exemplu, fie secvenţa: int a = 21,b; b=!a; // b este 0 deoarece a e diferit de 0 b=!b; // b devine 1 deoarece b este 0

Operatorul de negare la nivel de bit schimbă toţi biţii operandului din 0 în 1 şi din 1 în 0. Ca exemplu, fie secvenţa: short int a = 33,b; b=~a;

Să determinăm valoarea lui b. Reprezentarea în baza 2, pe 16 de biţi a numărului 33 este: 0000.0000.0010.0001. Dacă se schimbă toţi biţii 0 în 1 şi toţi biţii 1 în 0 şi se obţine numărul: 1111.1111.1101.1110. Deoarece această valoare este scrisă în cod complementar faţă de 2, valoarea în baza 10 a lui b este -34.

Operatorul de semn plus are ca rezultat valoarea operandului, iar operatorul de semn minus are ca rezultat valoarea cu semn schimbat a operandului. De exemplu, int a = 33, b, c; b = -a; c = +b;

Page 33: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

61

Operatorii de adresă şi de adresare indirectă, având sintaxa &operand, respectiv, *operand, sunt folosiţi pentru operaţii cu adrese de memorie şi vor fi detaliaţi în capitolul 8.

Operatorii de incrementare şi decrementare au fiecare două forme: prefixată ++operand şi postfixată operand++, respectiv, –operand şi operand--. În forma prefixată valoarea operandului se măreşte cu 1 (adică se incrementează), respectiv se micşorează cu 1 (adică se decrementează), iar rezultatul expresiei este noua valoare. În forma postfixată, valoarea operandului se incrementează, respectiv decrementează, iar rezultatul expresiei este vechea valoare.

Operatorul sizeof are ca rezultat dimensiunea în octeţi a operandului (adică numărul de octeţi necesari pentru memorarea unei informaţii de acest tip). Operandul poate să fie o expresie sau un tip de dată. Dacă operandul este o expresie, atunci se evaluează expresia şi sizeof calculează dimensiunea în octeţi pentru tipul rezultatului. De exemplu, long b; char c; b=sizeof(c); // b = 1 (un caracter ocupa 1 octet) b=sizeof(-b);// b = 4 (un long int ocupa 4 octeti)

Operatorul de conversie explicită (denumit operator cast) este folosit atunci când este necesară conversia unei valori la un anumit tip. Sintaxa operatorului este: (tip)expresie, unde tip este tipul spre care se converteşte valoarea expresiei. De exemplu, operatorul

Expresii. Operanzi. Operatori.

62

de împărţire aplicat la doi operanzi întregi efectuează împărţirea întreagă (fără zecimale); pentru a calcula valoarea reală unul dintre operanzi trebuie transformat în tipul float sau double: int a = 6, b = 10; float f; f=a/b; // f=6/10 = 0 (impartire intreaga) f=(float)a/b;// f=6.0/10 = 0.6 (impartire reala)

3.4.3. Operatori multiplicativi Operatorul de înmulţire are sintaxa

operand1*operand2 şi rezultatul este produsul valorilor celor doi operanzi, cu eventuale conversii de tip.

Operatorul de împărţire are sintaxa operand1/operand2 şi rezultatul este câtul împărţirii primului operand la cel de-al doilea operand, cu eventuale conversii de tip. Dacă ambii operanzi sunt întregi, atunci operandul 2 trebuie să fie nenul.

Operatorul modulo are sintaxa operand1%operand2, se aplică numai operanzilor de tip întreg şi rezultatul este restul împărţirii primului operand la cel de-al doilea operand. Operandul 2 trebuie să fie nenul.

Pentru exemplificare considerăm secvenţa următoare: int a = 3, b = 6; double c = 8.1, d = 1.1; printf("%d\n", a * b);// Valoarea 18 de tip intreg printf("%f\n", a * d);// Valoarea 6.6 de tip real

Page 34: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

63

printf("%f\n", c * d);// Valoarea 8.91 de tip real printf("%d\n", b / a);// Valoarea 2 de tip intreg printf("%d\n", a / b);// Valoarea 0 de tip intreg printf("%f\n", c / a);// Valoarea 2.7 de tip real printf("%f\n", c / d);// Valoarea 7.36 de tip real printf("%d\n", a % b);// Valoarea 3 de tip intreg printf("%d\n", b % a);// Valoarea 0 de tip intreg

3.4.4. Operatori aditivi Operatorul de adunare are sintaxa

operand1+operand2 şi rezultatul este suma valorilor celor doi operanzi, cu eventuale conversii de tip. Operatorul de scădere are sintaxa operand1–operand2 şi rezultatul este diferenţa dintre valoarea primului şi a celui de-al doilea operand, cu eventuale conversii de tip.

Pentru exemplificare considerăm secvenţa următoare: int a = 3, b = 5; double c = 8.12, d = 1.1; printf("%d\n", a + b);// Valoarea 8 de tip intreg printf("%d\n", a – b);// Valoarea -2 de tip intreg printf("%f\n", c – b);// Valoarea 3.12 de tip real printf("%f\n", a + c);// Valoarea 11.12 de tip real printf("%f\n", c + d);// Valoarea 9.21 de tip real printf("%f\n", c – d);// Valoarea 7.02 de tip real

Expresii. Operanzi. Operatori.

64

3.4.5. Operatori pentru deplasare Operatorii de deplasare se aplică operanzilor de

tip întreg, rezultatul fiind de tip întreg. Operatorul de deplasare stânga are sintaxa: operand1<<operand2 şi rezultatul se obţine prin deplasarea la stânga a configuraţiei binare a primului operand, cu numărul de biţi dat de valoarea celui de-al doilea operand. Poziţiile binare rămase libere în dreapta se completează cu zerouri.

Operatorul de deplasare dreapta are sintaxa: operand1>>operand2 şi rezultatul se obţine prin deplasarea la dreapta a configuraţiei binare a primului operand, cu numărul de biţi dat de valoarea celui de-al doilea operand. Poziţiile binare rămase libere în stânga se completează cu bitul de semn pentru subtipurile cu semn (e.g. int, short, etc.), sau cu 0 pentru subtipurile fără semn (e.g. unsigned int, unsigned short, etc.).

i Deplasarea la stânga cu n poziţii binare este echivalentă cu înmulţirea cu 2n. Deplasarea la dreapta cu n poziţii binare este echivalentă cu împărţirea cu 2n. În ambele cazuri, valoarea finală nu poate depăşi domeniul de valori al tipului rezultatului.

De exemplu, fie secvenţa: short int a = 20, b = 2, c; c = a << b; // c este 80 d = a >> b; // d este 5

Pentru a calcula valoarea lui c, se reprezintă valoarea lui a în baza 2 (0000.0000.0001.0100), se

Page 35: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

65

deplasează cu 2 poziţii la stânga (0000.0000.0101.0000) şi se interpretează rezultatul în baza 10. Deci 20<<2 este 80.

Pentru a calcula valoarea lui d, se reprezintă valoarea lui a în baza 2 (0000.0000.0001.0100), se deplasează cu 2 poziţii la dreapta (0000.0000.0000.0101) şi se interpretează rezultatul în baza 10. Deci 20>>2 este 5.

3.4.6. Operatori relaţionali Operatorii relaţionali sunt folosiţi pentru tipurile

aritmetice de date şi pentru datele de tip pointer, având sintaxa operand1 operator operand2. Valoarea expresiei se obţine astfel: se evaluează cei doi operanzi; dacă valoarea primului operand este în relaţia dată de operator, cu al doilea operand, rezultatul expresiei este 1; altfel rezultatul expresiei este 0. De exemplu: int a = 20, b = 13, c; c = a<b; // c este 0 c = a<=b; // c este 0 c = a>b; // c este 1 c = a>=b; // c este 1

3.4.7. Operatori de egalitate Operatorii relaţionali sunt folosiţi pentru tipurile

aritmetice de date şi pentru datele de tip pointer, având sintaxa operand1 operator operand2. Valoarea expresiei se obţine astfel: se evaluează cei doi operanzi; dacă valoarea primului operand este în relaţia dată de

Expresii. Operanzi. Operatori.

66

operator, cu al doilea operand, rezultatul expresiei este 1; altfel rezultatul expresiei este 0.

De exemplu, fie secvenţa: int a = 20, b = 13, c; c = a==b; // c este 0 c = a!=b; // c este 1

3.4.8. Operatori pe biţi Operatori pe biţi binari folosesc operanzi de tip

întreg, iar rezultatul este de tip întreg, sintaxa generală fiind operand1 operator operand2. Funcţia operatorului se aplică fiecărei perechi de biţi de acelaşi rang din reprezentarea binară a operanzilor conform tabelului următor, iar rezultatul este dat de configuraţia de biţi care se obţine.

Tabel 3.2 Tabelul de adevăr al funcţiilor ŞI, SAU exclusiv şi SAU

a b a&b a^b a|b 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 1 1 1 0 1

De exemplu, fie secvenţa: short int a = 20, b = 13, c, d, e; c = a & b; // c este 4 d = a ^ b; // d este 25 e = a | b; // e este 29

Pentru a calcula valoarea lui c, se reprezintă în baza 2 valoarea lui a şi b (adică 0000.0000.0001.0100, respectiv 0000.0000.0000.1101); se aplică operatorul ŞI

Page 36: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

67

pentru fiecare pereche de biţi şi se obţine: 0000.0000.0000.0100. Deci, 20 & 13 este 4.

Pentru a calcula valoarea lui d, se reprezintă valoarea lui a şi b în baza 2 şi se aplică operatorul SAU exclusiv pentru fiecare pereche de biţi şi se obţine: 0000.0000.0001.1001. Deci, 20 ^ 13 este 25.

Pentru a calcula valoarea lui e, se reprezintă valoarea lui a şi b în baza 2 şi se aplică operatorul SAU pentru fiecare pereche de biţi şi se obţine: 0000.0000.0001.1001. Deci, 20 | 13 este 29.

3.4.9. Operatori logici Operatori logici binari se aplică valorilor

operanzilor conform funcţiei descrise în Tabel 3.2. Evaluarea se realizează prin scurt-circuit, adică evaluarea operanzilor se opreşte atunci când valoarea expresiei a fost stabilită şi nu mai poate fi schimbată de restul expresiei. De exemplu: short int a = 20, b = 0, c, d; c = a && b; // c este 0 (deoarece c este 0) d = a || b; // d este 1 (deoarece a este 20)

3.4.10. Operatorul condiţional Operatorul condiţional este singurul operator

ternar al limbajului C. Sintaxa sa este: operand1?operand2:operand3. Valoarea expresiei se obţine astfel: se evaluează expresia operand1; dacă valoarea acesteia este nenulă rezultatul expresiei este valoarea lui operand2, iar operand3 nu se mai

Expresii. Operanzi. Operatori.

68

evaluează; altfel, rezultatul expresiei este valoarea lui operand3, iar operand2 nu se mai evaluează.

3.4.11. Operatori de atribuire Operatorii de atribuire sunt de două tipuri: simplu

şi compuşi. Operatorul de atribuire simplă are sintaxa var=expr unde var este o variabilă sau un element dintr-un tablou, iar expr este o expresie. Valoarea expresiei de atribuire se calculează astfel: se evaluează expresia expr. Valoarea obţinută se atribuie variabilei var, cu eventuale conversii de tip, iar valoarea operaţiei este valoarea lui var. De exemplu, considerăm secvenţa: int a, b; char c; double d; a = 5; // a este 5 d = 12.21; // c este 12.21 b = a + 3.5; // b este 8 (se face conversie la int) b = b+d; // b este 20 (se face conversie la int) c = 'x'; // d este caracterul ‘x’

Pentru atribuirea simplă de forma: variabila = variabila operator expresie;

unde operator poate fi oricare dintre operatorii binari, se poate folosi operatorul compus operator=, astfel: variabila operator=expresie;

De exemplu: int a = 3, b = 5; a += b; // Echivalent cu a = a+b, deci a = 8

Page 37: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

69

Operatorii de atribuire se evaluează de la dreapta către stânga. În cazul unor atribuiri multiple, evaluarea expresiei se face începând din partea dreaptă şi la fiecare pas se aplică conversiile de tip necesare. De exemplu, int a = 5, b = 8; double d = 12.21; a = b = b+d; // b este 20 (datorita conversiei) // deci a este 20

În acest caz, întâi se evaluează expresia b+d, rezultatul fiind 20.21. Ulterior se atribuie valoarea variabilei b, nu înainte de a se realiza conversia de la tipul double (tipul expresiei) la tipul int (tipul variabilei b), deci b devine 20 şi valoarea expresiei b = b+d este tot 20. În continuare, a capătă valoarea 20 şi valoarea întregii expresii este 20.

3.4.12. Operatorul virgulă Operatorul virgulă este folosit pentru scrierea

unei expresii formată dintr-o listă de expresii, având sintaxa: operand1, operand2, ..., operandn. Valoarea expresiei se calculează astfel: se evaluează succesiv valorile lui operand1, operand2, ..., operandn. Tipul şi valoarea întregii expresii este tipul şi valoarea expresiei operandn. De exemplu, int a, b, c; a=2, b=5, c=b%2;

Întâi se atribuie variabilei a valoarea 2, apoi se atribuie variabilei b valoarea 5 şi în final variabilei c restul împărţirii valorii lui b la 2, adică se atribuie variabilei c

Expresii. Operanzi. Operatori.

70

valoarea 1. Valoarea expresiei a=2,b=5,c=b%a este 1 (valoarea expresiei c=b%a), iar tipul ei este int (tipul expresiei c=b%a).

3.5. Exemple 9. Programul următor exemplifică funcţionarea operatorilor unari !, ~, ++, ––, &, *. #include <stdio.h> #include <conio.h> int main() { int val, temp, rez, *pint; char oper; printf("Testarea operatorilor unari.\n"); printf("Introduceti operandul:"); scanf("%d",&val); printf("Introduceti operatorul:"); scanf(" %c",&oper); switch(oper) { case '!': // Operatorul ! rez=!val; printf("!%d = %d", val, rez); break; case '~': // Operatorul ~ rez=~val; printf("~%d = %d", val, rez); break; case '+': // Operatorul ++ temp=val; // pastram valoarea initiala rez=++val; printf("++%d = %d\n", temp, rez); val=temp; // refacem valoarea initiala rez=val++; printf("%d++ = %d", temp, rez); break; case '-': // Operatorul -- temp=val; // pastram valoarea initiala rez=--val;

Page 38: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

71

printf("--%d = %d\n", temp, rez); val=temp; // refacem valoarea initiala rez=val--; printf("%d-- = %d", temp, rez); break; case '&': // Operatorul & case '*': // Operatorul * pint=&val; rez=*pint; printf("Adresa variabilei este %p\n", pint); printf("Valoarea de la adresa %p este %d", pint, *pint); break; default: printf("Operator necunoscut"); } getch(); }

Câteva dintre rezultatele produse de acest program sunt: Testarea operatorilor unari Introduceti operandul: 21 Introduceti operatorul: ! !21 = 0 Testarea operatorilor unari Introduceti operandul: 33 Introduceti operatorul: ~ ~33 = -34 Testarea operatorilor unari Introduceti operandul: 12 Introduceti operatorul: ++ ++12 = 13 12++ = 13 Testarea operatorilor unari Introduceti operatorul: & Introduceti operandul: 3 Adresa variabilei este FFF4

Expresii. Operanzi. Operatori.

72

Continutul de la adresa FFF4 este 3

10. Programul următor exemplifică funcţionarea operatorilor binari pe biţi. #include <stdio.h> #include <conio.h> int main() { int op1,op2,rez; char oper; printf("Testarea operatorilor pe biti.\n"); printf("Introduceti operandul 1:"); scanf("%d",&op1); printf("Introduceti operandul 2:"); scanf("%d",&op2); printf("Introduceti operatorul:"); scanf(" %c",&oper); switch(oper) { case '<': rez = op1<<op2; printf("%d<<%d = %d", op1, op2, rez); break; case '>': rez = op1>>op2; printf("%d>>%d = %d", op1, op2, rez); break; case '&': rez = op1&op2; printf("%d&%d = %d", op1, op2, rez); break; case '|': rez = op1|op2; printf("%d|%d = %d", op1, op2, rez); break; case '^': rez = op1^op2; printf("%d^%d = %d", op1, op2, rez); break; default: printf("Operator necunoscut"); }

Page 39: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

73

getch(); }

Câteva dintre rezultatele produse de acest program sunt: Testarea operatorilor pe biţi. Introduceti operandul 1: 20 Introduceti operandul 2: 2 Introduceti operatorul: << 20<<2 = 80 Introduceti operandul 1: 21 Introduceti operandul 2: 7 Introduceti operatorul: & 21&7 = 5

11. Programul următor prezintă funcţionarea operatorului ternar. #include <stdio.h> #include <conio.h> int main() { int a, b; printf("Testarea operatorului conditional.\n"); printf("Introduceti valoarea lui a:"); scanf("%d",&a); printf("Introduceti valoarea lui b:"); scanf("%d",&b); printf( a<b ? "%d e mai mic ca %d" : "%d e mai mare ca %d", a, b); getch(); }

Pentru a=3 şi b=5, deoarece expresia a<b este adevărată (deci nenulă), formatul de afişare al funcţiei printf este "%d e mai mic ca %d". În acest caz, rezultatul programului este: Testarea operatorului conditional.

Expresii. Operanzi. Operatori.

74

Introduceti valoarea lui a: 3 Introduceti valoarea lui b: 5 3 e mai mic ca 5

12. Programul următor exemplifică funcţionarea operatorilor de atribuire compuşi. #include <stdio.h> #include <conio.h> int main() { int x = 5; int n1 = 3, n2 = 2, n3 = 1, n4 = 4, n5 = 5; printf("Initial, x = %d.\n", x); printf("x+=%d este %d.\n", n1, x += n1); printf("x-=%d este %d.\n", n2, x -= n2); printf("x*=%d este %d.\n", n3, x *= n3); printf("x /= %d este %d.\n", n4, x/=n4); printf("x %%= %d este %d.\n", n5, x%=n5); printf("In final, x este %d.\n", x); getch(); }

Rezultatul execuţiei acestui program este: Valoarea initiala a lui x este 5. valoarea lui x += 3 este 8. Valoarea lui x -= 2 este 6. Valoarea lui x *= 1 este 6. Valoarea lui x /= 4 este 1. Valoarea lui x %= 5 este 1. Valoarea finala a lui x este 1.

13. Programul următor determină maximul a patru numere folosind operatorul condiţional. După citirea datelor, primul număr este ales ca maxim. În continuare, valoarea variabilei max este actualizată dacă se găseşte un număr mai mare decât ea. #include <conio.h> #include <stdio.h> int main() {

Page 40: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

75

float x, y, z, t, max; printf("Introduceti numerele:"); scanf("%f%f%f%f", &x, &y, &z, &t); max = x; max = max > y ? max : y; max = max > z ? max : z; max = max > t ? max : t; printf("Maximul este %f", max); getch(); }

Rezultatul execuţiei acestui program este: Introduceti numerele:2 3 5 1 Maximul este 5.000000

14. Program următor calculează numărul total de secunde care sunt egale cu h ore, m minute şi s secunde (citite de la tastatură). #include <conio.h> #include <stdio.h> int main() { int h, m, s, t; printf("Introduceti timpul in format h:m:s: "); scanf("%i:%i:%i", &h, &m, &s); t = h * 3600 + m * 60 + s; printf("Total secunde: %i", t); getch(); }

i Formatul "%i:%i:%i" determină funcţia scanf să citească de la tastatură valori întregi separate prin două puncte. Valorile vor sunt memorate în variabilele h, m şi s, iar caracterele două puncte sunt ignorate.

Rezultatul execuţiei acestui program este: Introduceti timpul in format h:m:s: 1:3:10 Total secunde: 3790

Expresii. Operanzi. Operatori.

76

15. Sistemul RGB permite codificarea fiecărei culori printr-un triplet de numere naturale care reprezintă intensităţile culorilor de bază roşu (Red), verde (Green) şi albastru (Blue). Fiecare componentă a tripletului poate avea valori din mulţimea { }255,,1,0 K . Tripletul poate fi codificat într-un întreg în baza 16, cu valori din mulţimea { }FFFFFF,,1,0 K , primele două cifre hexazecimale reprezentând intensitatea culorii roşu, următoarele două cifre hexazecimale reprezentând intensitatea culorii verde, iar ultimele două pentru albastru. Programul următor citeşte de la tastatură un întreg în format hexazecimal din mulţimea { }FFFFFF,,1,0 K şi calculează şi afişează intensităţile culorilor de bază. // Codificare culori #include "stdio.h" #include "conio.h" int main() { int cod,R,G,B; printf(" Tastati codul de culoare in baza 16 "); scanf("%x",&cod); if(cod>=0 && cod<=0xffffff) { printf(" Ati tastat codul %x \n",cod); B=cod%256; G=(cod>>8)%256; R=(cod>>16); printf(" R = %d \n",R); printf(" G = %d \n",G); printf(" B = %d \n",B); } else { printf(" Cod invalid \n"); } getch(); }

Rezultatul execuţiei este:

Page 41: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

77

Tastati codul de culoare in baza 16 ABCDEF Ati tastat codul abcdef R = 171 G = 205 B = 239

3.6. Exerciţii 25. Scrieţi un program C care citeşte de la tastatură o temperatură în grade Celsius şi afişează pe ecran temperatura în grade Farenheit şi Kelvin. 26. Scrieţi un program C care citeşte de la tastatură un număr întreg şi determină dacă acesta este divizibil cu 2, 3, 5, 10 şi 100. 27. Scrieţi un program C care determină perimetrul şi aria unui dreptunghi cunoscând lungimile laturilor. 28. Scrieţi un program C care determină consumul de combustibil la o sută de kilometri al unui vehicul ştiind consumul total pe parcursul unei anumite distanţe. 29. Scrieţi un program C care citeşte de la tastatură un număr natural şi afişează cei mai puţin semnificativi trei biţi din reprezentarea binară a numărului respectiv. 30. Scrieţi un program C care determină minimul a opt numere reale citite de la tastatură (folosiţi cât mai puţine comparaţii). 31. Scrieţi un program C care funcţionează ca un minicalculator: utilizatorul introduce o valoarea, un operator aritmetic şi apoi o altă valoare, iar programul afişează rezultatul operaţiei aritmetice. 32. Scrieţi un program C care calculează numărul maxim de ore, minute şi secunde care sunt egale cu un număr n de secunde (citit de la tastatură). 33. Scrieţi un program C care adună două intervale de timp exprimate în ore, minute şi secunde. Rezultatul trebuie exprimat tot în ore, minute şi secunde. În plus,

Expresii. Operanzi. Operatori.

78

numărul de secunde şi cel de minute trebuie să fie cuprinse între 0 ş i 59. 34. Scrieţi un program C care afişează valoarea funcţiei f(x,y)=2,5x2 + 3xy + 1,2y2, pentru valori x şi y citite de la tastatură cu ajutorul funcţiei scanf. 35. Scrieţi un program C care calculează media ponderată a trei numere naturale. Primul are pondere 20%, al doilea 30%, iar ultimul 50%.

Page 42: Bazele_programarii_calculatoarelor

79

444... IIINNNSSSTTTRRRUUUCCCŢŢŢIIIUUUNNNIII

Instrucţiunile unui program C determină operaţiile pe care acesta le efectuează. O instrucţiune este formată din cuvinte cheie, expresii şi/sau alte instrucţiuni. O instrucţiune care apare în interiorul altei instrucţiuni formează corpul acesteia din urmă.

O instrucţiune compusă este un grup de zero sau mai multe instrucţiuni incluse într-o pereche de acolade. Efectul execuţiei acesteia este acela al execuţiei rând pe rând a instrucţiunilor care o compun.

Cu excepţia instrucţiunilor compuse, toate instrucţiunile se termină prin punct şi virgulă. Instrucţiunea vidă este o instrucţiune care nu face nimic, fiind reprezentată de un singur caracter punct şi virgulă. Ea se foloseşte în locurile unde este necesară o instrucţiune, dar nu vrem să se efectueze nici o operaţie.

4.1. Instrucţiuni expresie O instrucţiune expresie este o expresie compusă

din operanzi şi operatorii limbajului C (vezi capitolul 3).

Instrucţiuni

80

Efectul execuţiei acesteia este acela al evaluării expresiei care o compune. Sintaxa instrucţiunii este următoare: expresie;

4.2. Instrucţiuni de decizie

4.2.1. Instrucţiunea if Instrucţiunea if permite condiţionarea execuţiei

unei instrucţiuni de valoarea de adevăr a unei expresii. Sintaxa instrucţiunii este următoare: if (conditie) instructiune1; else instructiune2;

Ramura else şi instrucţiune2 pot fi omise, obţinându-se varianta scurtă a instrucţiunii. Efectul instrucţiunii este următorul: se evaluează expresia conditie; dacă valoarea acesteia este diferită de zero, atunci se execută instrucţiune1, altfel se execută instrucţiune2 (dacă există ramura else).

Instrucţiunile instrucţiune1 şi instrucţiune2 pot fi la rândul lor instrucţiuni if. În această situaţie, compilatorul C asociază fiecare else cu cel mai recent if care nu are asociat un else. Programatorul poate modifica acest comportament prin folosirea acoladelor (instrucţiunea if fără else devine astfel o instrucţiune compusă). De exemplu, în secvenţa: if(i>=0) if(i<=10) j = 1;

Page 43: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

81

else j = 2;

dacă i este între 0 şi 10, atunci valoarea variabilei j devine 1; dacă i este mai mare ca 10, atunci j devine 2; dacă i este mai mic decât 0, j nu se modifică. În exemplul următor: if(i>=0) { if(i<=10) j = 1; } else j = 2;

dacă i este între 0 şi 10, atunci valoarea variabilei j devine 1; dacă i este mai mare decât 10, valoarea variabilei j nu se modifică; dacă i este mai mic decât 0, j devine 2.

4.2.2. Instrucţiunea switch La fel ca if, Instrucţiunea switch permite

condiţionarea execuţiei unei instrucţiuni de valoarea unei expresii, executând automat testele de egalitate. Sintaxa instrucţiunii este următoare: switch(expresie) { case constanta1: instructiune; ... case constanta2: instructiune; ... ... default: instructiune; ... ... case constantan:

Instrucţiuni

82

instructiune; ... }

Oricare variantă case poate să nu conţină instrucţiuni. Varianta implicită (default) şi instrucţiunile aferente acesteia pot fi omise. Efectul instrucţiunii este următorul: se evaluează instrucţiunea expresie o singură dată. Valoarea acesteia se compară pe rând cu valorile constanta1, constanta2, ..., constantan. Dacă există o valoare egală cu cea a expresiei, atunci se execută instrucţiunile începând de la acel caz până la sfârşitul instrucţiunii switch sau până la prima instrucţiune break. Dacă nu există nici o valoare egală, şi există cazul implicit se execută instrucţiunile începând de la acesta până la sfârşitul instrucţiunii switch sau până la prima instrucţiune break. Dacă nu există nici o valoare egală şi nu există cazul implicit, atunci nu se mai întâmplă nimic.

De exemplu, în secvenţa următoare: switch(i) { case 0: j=1; break; case 1: case 2: j=2; case 3: j=3; break; default: j = 0; }

dacă i are valoarea 0, atunci valoarea lui j devine 1; dacă i are valoarea 1 sau 2, atunci valoarea lui j devine 2; dacă i are valoarea 3, atunci valoarea lui j devine 3, iar pentru alte valori ale lui i, valoarea lui j devine 0.

Page 44: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

83

4.3. Instrucţiuni de ciclare

4.3.1. Instrucţiunea do-while Instrucţiunea do-while permite execuţia repetată

a unei instrucţiuni cât timp o expresie are valoare nenulă (adică adevărat). Sintaxa instrucţiunii este: do instructiune; while(conditie);

Efectul instrucţiunii este următorul: se execută corpul lui do-while (instrucţiunea instructiune) şi apoi se evaluează expresia condiţie. Dacă valoarea acesteia este 0, atunci se trece la următoarea instrucţiune, altfel se repetă instrucţiunea do-while curentă. De remarcat că testul se execută la sfârşit, deci instructiune se execută măcar o dată.

În exemplul următor: int i=3, j=1; do j=j+1; while (i>j);

se executa instrucţiunea j=j+1 (deci j devine 2) şi deoarece 3>2 (adică i>j) este adevărată se repetă instrucţiunea do-while; se executa instrucţiunea j=j+1 (deci j devine 3) şi deoarece 3>3 este falsă, se termină instrucţiunea do-while.

Instrucţiuni

84

4.3.2. Instrucţiunea while Instrucţiunea while permite execuţia repetată a

unei instrucţiuni cât timp o expresie are valoarea nenulă (adică adevărat). Sintaxa instrucţiunii este: while(conditie) instructiune;

Efectul instrucţiunii este următorul: se evaluează expresia condiţie. Dacă valoarea acesteia este 0, atunci se trece la următoarea instrucţiune de după while (nu se execută instrucţiunea din corpul lui while), altfel se execută corpul lui while (instrucţiunea instructiune) şi se repetă instrucţiunea while curentă. De remarcat că testul se execută la început, deci e posibil ca instructiune să nu se execute nici o dată.

În exemplul următor: int i=3, j=1; while (i>j) j=j+1;

deoarece 3>1 (adică i>j) este adevărată, se executa instrucţiunea j=j+1 (deci j devine 2) şi se repetă instrucţiunea while; deoarece 3>2 (adică i>j) este adevărată, se execută instrucţiunea j=j+1 (deci j devine 3) şi se repetă instrucţiunea while; deoarece 3>3 este falsă, se termină instrucţiunea while.

4.3.3. Instrucţiunea for Instrucţiunea for permite execuţia repetată a unei

instrucţiuni cât timp o expresie are valoarea nenulă (adică adevărat). Sintaxa instrucţiunii este următoare:

Page 45: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

85

for(initializare; conditie; actualizare) instructiune;

Efectul instrucţiunii este următorul: se evaluează expresia initializare o singură dată. Se evaluează expresia condiţie. Dacă valoarea acesteia este 0, atunci se trece la următoarea instrucţiune de după for (nu se execută instrucţiunea din corpul lui for), altfel se execută corpul lui for (instrucţiunea instructiune), se evaluează expresia actualizare şi se repetă instrucţiunea for curentă, dar fără expresia de iniţializare.

De remarcat că expresia de iniţializare se execută o singură dată, iar apoi testul de repetare se execută la început, deci e posibil ca instructiune şi actualizare să nu se execute nici o dată. Expresiile de iniţializare, condiţie şi actualizare sunt opţionale, dar caracterele punct şi virgulă trebuie scrise. Dacă lipseşte expresia conditie atunci valoarea ei se consideră nenulă. O instrucţiune for poate fi rescrisă folosind instrucţiunea while astfel: initializare; while(conditie) { instructiune; actualizare; }

În exemplul următor: int i, j; for(i = 1, j = 0; i <= 5; ++i) j = j+i;

Instrucţiuni

86

instrucţiunea for produce execuţia instrucţiunii j=j+i de cinci ori, cu valori diferite pentru i. Astfel, in final valoare lui j este 15 (adică 1+2+3+4+5).

4.4. Instrucţiuni de salt

4.4.1. Instrucţiunea break Aşa cum s-a văzut în secţiunea 4.2.2,

instrucţiunea break poate fi folosită pentru a forţa terminarea unei instrucţiuni switch. De asemenea, instrucţiunea break poate fi folosită pentru a forţa terminarea instrucţiunilor de ciclare for, while şi do-while. În acest caz, execuţia instrucţiunii break determină întreruperea instrucţiunii de ciclare curente şi continuarea execuţiei cu prima instrucţiune după aceasta.

În exemplul următor: int i, j=0; for(i = 1; i <= 5; ++i) { if (i == 4) break; j = j+i; }

instrucţiunea for produce (în mod normal) execuţia instrucţiunii j=j+i de cinci ori, cu valori diferite pentru i. Totuşi, când i ajunge la valoarea 4, instrucţiunea if determină execuţia instrucţiunii break, terminându-se astfel instrucţiunea for. Astfel, în final valoare lui j este 6 (adică 1+2+3).

Page 46: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

87

4.4.2. Instrucţiunea continue Instrucţiunea continue poate fi folosită pentru a

forţa terminarea execuţiei corpului instrucţiunii de ciclare. Cu toate aceste, instrucţiunea de ciclare îşi continuă execuţia prin evaluarea expresiei de actualizare (în cazul lui for) şi a expresiei condiţie. În exemplul următor: int i, j=0; for(i = 1; i <= 5; ++i) { if (i == 4) continue; j = j+i; }

instrucţiunea for produce (în mod normal) execuţia instrucţiunii j=j+i de cinci ori, cu valori diferite pentru i. Totuşi, când i ajunge la valoarea 4, instrucţiunea if determină execuţia instrucţiunii continue, terminându-se astfel corpul instrucţiunii for. Instrucţiunea for continuă cu expresia de actualizare şi cu iteraţia următoare. Astfel, in final valoare lui j este 11 (adică 1+2+3+5).

4.4.3. Instrucţiunea goto Instrucţiunea goto determină transferul execuţiei

programului la o anumită instrucţiune. Sintaxa instrucţiunii este următoarea: goto eticheta;

Efectul instrucţiunii este următorul: execuţia programului continuă cu prima instrucţiune după eticheta eticheta. În general, se recomandă a se evita folosirea instrucţiunii goto.

Instrucţiuni

88

O etichetă este un identificator unic (în interiorul funcţiei în care este declarat) asociat unei instrucţiuni şi care are semnificaţie doar pentru instrucţiunea goto. O etichetă se scrie înaintea instrucţiunii pe care o denumeşte şi este urmată de caracterul două puncte. De exemplu, în secvenţa următoare: int i=3, j=2; if (i < j) goto maimic; j = i; goto gata; maimic: i = j; // aici este o eticheta gata:; // aici este alta eticheta

dacă i este mai mic decât j, atunci execuţia continuă de la instrucţiunea cu eticheta maimic (adică cu i=j), altfel se execută instrucţiunea j=i şi se sare la instrucţiunea (vidă) cu eticheta gata.

4.5. Exemple 16. Următorul program determină soluţia unei ecuaţii de gradul 1 cu coeficienţi reali, de forma ax+b = 0. #include <stdio.h> #include <conio.h> int main() { float a, b; printf("Introduceti a: "); scanf("%f", &a); printf("Introduceti b: "); scanf("%f", &b); if(a == 0) if(b == 0) printf("Orice numar real e solutie"); else

Page 47: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

89

printf("Nu exista solutii reale"); else { float x = -b/a; printf("Solutie: %f", x); } getch(); }

Rezultatul rulării pentru ecuaţia 3x+8=0 este: Introduceti a: 3 Introduceti b: 8 Solutie: -2.666667

17. Următorul program determină soluţiile reale ale unei ecuaţii de gradul 2 cu coeficienţi reali, de forma ax2+bx+c=0. Dacă a este nul, rezolvarea ecuaţiei se reduce la rezolvarea unei ecuaţii de gradul 1. Pentru a calcula rădăcina pătrata a lui d vom folosi funcţia sqrt din biblioteca math.h. #include <stdio.h> #include <math.h> #include <conio.h> int main() { float a, b, c; printf("Introduceti a: "); scanf("%f", &a); printf("Introduceti b: "); scanf("%f", &b); printf("Introduceti c: "); scanf("%f", &c); if(a == 0) { if(b == 0) if(c == 0) printf("Orice numar real e solutie"); else printf("Nu exista solutii reale"); else {

Instrucţiuni

90

float x = -c/b; printf("Solutie: %f", x); } } else { float d = b*b-4*a*c; if(d < 0) printf("Ecuatia nu are solutii reale"); else { float x1 = (-b-sqrt(d))/(2*a); float x2 = (-b+sqrt(d))/(2*a); printf("Solutii: %f si %f", x1, x2); } } getch(); }

Rezultatul rulării pentru ecuaţia x2+3x+2=0 este: Introduceti a: 1 Introduceti b: 3 Introduceti c: 2 Solutii: -2.000000 si -1.000000

18. Următorul program calculează valoarea n! pentru n număr natural după formula n!=1·2·...·n. #include <stdio.h> #include <conio.h> int main() { int i, n, nf; printf("Valoarea lui n este : "); scanf("%d",&n); nf=1; for(int i=1; i<=n; ++i) nf *= i; printf("%d! = %d\n",n, nf); getch(); }

Rezultatul rulării pentru n=6 este:

Page 48: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

91

Valoarea lui n este : 6 6! = 720

19. Fiind dat un număr natural n nenul să se determine numărul de zerouri cu care se termină n!. //Numrul ultimelor zerouri ale lui n! #include<stdio.h> #include<conio.h> int main () {int n,i,k; long nfact; printf("n="); scanf("%d", &n); nfact=1; for(i=1; i<=n; i++) { nfact=nfact*i; } printf(" %d! = %d se termina cu ",n,nfact); k=0; while(nfact%10==0) { k++; nfact=nfact/10; } printf(" %d zerouri \n", k); getch(); }

Un exemplu de rulare este următorul: n=11 11! = 39916800 se termina cu 2 zerouri

20. Următorul program calculează S(n)=1!+2!+...+n!, pentru n un număr natural. Prima variantă, foloseşte directe formula lui S, ceea ce înseamnă că fiecare termen este calculat separat. #include <stdio.h> #include <conio.h> int main() {

Instrucţiuni

92

int s, i, j, n, nf; printf("Valoarea lui n este : "); scanf("%d",&n); s=0; for(int i=1; i<=n; ++i) { nf=1; for(int j=1; j<=i; ++j) nf *= j; s = s + nf; } printf("S(%d) = %d\n", n, s); getch(); }

21. O variantă mai eficientă pentru exemplu anterior obţinem dacă observăm că putem refolosi valorile calculate la pasul anterior pentru a calcula valoarea curentă. #include <stdio.h> #include <conio.h> int main() { int s, i, n, nf; printf("Valoarea lui n este : "); scanf("%d",&n); s=0; nf=1; for(int i=1; i<=n; ++i) { nf *= i; s = s + nf; } printf("S(%d) = %d\n", n, s); getch(); }

În ambele cazuri, rezultatul rulării pentru n=6 este: Valoarea lui n este : 6 S(6) = 873

Page 49: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

93

22. Programul următor afişează primii n termeni ai şirului lui Fibonacci, definit prin: :fib N N→ , ( )0 1fib = ,

( )1 1fib = , ( ) ( )( 1) 2fib n fib n fib n= − + − , ( ) 2n∀ ≥ . Pentru a-i calcula, vom folosi următoarea metodă: notăm cu ti termenul la pasul i, cu ti+1 termenul următor şi cu ti+2 termenul de după acesta. Astfel, pentru fiecare valoare a lui i, afişăm valoarea ti, calculăm ti+2 şi facem trecerea de la i la i+1 (adică ti=ti+1 şi ti+1=ti+2). #include <stdio.h> #include <conio.h> int main() { int i, n, ti, ti_plus_1, ti_plus_2; printf("Valoarea lui n este : "); scanf("%d",&n); ti=ti_plus_1=1; for(int i=0; i<n; ++i) { printf("Fib(%i) = %i\n", i, ti); ti_plus_2 = ti_plus_1 + ti; ti = ti_plus_1; ti_plus_1 = ti_plus_2; } getch(); }

Rezultatul rulării pentru n=7 este: Valoarea lui n este : 7 Fib(0) = 1 Fib(1) = 1 Fib(2) = 2 Fib(3) = 3 Fib(4) = 5 Fib(5) = 8 Fib(6) = 13

23. Fiind dat un număr natural n nenul, să se determine descompunerea lui n în sumă de termeni ai şirului lui Fibonacci.

Instrucţiuni

94

#include "stdio.h" #include "conio.h" int main() { int n,x0,x1,xn,xn1,xn2,dif; printf(" n = "); scanf("%d",&n); if(n>1) { x0=1; x1=1; dif=n; while(dif>2) { xn=x0; xn1=x1; xn2=xn1+xn; while(xn2<dif) { xn=xn1; xn1=xn2; xn2=xn1+xn; } printf(" %d ",xn1); dif=dif-xn1; } if(dif==2) printf(" %d ",x1+x0); else printf(" %d ",x0); } else printf(" Valoarea introdusa nu este in domeniu \n"); getch(); }

Un exemplu de rulare a programului este: n = 27 21 5 1

24. Este cunoscut faptul că există numere naturale care se pot reprezenta ca suma a trei cuburi distincte. De

Page 50: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

95

exemplu 3 3 336 1 2 3 1 8 27= + + = + + . Programul următor determină toate numerele naturale din mulţimea { }1,2, ,kK care au această proprietate, k citit de la tastatură. // Test suma de trei cuburi distincte #include "stdio.h" #include "conio.h" int main() { int k,n,c1,c2,c3,i,dif; printf(" k = "); scanf("%d",&k); for(n=1;n<=k;n++) { i=1; while(i*i*i<n) { c3=i*i*i; i++; } dif=n-c3; i=1; while(i*i*i<dif) { c2=i*i*i; i++; } dif=dif-c2; if(dif!=1) { i=1; while(i*i*i<=dif) { c1=i*i*i; i++; } } else c1=1; if(n==c1+c2+c3 && c1!=c2 && c1!=c3 && c2!=c3)

Instrucţiuni

96

{ printf(" %d satisface proprietatea \n",n); printf(" %d = %d + %d + %d \n",n,c1,c2,c3); } } getch(); }

Un exemplu de rulare este: k = 100 36 satisface proprietatea 36 = 1 + 8 + 27 73 satisface proprietatea 73 = 1 + 8 + 64 92 satisface proprietatea 92 = 1 + 27 + 64 99 satisface proprietatea 99 = 8 + 27 + 64

25. Fie a şi b două numere naturale nenule. Determinarea produsului celor două numere poate fi făcută cu algoritmul rusesc de înmulţire. Fie suma o variabilă care, în final va memora valoarea produsului

*a b . Dacă a este impar se face atribuirea suma=suma+b. Dacă a este par se împarte a la doi şi se înmulţeşte b cu doi. Algoritmul continuă cât timp a>=1. În final variabila suma are valoarea egală cu cea a produsului *a b . Programul următor codifică algoritmul rusesc de înmulţire. // Produsul rusesc #include "stdio.h" #include "conio.h" int main() { int a,b,sum,asalv,bsalv; printf(" a = "); scanf("%d",&a); printf(" b = "); scanf("%d",&b); asalv=a;

Page 51: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

97

bsalv=b; printf(" a b produs \n"); sum=0; while(a>=1) { printf(" %d %d %d \n",a,b,sum); while(a%2==0) { a=a/2; b=b*2; } sum=sum+b; printf(" %d %d %d \n",a,b,sum); a=a/2; b=b*2; } printf(" %d * %d = %d \n",asalv,bsalv,sum); getch(); }

Rularea programului produce rezultatele: a = 24 b = 25 a b produs 24 25 0 3 200 200 1 400 200 1 400 600 24 * 25 = 600 a = 93 b = 10 a b produs 93 10 0 93 10 10 46 20 10 23 40 50 11 80 50 11 80 130 5 160 130 5 160 290 2 320 290 1 640 930

Instrucţiuni

98

93 * 10 = 930

26. Următorul program aproximează numărul π folosind

formula 21

16n

ni i

π=

= ∑ . Valoarea aproximativă este afişată

la fiecare 1000 de paşi şi la ultimul pas. #include <stdio.h> #include <conio.h> #include <math.h> int main() { int n; double s, p; printf("Valoarea lui n este : "); scanf("%d",&n); s = 0; for(int i=1; i<=n; ++i) { s += 1.0/(i*i); if(i == n || i % 1000 == 1) { p = sqrt(6*s); printf("pi(%i) = %f\n", i, p); } } getch(); }

Rezultatul rulării pentru n=10000 este: Valoarea lui n este : 10000 pi(1) = 2.449490 pi(1001) = 3.140639 pi(2001) = 3.141116 pi(3001) = 3.141274 pi(4001) = 3.141354 pi(5001) = 3.141402 pi(6001) = 3.141434 pi(7001) = 3.141456 pi(8001) = 3.141473 pi(9001) = 3.141487

Page 52: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

99

pi(10000) = 3.141497

27. Următorul program calculează suma cifrelor unui număr natural. Cât timp valoarea lui n este nenulă, programul foloseşte operatorul % pentru a obţine ultima cifră şi operatorul / pentru a elimina această cifră din număr. #include <stdio.h> #include <conio.h> int main() { int c, n, s; printf("Valoarea lui n este : "); scanf("%d",&n); s=0; while (n>0) { c = n%10; // restul impartirii n = n/10; // catul impartirii printf("Adun %d la suma. A mai ramas %d\n", c, n); s += c; } printf("Suma cifrelor este %d\n",s); getch(); }

Pentru valoarea 12345 programul furnizează rezultatele: Valoarea lui n este : 12345 Adun 5 la suma. A mai ramas 1234 Adun 4 la suma. A mai ramas 123 Adun 3 la suma. A mai ramas 12 Adun 2 la suma. A mai ramas 1 Adun 1 la suma. A mai ramas 0 Suma cifrelor este 15

28. Următorul program determină răsturnatul unui număr natural. În acest context, pentru 1 2... kn a a a= , se

Instrucţiuni

100

numeşte răsturnatul lui n numărul natural 1 1...k ka a a− . De exemplu, răsturnatul numărului 12345 este numărul 54321. #include <stdio.h> #include <conio.h> int main() { int c, n, nr; printf("Valoarea lui n este : "); scanf("%d",&n); nr=0; while (n>0) { c = n%10; // restul impartirii n = n/10; // catul impartirii printf("Adaug %d la numarul %d. A mai ramas %d\n", c, nr, n); nr = nr * 10 + c; } printf("Rasturnatul este %d\n", nr); getch(); }

Pentru valoarea 12345 programul furnizează rezultatele: Valoarea lui n este : 12345 Adaug 5 la numarul 0. A mai ramas 1234 Adaug 4 la numarul 5. A mai ramas 123 Adaug 3 la numarul 54. A mai ramas 12 Adaug 2 la numarul 543. A mai ramas 1 Adaug 1 la numarul 5432. A mai ramas 0 Rasturnatul este 54321

29. Un număr natural p se numeşte palindrom dacă este egal cu răsturnatul său. Un număr natural p se numeşte superpalindrom dacă p şi pătratul său sunt palindroame. Programul următor determină şi afişează primele n superpalindroame. //Superpalindroame

Page 53: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

101

#include<conio.h> #include<stdio.h> int main() { int n,k,ipatrat,inou, ipatratnou; int isalv, ipsalv,ipal,ippal,i; printf(" n ="); scanf( "%d", &n); printf("Primele %d superpalindroame \n\n", n); k=0; i=0; while(k<n) { isalv=i; inou=0; while(i) { inou=inou+(i%10); inou=inou*10; i=i/10; } inou=inou/10; if(isalv==inou) ipal=1; else ipal=0; ipatrat=isalv*isalv; ipsalv=ipatrat; ipatratnou=0; while(ipatrat) { ipatratnou=ipatratnou+(ipatrat%10); ipatratnou=ipatratnou*10; ipatrat=ipatrat/10; } ipatratnou=ipatratnou/10; if(ipsalv==ipatratnou) ippal=1; else ippal=0; if(ipal==1 && ippal==1) {

Instrucţiuni

102

printf(" %d %d\n", isalv, ipsalv); k++; i=isalv+1; } else i=isalv+1; } getch(); }

Un rezultat al rulării programului este: n =11 Primele 11 superpalindroame 0 0 1 1 2 4 3 9 11 121 22 484 101 10201 111 12321 121 14641 202 40804 212 44944

30. Programul următor citeşte un număr întreg n şi folosind instrucţiunea if, dacă 1 7n≤ ≤ , atunci afişează numele zilei corespunzătoare din săptămână, altfel afişează "Eroare". #include <stdio.h> #include <conio.h> int main() { int n; printf("Introduceti n:"); scanf("%i", &n); if(n == 1) printf("Luni"); else if(n == 2) printf("Marti"); else if(n == 3) printf("Miercuri"); else if(n == 4) printf("Joi"); else if(n == 5) printf("Vineri");

Page 54: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

103

else if(n == 6) printf("Sambata"); else if(n == 7) printf("Duminica"); else printf("Eroare"); getch(); }

31. Reluăm exemplul anterior, folosind instrucţiunea switch. #include <stdio.h> #include <conio.h> int main() { int n; printf("Introduceti n:"); scanf("%i", &n); switch(n) { case 1: printf("Luni"); break; case 2: printf("Marti"); break; case 3: printf("Miercuri"); break; case 4: printf("Joi"); break; case 5: printf("Vineri"); break; case 6: printf("Sambata"); break; case 7: printf("Duminica"); break; default: printf("Eroare"); } getch(); }

În ambele cazuri, programul furnizează rezultatul: Introduceti n:6 Sambata

i O variantă mai simplă (şi mai elegantă) este prezentată în capitolul 6. Aceasta foloseşte un vector de şiruri de caractere pentru a memora numele zilelor.

32. Programul următor calculează cel mai mare divizor comun a două numere folosind metoda cu scăderi a lui Euclid.

Instrucţiuni

104

#include <stdio.h> #include <conio.h> int main() { int m, n; printf("Introduceti m si n:"); scanf("%i%i", &m, &n); while(m != n) if(m > n) m -= n; else n-= m; printf("cmmdc = %i", m); getch(); }

Programul furnizează următoarele rezultate: Introduceti m si n:153 9 cmmdc = 9

4.6. Exerciţii 36. Scrieţi un program C care calculează produsul cifrelor unui număr natural. 37. Scrieţi un program C care determină dacă un număr natural n este prim sau compus. 38. Scrieţi un program C care determină divizorii unui un număr natural n. 39. Scrieţi un program C care citeşte un număr natural n şi dacă 1 12n≤ ≤ afişează numele lunii a n-a, altfel afişează "Eroare".

40. Scrieţi un program C care calculează !( )!

kn

nAn k

=−

pentru n şi k numere naturale.

41. Scrieţi un program C care calculează !!( )!

kn

nCk n k

=−

pentru n şi k numere naturale.

Page 55: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

105

42. Scrieţi un program C care calculează kn pentru n şi k numere naturale. 43. Scrieţi un program C care aproximează numărul e

folosind formula 0

1!

n

ni

ei=

= ∑ pentru un număr natural n.

Page 56: Bazele_programarii_calculatoarelor

107

555... FFFUUUNNNCCCŢŢŢIIIIII SSSTTTAAANNNDDDAAARRRDDD DDDEEE IIINNNTTTRRRAAARRREEE///IIIEEEŞŞŞIIIRRREEE

Deşi limbajul C nu conţine nici o instrucţiune pentru citirea datelor (intrare) de la utilizator sau afişarea rezultatelor (ieşire) pe ecran, există un număr mare de funcţii standard pentru operaţii de intrare/ieşire. Aceste funcţii se găsesc în biblioteca stdio.h (stdio este un acronim pentru standard input/output). Tot aici sunt definite constantele EOF (End Of File) şi NULL. Aceste valori sunt returnate de diverse funcţii când prelucrarea datelor s-a finalizat (datorită terminării datelor sau datorită unei erori).

5.1. Funcţii pentru caractere Funcţiile pentru caractere au o utilizare limitată,

fiind folosite pentru citirea şi afişarea informaţiei caracter cu caracter, fără nici o prelucrare în prealabil.

Tabel 5.1 Funcţii standard de intrare/ieşire la nivel de caracter

Prototip int putchar (int c)

Descriere Efect: afişează caracterul cu codul ASCII c pe ecran.

Funcţii standard de intrare/ieşire

108

Rezultat: valoarea c sau EOF în caz de eroare

Prototip int getchar() Descriere Efect: citeşte de la tastatură un singur

caracter Rezultat: codul ASCII al caracterului citit sau EOF dacă s-au terminat datele sau a apărut o eroare

i Funcţia getchar aşteaptă apăsarea tastei Enter înainte de a returna primul caracter introdus. La următoarele apeluri, funcţia returnează restul caracterelor tastate înainte de apăsarea tastei Enter.

De exemplu, secvenţa următoare afişează pe ecran primul caracter introdus de utilizator de la tastatură: int c; puts("Tastati un caracter si apasati Enter"); c = getchar(); putchar(c);

5.2. Funcţii pentru şiruri de caractere Funcţiile pentru şiruri de caractere permit citirea

şi afişarea informaţiei sub formă de şir de caracter, fără alte prelucrări.

Tabel 5.2 Funcţii standard de intrare/ieşire pentru şiruri de caractere

Prototip int puts (char *s)

Descriere Efect: afişează pe ecran şirul din variabila s şi trece pe linia următoare

Page 57: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

109

Rezultat: un număr nenegativ în caz de succes sau EOF în caz de eroare

Prototip char* gets (char *s)

Descriere Efect: citeşte un şir de caractere de la tastatura până la apăsarea tastei Enter şi îl pune în memorie la adresa s Rezultat: adresa s a şirului citit sau NULL daca s-au terminat datele sau a apărut o eroare

De exemplu, secvenţa următoare afişează pe ecran un mesaj introdus de utilizator de la tastatură: char sir[80]; puts("Tastati un text si apasati Enter"); gets(sir); puts("Ati tastat:"); puts(sir);

5.3. Funcţii cu formatare Funcţiile cu formatare permit citirea şi afişarea

informaţiei după o prelucrare în prealabil a acesteia. Prelucrarea se realizează conform unor coduri de format scrişi de programator într-un şir de caractere.

Tabel 5.3 Funcţii standard de intrare/ieşire cu formatare

Prototip int printf (char *fmt, ...);

Descriere Efect: scrie pe ecran şirul fmt în care codurile de format sunt înlocuite cu valorile expresiilor marcate prin trei puncte; Rezultat: numărul de caractere scrise sau EOF în caz de eroare

Funcţii standard de intrare/ieşire

110

Prototip int scanf (char *fmt, ...); Descriere Efect: citeşte de la tastatură date conform

şirului fmt şi le pune în memorie la adresele marcate prin trei puncte; Rezultat: numărul de coduri de format prelucrate corect sau EOF în caz de eroare

De exemplu, secvenţa următoare preia de la utilizator valoarea variabilei n şi afişează un mesaj ce conţine această valoare. int n; printf("Introduceti n:"); scanf("%i", &n); // &n este adresa variabilei n printf("Valoarea lui n este %i\n", n);

5.3.1. Codurile de format ale funcţiei printf Primul parametru al funcţiei printf este un şir de

caractere alcătuit din caractere normale, secvenţe escape şi coduri de format. Caracterele normale (exceptând %) şi secvenţele escape vor fi afişate pe ecran exact aşa cum apar în şir. În schimb, fiecare cod de format va fi înlocuit cu valoarea parametrului corespunzător lui: primul cod de format foloseşte valoarea celui de-al doilea parametru al funcţiei, al doilea cod de format foloseşte valoarea celui de-al treilea argument al funcţiei, ş.a.m.d. Un cod de format este alcătuit (în ordine de la stânga la dreapta) din:

• caracterul %, care marchează începutul codului de format (dacă vrem să afişăm un %, atunci trebuie scris %%);

• un caracter de umplere (opţional) conform cu Tabel 5.4;

Page 58: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

111

• un caracter modificator (opţional) pentru valori numerice conform Tabel 5.5;

• dimensiunea minimă a câmpului (opţional); • un punct urmat de precizia de afişare (opţional); • un caracter pentru subtipul informaţiei (opţional)

conform cu Tabel 5.6; • un caracter pentru tipul informaţiei conform cu

Tabel 5.7. Dimensiunea minimă a câmpului determină

numărul minim de caractere care vor fi afişate (daca informaţia ocupă mai puţine caractere, atunci ea va fi completată conform caracterului de umplere).

Precizia determină numărul de zecimale afişate în cazul valorilor numerice reale, respectiv numărul maxim de caractere afişate în cazul şirurilor de caractere.

Dimensiunea şi precizia pot fi numere zecimale pozitive sau caracterul asterisc, caz în care acestea sunt furnizate în lista parametrilor sub forma unui parametru întreg.

Tabel 5.4 Caractere de umplere

Caracter Descriere spaţiu informaţia este completată în partea stângă cu

spaţii (opţiunea implicită) 0 (zero) informaţia este completată în partea stângă cu

zerouri - (minus) informaţia este completată în partea dreaptă

cu spaţii

Funcţii standard de intrare/ieşire

112

Tabel 5.5 Caractere modificatori pentru valori numerice

Caracter Descriere + pentru formatele i, d, u, e, E, f, g şi G specifică

afişarea semnului valorilor numerice pozitive (implicit, semnul este afişat doar pentru valorile negative)

# pentru formatul o specifică afişarea prefixului 0

# pentru formatele x şi X specifică afişarea prefixului 0x, respectiv 0X

# pentru formatele e, E şi f specifică afişarea punctului zecimal chiar şi pentru numere fără zecimale

# pentru formatele g şi G specifică afişarea zerourilor nesemnificative de la sfârşitul numărului

Tabel 5.6 Caractere pentru subtip folosite în formatul funcţiei printf

Caracter Descriere h pentru formatele d, i, o, x şi X specifică

subtipul short int h pentru formatul u specifică subtipul short

unsigned int l pentru formatele d, i, o, x şi X specifică

subtipul long int l pentru formatul u specifică subtipul long

unsigned int

Page 59: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

113

Tabel 5.7 Caractere pentru tip folosite în formatul funcţiei printf

Caracter Tip parametru

Format ieşire

d int număr întreg scris în baza 10 i int număr întreg scris în baza 10 u unsigned

int

număr întreg scris în baza 10

o unsigned

int

număr întreg scris în baza 8

x unsigned

int

număr întreg scris în baza 16, cu literele "abcdef"

X unsigned

int

număr întreg scris în baza 16, cu literele "ABCDEF"

f double număr real scris în baza 10 e double număr real scris în baza 10 cu

exponent "e" E double număr real scris în baza 10 cu

exponent "E" g double format e sau f (care este mai

scurt) G double format E sau f (care este mai

scurt) c int un caracter s char * un şir de caractere p void * un pointer (i.e. o adresă de

memorie) De exemplu, secvenţa următoare:

char ch = 'h', *str = "test"; int in = 12;

Funcţii standard de intrare/ieşire

114

double db = 2.15; printf("|%d|%+d|%4d|\n",in, in, in); printf("|%04d|%0*d|\n", in, 9, in); printf("|%X|%x|%#x|\n", in, in, in); printf("|%o|%#o|\n", in, in); printf("|%c|\n", ch); printf("|%s|%6s|%-6s|%.2s|\n", str, str, str, str); printf("|%f|%e|%g|\n", db, db, db); printf("Adresa variabilei i este %p", &in);

produce următorul rezultat: |12|+12| 12| |0012|000000012| |C|c|0xc| |14|014| |h| |test| test|test |te| |2.150000|2.150000e+000|2.15| Adresa variabilei i este 0022FF6C

5.3.2. Codurile de format ale funcţiei scanf Formatul funcţiei scanf este alcătuit din

caractere de spaţiere (vezi secţiunea 2.1), caractere normale şi coduri de format. Caracterele de spaţiere sunt citite, dar nu sunt memorate, ele fiind interpretate ca separatori între datele de intrare. Caracterele normale (exceptând %) vor fi citite, dar nu vor fi memorate. Dacă se citeşte un caracter diferit de cel aşteptat atunci funcţia se opreşte din cititul datelor. În locul fiecărui cod, funcţia citeşte o valoare de intrare şi o memorează la o adresă trimisă funcţiei ca parametru: primul cod de format foloseşte adresa din al doilea argument al funcţiei, al doilea cod de format foloseşte adresa din al treilea

Page 60: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

115

argument al funcţiei, ş.a.m.d. Un cod de format este alcătuit (în ordine de la stânga la dreapta) din:

• caracterul %, care marchează începutul codului de format (dacă vrem să citim un %, atunci trebuie scris %%);

• un caracter asterisc (opţional) care specifică că informaţia va fi citită, dar nu va fi memorată;

• dimensiunea maximă a câmpului (opţional); • un caracter pentru subtipul informaţiei (opţional)

conform cu Tabel 5.8; • un caracter pentru tipul informaţiei conform cu

Tabel 5.9. Dimensiunea maximă a câmpului defineşte

numărul maxim de caractere care vor fi citite (daca se întâlneşte un caracter de spaţiere sau care intră în conflict cu formatul curent, atunci citirea opreşte).

În locul caracterului s (folosit pentru citirea unui şir de caractere) se poate specifica un şir format numai din anumite caractere scriind mulţimea caracterelor permise între paranteze pătrate. Dacă primul caracter din mulţime este săgeata în sus (^), atunci va fi acceptat orice caracter care nu aparţine mulţimii.

Tabel 5.8 Caractere pentru subtip folosite în formatul funcţiei scanf

Caracter Descriere h pentru formatele d, i, n, o şi x specifică

subtipul short int h pentru formatul u specifică subtipul short

unsigned int l pentru formatele d, i, n, o şi x specifică

Funcţii standard de intrare/ieşire

116

subtipul long int l pentru formatul u specifică subtipul long

unsigned int l pentru formatul f specifică tipul double (în loc

de float) Tabel 5.9 Caractere pentru tip folosite în

formatul funcţiei scanf

Caracter Tip parametru

Format intrare

d int* număr întreg scris în baza 10 D long int* număr întreg scris în baza 10 i int* număr întreg scris în baza 8,

10 sau 16 I long int* număr întreg scris în baza 8,

10 sau 16 u unsigned

int*

număr întreg fără semn scris în baza 10

U unsigned

long*

număr întreg fără semn scris în baza 10

o unsigned

int*

număr întreg scris în baza 8

O unsigned

long*

număr întreg scris în baza 8

x unsigned

int*

număr întreg scris în baza 16

X unsigned

long*

număr întreg scris în baza 16

f, e, E, g, G

float* număr real scris în baza 10 cu sau fără exponent

Page 61: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

117

c char* un caracter (inclusiv caractere de spaţiere)

s char* un şir de caractere n int* nimic (la adresa din

parametru va fi memorat numărul de caractere citite până în prezent)

p void** un pointer (rar folosit) De exemplu, secvenţa următoare:

char ch, str[30]; int in; float fl; double db; printf("Introduceti date: "); scanf("%i%f%lf%s%c", &in, &fl, &db, str, &ch); printf("Valori: %i %f %f %s %c", in,fl,db,str,ch);

produce rezultatul de mai jos. De remarcat că în cazul variabilei str nu s-a folosit operatorul & (deoarece str este un vector de caractere), iar codul %s a oprit citirea datelor la primul spaţiu, următorul caracter fiind preluat de codul %c. Introduceti date: 5 4.23 3.2 test program 5 4.230000 3.200000 test p

5.4. Funcţii cu formatare pentru şiruri de caractere Funcţia sprintf funcţionează similar cu funcţia

printf, cu excepţia că scrierea datelor se va face într-un şir de caractere şi nu pe ecran. În mod similar, funcţia

Funcţii standard de intrare/ieşire

118

sscanf funcţionează ca funcţia scanf, cu excepţia că citirea datelor se va face dintr-un şir de caractere şi nu de la tastatură.

Tabel 5.10 Funcţii standard cu formatare pentru şiruri de caractere

Prototip int sprintf (char* dest, char *fmt,

...);

Descriere Efect: scrie în şirul dest şirul fmt în care codurile de format sunt înlocuite cu valorile expresiilor marcate prin trei puncte; Rezultat: numărul de caractere scrise sau EOF în caz de eroare

Prototip int sscanf (char* srs, char *fmt,

...);

Descriere Efect: citeşte din şirul srs date conform şirului fmt şi le pune în memorie la adresele marcate prin trei puncte; Rezultat: numărul de coduri de format prelucrate corect sau EOF în caz de eroare

5.5. Exemple 33. Programul următor citeşte de la tastatură caracter cu caracter şi afişează pe ecran doar literele. Programul se opreşte la citirea primului caracter punct. #include <stdio.h> #include <conio.h> int main() { int car; puts("Introduceti textul:"); do

Page 62: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

119

{ car = getchar(); if(car >= 'A' && car <= 'Z' || car >= 'a' && car <= 'z') putchar(car); } while(car != EOF && car != '.'); getch(); }

Introduceti textul: Limbajul C are 32 de instructiuni. Asta e bine. LimbajulCaredeinstructiuni

34. Programul următor citeşte de la tastatură linie cu linie şi afişează pe ecran liniile după ce majusculele au fost transformate în minuscule (folosind funcţia strlwr, descrisă în capitolul 9). Programul se opreşte la citirea primei linii care începe cu un punct. #include <stdio.h> #include <conio.h> #include <string.h> int main() { char mesaj[150]; puts("Introduceti textul:"); while(gets(mesaj) && mesaj[0] != '.') { strlwr(mesaj); puts(mesaj); } getch(); }

Introduceti textul: Limbajul C are doar 32 de instructiuni. limbajul c are doar 32 de instructiuni. Asta e bine! asta e bine! .gata

35. Programul următor afişează tabla înmulţirii.

Funcţii standard de intrare/ieşire

120

#include <stdio.h> #include <conio.h> int main() { int l,c; for(l = 1; l<= 10; ++l) { for(c = 1; c <= 10; ++ c) printf("%5d", l*c); printf("\n"); } getch(); }

Rezultatul execuţiei este următorul: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100

36. Programul următor afişează tabela codurilor ASCII. Programul listează pe fiecare linie, în ordine crescătoare coduri ASCII în baza 10, 8 şi 16, şi caracterul asociat acestora. După 22 de linii, execuţia se suspendă până la apăsarea unei tastei. Rezultatul execuţiei acestui program este similar cu tabelul din Anexa 3 – Setul de caractere ASCII. #include <stdio.h> #include <conio.h> int main() { int i; puts("Cod 10\tCod 8\tCod 16\tCaracter"); for(i=0;i<=255;i++) {

Page 63: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

121

printf("%3i\t%#4o\t%#4X\t%c\n", i, i, i, i); if(i % 22 == 21) { getch(); puts("Cod 10\tCod 8\tCod 16\t Caracter"); } } }

37. Programul următor afişează o tabelă de corespondenţă pentru temperaturi în grade Celsius, Farenheit şi Kelvin. #include <stdio.h> #include <conio.h> int main() { float c, k, f; puts("Celsius\tKelvin\tFarenheit"); for(c=-30;c<=30;c+=10) { f = 32 + 9*c/5; k = c - 273.15; printf("%g\t%g\t%g\n", c, k, f); } getch(); }

Rezultatul programului este următorul: Celsius Kelvin Farenheit -30 -303.15 -22 -20 -293.15 -4 -10 -283.15 14 0 -273.15 32 10 -263.15 50 20 -253.15 68 30 -243.15 86

Funcţii standard de intrare/ieşire

122

5.6. Exerciţii 44. Scrieţi un program C care afişează valorile polinomului 2( ) 2,1 3,5 8,2p x x x= + + pentru toate valorile [ , , 2 ,..., ]n n p n p m+ + , unde n, p şi m sunt numere reale citite de la tastatură. 45. Scrieţi un program C care citeşte un întreg zecimal de cel mult 5 cifre de la tastatura şi îl afişează încadrat de 2 caractere % astfel: în octal, pe 10 poziţii, aliniat la dreapta, şi în hexazecimal, pe 15 poziţii şi aliniat la stânga. 46. Scrieţi un program C care citeşte două numere hexazecimale separate prin virgulă şi afişează suma lor în baza 8 şi 10, separate prin două puncte. 47. Scrieţi un program care citeşte de la tastatură un număr natural n şi un şir de caractere s şi afişează doar primele n caractere din s. 48. Scrieţi un program C care citeşte un caracter de la tastatură folosind funcţia scanf şi afişează codul său ASCII în baza 8 şi 10. 49. Scrieţi un program C care afişează reprezentarea în baza 2 a unui număr între n, citit de la tastatură. 50. Scrieţi un program C care citeşte de la tastatură un număr întreg n între 2 şi 8 şi afişează pe ecran un desen cu o linie de lungime n, astfel: |T'T'T'T'T'T'T'T'T'T'T'T'T'T'T'T'T'T'T'T'| || ' | ' | ' | ' | ' | |0 1 2 3 4 | !________________________________________!

Page 64: Bazele_programarii_calculatoarelor

123

666... TTTIIIPPPUUURRRIII DDDEEE DDDAAATTTEEE CCCOOOMMMPPPUUUSSSEEE

6.1. Masive Masivele de date sunt tablouri de date de acelaşi

tip, standard sau definit de utilizator, dispuse contiguu în memorie. Un astfel de tablou poate avea mai multe dimensiuni. Numărul dimensiunilor tabloului este limitat doar de memoria calculatorului pe care rulează programul care foloseşte masive de date. Masivul care are o singură dimensiune se numeşte vector sau şir. Masivul care are două dimensiuni se numeşte matrice. Un masiv cu k dimensiuni se declară astfel: tip nume[dim1][dim2]…[dimk]

unde: tip este tipul datelor din masiv, nume este numele masivului, iar dim1, dim2, ..., dimk sunt expresii constante a căror valori reprezintă numărul de componente pentru fiecare dimensiune.

De exemplu, următoarea secvenţă de program declară vectorul v cu 20 de elemente de tip întreg, matricea mat cu 5x5 elemente de tip caracter şi masivul tridimensional masiv cu 3x4x3 elemente de tip real. int v[20];

Tipuri de date compuse

124

char mat[5][5]; float masiv[3][4][2];

Numărul total de elemente ale unui masiv cu k dimensiuni este egal cu dim1*dim2*...*dimk, iar pentru memorarea unui masiv cu tipul tip şi k dimensiuni sunt necesari sizeof(tip)*dim1*dim2*...*dimk octeţi. Accesarea unui element al masivului se face cu construcţia nume[i1][i2]…[in], unde i1, i2, …, in sunt coordonatele elementului pe fiecare dintre dimensiuni (0 ≤ ij < dimj, 1≤j≤k).

Masivele pot fi iniţializate la declarare astfel: tip nume[dim1][dim2]...[dimk]={v1, v2, ..., vn};

valorile v1, v2, ..., vn, fiind stocate în masiv în ordine de la coordonatele mici (0,0,..., 0) la coordonatele mari (dim1-1, dim2-1,..., dimk-1).

i Pentru a forţa completarea valorilor în anumite locaţii ale masivului, acestea trebuie grupate între acolade astfel:

tip nume[dim1][dim2]...[dimk]={{v1,...,vp}, {vp+1,...}, {...,vn}};

i Dimensiunea cea mai importantă (dim1) poate să lipsească, ea fiind determinată automat din numărul valorilor de iniţializare, astfel:

tip nume[][dim2]...[dimk]= {v1, v2, ..., vn};

Dacă numărul de valori folosite la iniţializare depăşeşte numărul de elemente din masiv atunci compilatorul generează un mesaj de eroare. Dacă numărul de valori folosite la iniţializare este mai mic decât

Page 65: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

125

numărul de elemente din masiv, atunci restul valorilor sunt iniţializate cu 0.

De exemplu, prin declaraţia int v[4]={-1,2,4,0};

sunt iniţializate componentele vectorului cu identificatorul vector cu valori întregi astfel: v[0]=-1, v[1]=2, v[2]=4. Echivalent, se poate folosi declaraţia: int v[]={-1,2,4,0};

sau int v[4]={-1,2,4};

6.2. Structuri Spre deosebire de masive, care grupează sub un

singur nume, date de acelaşi tip, structura este un tip nou de dată care grupează date de tipuri diferite. Un element al unei structuri se numeşte membru sau câmp al structurii. Un membru al unei structuri poate fi folosit fie ca un singur tip de dată, fie ca o dată individuală. Fiecare membru al unei structuri are un tip predefinit sau definit de utilizator, în particular putând fi tot o structură. O structură se declară cu ajutorul cuvântului cheie struct, astfel: struct tipstructura { tip1 memebru1; tip2 memebru2; ... tipn memebrun; };

Tipuri de date compuse

126

Prin această declaraţie este definit un şablon, un tip nou de dată cu numele tipstructura care are n membri, fiecare membrui având tipul tipi. În particular, tipi poate fi de o structură, caz în care se spune că structurile cu tipurile tipstructura şi tipi sunt imbricate.

La compilare după o astfel de declaraţie nu se rezervă memorie deoarece este declarat un tip de dată. Declararea unor variabile de tipul tipstructura se poate face ca pentru orice tip predefinit, astfel: tipstructura variabila1, variabila2,…, variabilak;

Efectul obţinut prin declaraţiile de mai sus se poate obţine cu declaraţia: struct tipstructura { tip1 memebru1; tip2 memebru2; ... tipn memebrun; } variabila1, variabila2,…, variabilak;

Dacă tipstructura este absent, se spune că a fost definită o structură anonimă. În acest caz, cel puţin o variabilă trebuie să fie prezentă.

Accesarea membrului membrui al variabilei variabilap se face cu ajutorul operatorului de selecţie directă ‘.’ astfel: variabilap.memebrui = valoare; variabila = variabilap.memebrui;

De exemplu, declaraţiile următoare: struct student

Page 66: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

127

{ char nume[20]; char prenume[20]; int nota; };

declară tipul de dată student care are următorii membri: vectorii de caractere nume şi prenume, şi variabila de tip întreg nota. Declaraţia: student studenti[25];

declară vectorul studenti cu 25 de elemente de tip student. Expresia studenti[i].nota face referire la membrul nota al componentei de rang i a vectorului studenti. Declaraţiile următoare: struct grupastudiu { char disciplina[50]; student studenti[25]; }; grupastudiu anstudiu[5];

definesc tipul structură grupastudiu şi vectorul anstudiu cu 5 de componente de tipul grupastudiu. Expresia: anstudiu[2].studenti[3].nota = 10;

atribuie valoarea 10 notei celui de-al patrulea student din a treia grupa din anul de studiu.

Structurile se pot iniţializa la declarare asemănător cu iniţializarea masivelor. De exemplu, instrucţiunea următoare: student std={“Ionescu”,”Dan”,9};

Tipuri de date compuse

128

declară şi iniţializează variabila std corespunzătoare studentului Ionescu Dan, care are nota 9.

La asignarea unei variabile de tip structură, se realizează o copie a structurii sursă în variabila destinaţie. De asemenea, structurile pot fi folosite ca parametri pentru funcţii. În cazul în care tipul unui parametru este o structură, la apelul funcţiei se creează o copie a structurii care va fi folosită de funcţie. Dacă parametrul este un pointer la o structură, funcţia are acces direct la zona de memorie a structurii folosind operatorul ->.

De exemplu, funcţia afiseaza primeşte ca parametru un pointer la o structură student şi afişează informaţii despre studentul respectiv: void afiseaza(struct student *p) { printf("%s %s %d\n", p->nume, p->prenume, p->nota); }

În acest caz, referirea la câmpurile structurii se realizează cu ajutorul operatorului de selecţie indirectă ‘->’ prin construcţia p->nota, sau cu construcţia (*p).nota. Apelul funcţiei se realizează astfel: f(&std); f(&anstudiu[2].studenti[3]);

6.3. Uniuni Uniunile oferă utilizatorului posibilitatea refolosirii

aceleiaşi zone de memorie pentru a memora date de tipuri diferite la momente diferite de timp. Astfel, o zonă de memorie poate memora date de tip int, pentru ca

Page 67: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

129

ulterior aceeaşi zonă să memoreze date de tip float, ceea ce nu are loc în cazul structurilor. Scopul acestor reutilizări ale memoriei este economisirea acesteia.

Uniunile se declară şi se folosesc la fel ca structurile, deosebirea constând în înlocuirea cuvântului cheie struct cu cuvântul cheie union. În cazul structurilor se rezervă pentru fiecare membru câte o zonă de memorie de dimensiune corespunzătoare. În cazul uniunilor se rezervă o singură zonă de memorie, de dimensiune egală cu valoarea maximă a dimensiunilor necesare membrilor săi. În cazul uniunilor utilizatorul trebuie să monitorizeze tipurile de date aflate în memorie, în fiecare moment al execuţiei programului.

În declaraţia: union anstudiu { char cod; int numar; } an;

este declarat tipul de dată anstudiu de tip uniune şi variabila an de acest tip.

6.4. Câmpuri Un câmp este un şir de biţi definit ca membru al

unei structuri. Un câmp se declară ca un membru de tip unsigned, numele fiind urmat de un întreg care precizează dimensiunea în biţi a câmpului respectiv. În memorie câmpurile sunt grupate, în ordinea declarării lor, de la dreapta spre stânga. Forma generală de declarare a unor câmpuri este:

Tipuri de date compuse

130

struct tipstructura { ... unsigned camp1:dim1; unsigned camp2:dim2; ... } identificator;

în care campi este numele câmpului i care ocupă dimi biţi. Nici un câmp nu poate avea o dimensiune mai mare decât lungimea unui cuvânt calculator (32 biţi). Nu se pot defini masive de câmpuri. Operatorul unar adresă ‘&’ nu poate avea ca operand un câmp.

Prin declaraţia: struct { unsigned bitii01:2; unsigned bitii25:4; unsigned bitii68:3; } campuri;

este definită structura câmpuri cu câmpurile de biţi bitii01 (2 biţi), bitii25 (4 biţi) şi bitii68 (3 biţi).

6.5. Exemple 38. Programul următor citeşte un număr întreg n şi dacă 1 7n≤ ≤ , atunci afişează numele zilei corespunzătoare din săptămână, altfel afişează "Eroare". #include <stdio.h> #include <conio.h> int main() { int n; char *zile[] = {"Eroare", "Luni", "Marti", "Miercuri", "Joi", "Vineri", "Sambata", "Duminica"};

Page 68: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

131

printf("Introduceti n:"); scanf("%i", &n); if(n>=1 && n<=7) printf(zile[n]); else printf(zile[0]); getch(); }

39. Următorul program determină valoarea minimă şi poziţia sa într-un vector de numere reale: #include <stdio.h> #include <conio.h> int main() { int i,n,pmin; float v[100]; printf("Introduceti numarul de termeni:"); scanf("%d",&n); printf("Introduceti componentele:\n"); for(i=0; i<n; i++) { printf("v[%d]=", i); scanf("%f",&v[i]); } for(pmin=0,i=1; i<n; i++) if(v[pmin]>v[i]) pmin = i; printf("Minimul este v[%d]=%f\n", pmin, v[pmin]); getch(); }

Rularea programului generează următoarele rezultate: Introduceti numarul de termeni: 5 Introduceti componentele: v[0]= 23.1 v[1]= -2.3 v[2]= 4.5 v[3]= -11.2 v[4]= 9.8

Tipuri de date compuse

132

Minimul este v[3] = -11.200000

40. Următorul program determină suma componentelor unui vector de numere întregi: #include <stdio.h> #include <conio.h> int main() { int i,n,suma; int v[100]; printf("Introduceti numarul de termeni:"); scanf("%d",&n); printf("Introduceti componentele:\n"); for(i=0; i<n; i++) { printf("v[%d]=", i); scanf("%d",&v[i]); } for(suma=0, i=0; i<n; i++) suma += v[i]; printf("Suma este %d\n", suma); getch(); }

Rularea programului generează următoarele rezultate: Introduceti numarul de termeni: 5 Introduceti componentele: v[0]= 23 v[1]= -20 v[2]= 4 v[3]= -11 v[4]= 9 Suma este 5

41. În programul următor se citeşte un număr natural *, 99n N n∈ ≤ şi vectorii ( )0 1 1, , , na a a a −= K ,

( )0 1 1, , , nb b b b −= K , fiecare cu n componente numere reale

şi se generează vectorul ( )1 2 0 0 1 1, , , , , , ,n n nc b b b a a a− − −= K K .

Page 69: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

133

// Generare vector #include "stdio.h" #include "conio.h" int main() { float a[100],b[100],c[100]; int i,j,n; printf(" Numar componente "); scanf("%d",&n); if(n>0 && n<=99) { printf(" Componentele vectorului a \n"); for(i=0;i<n;i++) { printf(" a[%d] = ",i); scanf("%f",&a[i]); } printf(" Componentele vectorului b \n"); for(i=0;i<n;i++) { printf(" b[%d] = ",i); scanf("%f",&b[i]); } for(i=n-1;i>=0;i--) c[n-i-1]=b[i]; for(i=0;i<n;i++) c[n+i]=a[i]; printf(" Vectorul c = ( "); for(i=0;i<2*n;i++) printf(" %f ",c[i]); printf(" )\n"); } else printf(" %d nu este in domeniu \n",n); getch(); }

Un exemplu de rulare este: Numar componente 2 Componentele vectorului a a[0] = 1 a[1] = 2 Componentele vectorului b

Tipuri de date compuse

134

b[0] = 3 b[1] = 4 Vectorul c = ( 4.000000 3.000000 1.000000 2.000000 )

42. Programul următor citeşte un caracter, un şir de caractere şi determină numărul de apariţii (dacă există) ale caracterului citit în şir. #include "stdio.h" #include "conio.h" int main() { int n=0,i=0; char s[50],c; puts(" Sirul "); gets(s); puts(" Caracterul "); scanf("%c",&c); while(s[i]!='\0') { if(s[i]==c) n++; i++; } if(n==0) printf("Sirul %s nu contine caracterul %c \n",s,c); else printf(" Numarul de caractere %c din sirul %s este egal cu %d \n",c,s,n); getch(); }

Un exemplu de rulare este: Sirul frecventa caracter in sir Caracterul # Sirul frecventa caracter in sir nu contine caracterul # Sirul

Page 70: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

135

frecventa caracter in sir Caracterul e Numarul de caractere e din sirul frecventa caracter in sir este egal cu 3

43. Următorul program determină numărul de cuvinte dintr-un şir de caractere. Un cuvânt este şirul de caractere cuprins între unul din următorii separatori: spaţiu, virgulă, punct virgulă. #include "stdio.h" #include "conio.h" int main() { char sir[100]; int nc,i; puts(" Sirul :"); gets(sir); i=0; if(sir[i]==' ' || sir[i]==',' || sir[i]==';') nc=-1; else nc=0; while(sir[i]!='\0') { if(sir[i]==' '||sir[i]==','||sir[i]==';') { nc++; i++; while(sir[i]==' ' || sir[i]==',' || sir[i]==';') i++; } i++; } nc++; printf(" Numar cuvinte %d \n",nc); getch(); }

Un exemplu de rulare este:

Tipuri de date compuse

136

Sirul : numar; de , cuvinte;;; din , sir Numar cuvinte 5

44. Următorul program determină şi sortează descrescător frecvenţele de apariţie ale tuturor caracterelor dintr-un şir de caractere. // Frecventele sortate descrescator ale caracterelor #include "stdio.h" #include "conio.h" int main() { char sir[100],c[100],smartor[100],auxc; int nap[100],i,j,k,ncardist,auxi; puts(" Sirul : "); gets(sir); for(i=0;sir[i]!='\0';i++) smartor[i]=sir[i]; i=0; while(sir[i]!='\0') { nap[i]=1; c[i]=sir[i]; j=i+1; while(sir[j]!='\0') { if(sir[i]==sir[j]) { for(k=j;sir[k]!='\0';k++) sir[k]=sir[k+1]; sir[k]='\0'; nap[i]++; } else j++; } i++; } ncardist=i; printf(" Frecventele nesortate ale caracterelor\n\n In sirul %s \n",smartor);

Page 71: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

137

for(k=0;k<ncardist;k++) printf(" Caracterul %c apare de %d ori\n", c[k],nap[k]); getch(); printf(" Frecventele sortate descrescator ale caracterelor\n\n In sirul %s \n",smartor); for(i=0;i<ncardist;i++) for(j=i+1;j<ncardist;j++) if(nap[i]<=nap[j]) { auxc=c[i]; c[i]=c[j]; c[j]=auxc; auxi=nap[i]; nap[i]=nap[j]; nap[j]=auxi; } for(k=0;k<ncardist;k++) printf(" Caracterul %c apare de %d ori\n", c[k],nap[k]); getch(); }

Un exemplu de rulare este: Sirul : determinarea frecventelor Frecventele nesortate ale caracterelor In sirul determinarea frecventelor Caracterul d apare de 1 ori Caracterul e apare de 6 ori Caracterul t apare de 2 ori Caracterul r apare de 4 ori Caracterul m apare de 1 ori Caracterul i apare de 1 ori Caracterul n apare de 2 ori Caracterul a apare de 2 ori Caracterul apare de 1 ori Caracterul f apare de 1 ori Caracterul c apare de 1 ori Caracterul v apare de 1 ori Caracterul l apare de 1 ori Caracterul o apare de 1 ori

Tipuri de date compuse

138

Frecventele sortate descrescator ale caracterelor In sirul determinarea frecventelor Caracterul e apare de 6 ori Caracterul r apare de 4 ori Caracterul a apare de 2 ori Caracterul n apare de 2 ori Caracterul t apare de 2 ori Caracterul o apare de 1 ori Caracterul l apare de 1 ori Caracterul v apare de 1 ori Caracterul c apare de 1 ori Caracterul f apare de 1 ori Caracterul apare de 1 ori Caracterul i apare de 1 ori Caracterul m apare de 1 ori Caracterul d apare de 1 ori

45. Următorul program determină rădăcinile întregi ale polinoamelor cu coeficienţi întregi. Fie [ ]P Z X∈ ,

*gradP n N= ∈ , ( ) 11 1 0

n nn nP X a X a X a X a−

−= + + + +L ,

ia Z∈ , ( ) 0,1,...,i n∀ = , 0na ≠ . Este cunoscut faptul că rădăcinile întregi ale polinomului, dacă există, se găsesc printre divizorii termenului liber 0a . Programul are ca date de intrare gradul n şi coeficienţii 0 1, ,..., na a a ai polinomului şi ca date de ieşire rădăcinile sale întregi. Se determină toţi divizorii termenului liber şi pentru fiecare din ei se calculează valoarea polinomului. Dacă această valoare este nulă, atunci divizorul respectiv este rădăcină întreagă a polinomului. #include <stdio.h> #include <conio.h> int main() { int coef[10], i, j, n, m; printf("Gradul polinomului:");

Page 72: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

139

scanf("%d", &n); for (i=0; i<=n; ++i) { printf("Coeficientul %d: ", i); scanf("%d", &coef[i]); } m = coef[0]<0 ? -coef[0] : coef[0]; for (i=-m; i<=m; ++i) if(i==0 || m%i == 0) { long valoare = 0, putere = 1; for(j = 0; j<=n; ++j) { valoare += coef[j] * putere; putere *= i; } if(valoare == 0) printf("%d este radacina\n", i); } getch(); }

Pentru ( ) 3 26 11 6P X X X X= − + − , care are rădăcinile întregi 1 1x = , 2 2x = şi 3 3x = , programul furnizează rezultatele: Gradul polinomului:3 Coeficientul 0: -6 Coeficientul 1: 11 Coeficientul 2: -6 Coeficientul 3: 1 1 este radacina 2 este radacina 3 este radacina

Pentru ( ) 3 22 2P X X X X= − − + , care are rădăcinile întregi 1 1x = , 2 1x = − şi 3 2x = , programul furnizează rezultatele: Gradul polinomului:3 Coeficientul 0: 2

Tipuri de date compuse

140

Coeficientul 1: -1 Coeficientul 2: -2 Coeficientul 3: 1 -1 este radacina 1 este radacina 2 este radacina

46. Următorul program sortează crescător componentele unui vector de numere reale prin metoda bulelor: #include <stdio.h> #include <conio.h> int main() { int i,j,n,pmin; float v[100], aux; printf("Introduceti numarul de termeni:"); scanf("%d",&n); printf("Introduceti componentele:\n"); for(i=0; i<n; i++) { printf("v[%d]=",i); scanf("%f",&v[i]); } for(i=0;i<n;i++) for(j=i+1;j<n;j++) if(v[i]>v[j]) { aux=v[i]; v[i]=v[j]; v[j]=aux; } printf("Vectorul sortat crescator este:\n"); for(i=0;i<n;i++) printf("a[%d] = %f\n",i,v[i]); getch(); }

Rularea programului generează următoarele rezultate: Introduceti numarul de termeni: 5

Page 73: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

141

Introduceti componentele: v[0]= 23.1 v[1]= -2.3 v[2]= 4.5 v[3]= -11.2 v[4]= 9.8 Sirul sortat crescator este : v[0] = -11.200000 v[1] = -2.300000 v[2] = 4.500000 v[3] = 9.800000 v[4] = 23.100000

47. Următorul program calculează produsul a două matrice de numere reale: #include <stdio.h> #include <conio.h> int main() { float a[20][20],b[20][20],c[20][20]; int m,n,p,q,i,j,k; printf("Numarul de linii al matricei A:"); scanf("%d",&m); printf("Numarul de coloane al matricei A:"); scanf("%d",&n); printf("Numarul de linii al matricei B:"); scanf("%d",&p); printf("Numarul de coloane al matricei B:"); scanf("%d",&q); if(n==p) { printf("Se calculeaza produsul A*B\n"); for(i=0;i<m;i++) for(j=0;j<n;j++) { printf("a[%d][%d]=",i,j); scanf("%f",&a[i][j]); } for(i=0;i<p;i++) for(j=0;j<q;j++) { printf("b[%d][%d]=",i,j);

Tipuri de date compuse

142

scanf("%f",&b[i][j]); } for(i=0;i<m;i++) for(j=0;j<q;j++) { c[i][j]=0; for(k=0;k<n;k++) c[i][j] += a[i][k]*b[k][j]; printf("c[%d][%d]=%f\n",i,j,c[i][j]); } } else { printf("Produsul A*B nu este definit\n"); } getch(); }

De exemplu, fie matricele 1 23 4

A

=

şi

1 1 22 0 1

B−

= − . Produsul este

1 23 4

A B

⋅ = ⋅

1 1 2 5 1 42 0 1 11 3 10

− − = − −

.

Numarul de linii a matricei A: 2 Numarul de coloane a matricei A: 2 Numarul de linii a matricei B: 2 Numarul de coloane a matricei B: 3 Se calculeaza produsul A*B a[0][0] = 1 a[0][1] = 2 a[1][0] = 3 a[1][1] = 4 b[0][0] = -1 b[0][1] = 1 b[0][2] = 2 b[1][0] = -2 b[1][1] = 0 b[1][2] = 1 c[0][0]= -5.000000

Page 74: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

143

c[0][1]= 1.000000 c[0][2]= 4.000000 c[1][0]= -11.000000 c[1][1]= 3.000000 c[1][2]= 10.000000

48. Următorul program reprezintă un dicţionar român-englez-francez "de jucărie". Programul foloseşte funcţii pentru şiruri de caractere descrise în capitolul 9. #include <stdio.h> #include <string.h> #include <conio.h> char* cuvinte[][3] = { "masa", "table", "table", "scaun", "chair", "chaise", "mancare", "food", "nourriture", "apa", "water", "eau", "om", "human", "humain", NULL, NULL, NULL }; int main(void) { char cuv_rom[80]; int i; printf("Introduceti cuvantul romana:"); gets(cuv_rom); /* cautam cuvantul in dictionar */ for(i = 0; cuvinte[i][0] != NULL; ++i) if(!strcmp(cuv_rom, cuvinte[i][0])) { printf("Engleza: %s\nFranceza: %s\n", cuvinte[i][1], cuvinte[i][2]); break; } if(cuvinte[i][0] == NULL) printf("Cuvant necunoscut.\n"); getch(); }

Tipuri de date compuse

144

49. Reluăm exemplul dicţionarului, dar de această dată cuvintele sunt memorate în variabile de tip structură. #include <stdio.h> #include <string.h> #include <conio.h> struct cuvant { char *romana, *engleza, *franceza; } cuvinte[] = { {"masa", "table", "table"}, {"scaun", "chair", "chaise"}, {"mancare", "food", "nourriture"}, {"apa", "water", "eau"}, {"om", "human", "humain"}, NULL, NULL, NULL }; int main(void) { char cuv_rom[80]; int i; printf("Introduceti cuvantul romana:"); gets(cuv_rom); /* cautam cuvantul in dictionar */ for(i = 0; cuvinte[i].romana != NULL; ++i) if(!strcmp(cuv_rom, cuvinte[i].romana)) { printf("Engleza: %s\nFranceza: %s\n", cuvinte[i].engleza, cuvinte[i].franceza); break; } if(cuvinte[i].romana == NULL) printf("Cuvant necunoscut.\n"); getch(); }

50. Exemplu următor citeşte informaţiile unei grupe de studenţi, le sortează descrescător după notă şi afişează rezultatele.

Page 75: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

145

#include <stdio.h> #include <conio.h> int main() { struct student { char nume[20]; char prenume[20]; int nota; } student[25], aux; int i,j,n; printf("Numar studenti:"); scanf("%d",&n); for(i=0;i<n;i++) { printf("Nume si prenume student %d:", i+1); scanf("%s%s",student[i].nume, student[i].prenume); printf("Nota studentului %s %s:", student[i].nume, student[i].prenume); scanf("%d",&student[i].nota); } for(i=0;i<n;i++) for(j=i+1;j<n;j++) if(student[i].nota < student[j].nota) { aux=student[i]; student[i]=student[j]; student[j]=aux; } printf("Rezultatele grupei:\n"); for(i=0;i<n;i++) printf("%s %s\t%d\n", student[i].nume, student[i].prenume, student[i].nota); getch(); }

Rularea programului furnizează următoarele rezultate:

Tipuri de date compuse

146

Numar studenti 5 Nume si prenume student 1 Ion Dan Nota studentului Ion Dan 8 Nume si prenume student 2 Ilie Victor Nota studentului Ilie Victor 7 Nume si prenume student 3 Ionescu Vlad Nota studentului Ionescu Vlad 6 Nume si prenume student 4 Popescu Ion Nota studentului Popescu Ion 9 Nume si prenume student 5 Dima Florin Nota studentului Dima Florin 10 Rezultatele grupei: Ionescu Vlad 6 Ilie Victor 7 Ion Dan 8 Popescu Ion 9 Dima Florin 10

51. Presupunem că dorim să prelucrăm o mulţime de puncte din plan. Despre fiecare punct ne interesează numele şi coordonatele acestuia. În final afişăm distanţele dintre oricare două puncte. Vom defini o structură de date care reprezintă informaţiile despre un punct din plan. #include <stdio.h> #include <math.h> #include <conio.h> struct Punct { float x; // coordonata x float y; // coordonata y char nume[20]; // numele }; int main() { struct Punct pt[10];// maxim 10 de puncte int i, j, n; // numarul real de puncte printf("Numarul de puncte: "); scanf("%d", &n); for(i = 0; i<n; ++i) { printf("Punctul %d.\n", i); printf("\tNume: ");

Page 76: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

147

scanf("%s", pt[i].nume); printf("\tCoordonata pe axa Ox: "); scanf("%d", &pt[i].x); printf("\tCoordonata pe axa Oy: "); scanf("%d", &pt[i].y); } for(i=0; i<n; ++i) for(j=0; j<n; ++j) printf("Distanta de la %s la %s este %f.\n", pt[i].nume, pt[j].nume, sqrt(pt[i].x*pt[j].x + pt[i].y*pt[j].y)); getch(); }

6.6. Exerciţii 51. Scrieţi un program C care afişează suma elementelor pare şi produsul elementelor impare dintr-un vector de numere reale. 52. Scrieţi un program C care afişează poziţia primei apariţii a unui număr m într-un vector de numere reale. 53. Scrieţi un program C care afişează produsul scalar a doi vectori de numere reale. 54. Scrieţi un program C care afişează suma a doi vectori de numere reale. 55. Scrieţi un program C care afişează suma a două matrice de numere reale. 56. Scrieţi un program C care citeşte un număr natural

*, 99n N n∈ ≤ şi vectorii ( )0 1 1, , , na a a a −= K ,

( )0 1 1, , , nb b b b −= K , fiecare cu n componente numere reale şi se generează vectorul ( )0 1 1 0 1 1, , , , , , ,n nc b b b a a a− −= K K . 57. Scrieţi un program C care citeşte un număr natural

*, 99n N n∈ ≤ şi vectorii ( )0 1 1, , , na a a a −= K ,

Tipuri de date compuse

148

( )0 1 1, , , nb b b b −= K , fiecare cu n componente numere reale şi se generează vectorul ( )0 0 1 1 1 1, , , , , ,n nc b a b a b a− −= K . 58. Scrieţi un program C care simulează evoluţia unei populaţii de viruşi. Aceşti se află în anumite noduri ale unei reţele cu dimensiune n x n. Un virus trăieşte dacă are 2 sau 3 vecini, altfel moare (sufocat sau de singurătate). Într-un nod liber cu 2 sau 3 vecini, se naşte un virus. 59. Scrieţi un program C care gestionează un vector cu informaţii despre un angajat. Informaţiile trebuie memorate într-o structură. Programul trebuie să permită adăugarea şi ştergerea de elemente din vector.

Page 77: Bazele_programarii_calculatoarelor

149

777... FFFUUUNNNCCCŢŢŢIIIIII

Pentru scrierea unor programe de complexitate cel puţin medie, există posibilitatea organizării unor date şi a unor acţiuni asupra lor, într-o entitate independentă, numită funcţie. Limbajul C pune la dispoziţia utilizatorului diverse funcţii predefinite, numite funcţii standard. Utilizatorul poate să definească şi să utilizeze propriile sale funcţii, numite funcţii utilizator.

Există o funcţie standard, principală, funcţia main(), apelată de sistemul de operare la începutul execuţiei oricărui program. Funcţiile apelate pot comunica cu funcţiile apelante prin intermediul parametrilor.

7.1. Declararea şi definirea funcţiilor Pentru a fi utilizată într-un program C, o funcţie

trebuie întâi declarată (şi ulterior definită). O funcţie se declară pentru ca tipul ei, precum şi tipul şi numărul argumentelor sale să fie cunoscute în vederea utilizării ei viitoare de către alte funcţii. O funcţie se defineşte specificând tipul şi numele ei, tipul şi numele

Funcţii

150

argumentelor sale, precum şi corpul funcţiei (compus din declaraţii şi instrucţiuni).

i Argumentele unei funcţii precizate la definirea ei se numesc parametri formali, în timp ce argumentele precizate la apelul ei se numesc parametri actuali.

Sintaxa declaraţiei unei funcţii este: tip identificator(lista parametri formali) { declaraţii şi instrucţiuni }

unde tip identificator(lista parametri formali)

se numeşte antet al funcţiei, iar restul reprezintă corpul funcţiei. Elementul tip reprezintă tipul valorii întoarse prin apelul funcţiei, iar identificator este numele funcţiei. Pentru funcţiile care nu returnează nici o valoare, tipul funcţiei este void. Pentru fiecare parametru din lista parametri formali trebuie specificat tipul şi numele. Lista parametrilor formali poate să fie şi vidă, caz în care poate fi înlocuită cu cuvântul void.

Corpul funcţiei se execută până la executarea ultimei instrucţiuni sau până la executarea instrucţiunii return. Dacă funcţia returnează o valoare atunci se foloseşte forma return expresie;

a instrucţiunii return. Prin executarea acestei instrucţiuni se evaluează expresia, se atribuie funcţiei valoarea expresiei şi se încheie execuţia funcţiei. Tipul expresiei din instrucţiunea return trebuie să fie compatibil cu tipul

Page 78: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

151

funcţiei. Dacă funcţia nu returnează valori, instrucţiunea return poate să lipsească sau se foloseşte forma return;

i În limbajul C nu este admisă definirea unei funcţii în cadrul altei funcţii şi nu sînt permise salturi cu instrucţiunea goto în afara funcţiei.

Dacă un parametru formal este un masiv, acesta poate fi declarat fără a indica valoarea maximă a primei dimensiuni. Valorile maxime ale celorlalte dimensiuni trebuie scrise. Următoarele prototipuri conţin parametrul formal vector care este un masiv unidimensional (vector): int f(int vector[]); int f(int vector[50]);

Prototipuri de mai jos conţin parametrul formal masiv care este un masiv tridimensional: int f(int masiv[][50][50]); int f(int masiv[50][50][50]);

7.1.1. Prototipul funcţiilor O funcţie poate fi folosită înainte de a fi definită

dacă ea a fost declarată. Declararea se realizează prin scrierea antetului funcţiei urmat de caracterul ';' (construcţie denumită prototip), astfel: tip identificator(lista parametri formali);

Prototipul unei funcţii informează compilatorul despre tipul valorii pe care o va returna funcţia şi despre tipurile tuturor parametrilor. Numele parametrilor pot lipsi, fiind suficientă specificarea tipurilor lor. Prototipul trebuie

Funcţii

152

inserat în program înaintea primului apel al funcţiei. De exemplu, următoarele prototipuri sunt identice şi corecte: int functie(int param1, float param2, char param3); int functie(int, float, char);

7.1.2. Transferul parametrilor Parametrii funcţiilor se pot transmite prin valoare,

prin referinţă şi prin intermediul variabilelor globale. La transmiterea parametrilor prin valoare, valorile din funcţia apelantă sunt copiate în parametrii actuali ai funcţiei, şi nu pot fi modificate de către funcţia apelată deoarece aceasta lucrează cu copii ale lor.

În cazul transmiterii parametrilor prin referinţă (sau prin pointeri) funcţia apelantă furnizează funcţiei apelate adresa zonei de memorie unde sunt păstrate datele. Funcţia apelată poate modifica aceste date accesând direct zona respectivă de memorie.

O altă metode de transfer al parametrilor este transmiterea acestora prin variabile globale. Această metodă se bazează pe faptul că variabilele globale sunt accesibile din orice funcţie.

7.2. Apelul funcţiilor Apelul unei funcţii se face prin scrierea numelui

acesteia urmat de parametrii actuali ai funcţiei scrişi între paranteze rotunde şi separaţi prin virgule. Parametrii actuali trebuie să corespundă cu cei formali prin ordine,

Page 79: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

153

tip şi număr. O funcţie se poate apela recursiv din altă funcţie sau direct, chiar din interiorul ei.

Dacă funcţia nu are parametri, între paranteze nu se scrie nimic. Valoarea întoarsă de funcţie participă la evaluarea valorii finale a expresiei în care apare.

Utilizarea unei funcţii din bibliotecă impune includerea fişierului asociat, cu directiva #include: #include “numebiblioteca.h”

sau #include <numebiblioteca.h>

De exemplu, secvenţa: int f(int x) { return x-1; }

declară o funcţie care are identificatorul f, un parametru formal de tip întreg x, şi care returnează o valoare de tip întreg. Instrucţiunea return x-1; întoarce ca valoare a funcţiei valoarea lui x minus 1, deci apelul f(5) va returna valoarea 4. Transmiterea parametrului este făcută prin valoare. De exemplu: #include <stdio.h> #include <conio.h> int f(int x) { return x-1; } int main() { int x; printf("Valoarea lui x = "); scanf("%d",&x);

Funcţii

154

printf("f (%d) = %d \n",x,f(x)); getch(); }

Rularea programului conduce la rezultatele: Valoarea lui x = 5 f (5) = 4

7.3. Transmiterea parametrilor În limbajul C există două posibilităţi de transfer a

datelor între funcţia apelantă şi cea apelată: prin parametri şi prin variabile globale. Prin utilizarea variabilelor globale nu se face un transfer propriu-zis, ci se folosesc în comun anumite zone de memorie partajată între toate funcţiile din program.

7.3.1. Transferul prin parametri În limbajul C transferul prin parametri se poate

realiza prin valoare şi prin referinţă. În acest transferului prin valoare, valoarea parametrului este copiată, iar funcţia apelată lucrează cu această copie. Deci operaţiile efectuate asupra unui parametru formal transmis prin valoare (adică orice parametru care are tip fundamental), nu modifică, la ieşirea din funcţie, parametrul actual corespunzător. În plus, transferul valorii este însoţit de eventuale conversii de tip, realizate pe baza informaţiilor despre funcţie de care dispune compilatorul. În concluzie, parametrii transmişi prin valoare se pot modifica în corpul funcţiei, dar după terminarea apelului funcţiei apelate în funcţia apelantă aceştia au aceleaşi valori pe care le-au

Page 80: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

155

avut înaintea apelului. Din acest motiv, transferul prin valoare este folosit pentru transmiterea datelor de intrare.

Exemplu următor prezintă o funcţie de calcul a sumei primelor n numere naturale. Parametrul n este transmis prin valoare. Funcţia suma are un singur parametru, k, de tip int, care este parametru de intrare. Funcţia returnează valoarea sumei prin numele său. #include <stdio.h> #include <conio.h> int suma(int k) // parametrul transmis prin valoare { int s = 0,i; for(i=1;i<=k;i++) s+=i; return s; } int main() { int n; printf(" Valoarea lui n = "); scanf("%d",&n); printf("Suma primelor %d numere naturale este %d\n", n, suma(n)); getch(); }

Pentru n=10 programul furnizează valoarea 55. Valoarea lui n = 10 Suma primelor 10 numere naturale este 55

Folosind transferul prin valoare se pot transmite funcţiei numai parametri de intrare. Pentru a transmite parametri de ieşire se foloseşte transferul prin referinţă (adică prin pointeri), adică se transmit funcţiei adrese de

Funcţii

156

variabile. Pointerii sunt subiectul capitolului 8, dar, pe scurt, antetul unei funcţii care foloseşte transferul de parametri de tip referinţă este (observaţi folosirea caracterului asterisc înainte de numele variabilei): tip identificator(…, tip *variabila,…)

Parametrii transmişi prin referinţă se pot modifica în corpul funcţiei, iar după terminarea apelului funcţiei aceştia au valorile primite în timpul execuţiei funcţiei apelate. În acest caz, funcţia primeşte ca parametru adresa zonei de memorie unde se găsesc datele şi poate să le actualizează pe acestea modificând conţinutul zonei de memorie.

Reluăm exemplul anterior. Acum, funcţia suma are un parametru formal de intrare k şi un parametru formal de ieşire s, care este un pointer la tipul int. #include <stdio.h> #include <conio.h> // Parametrul n se transmite prin valoare // Parametrul s se transmite prin adresa int suma(int k,int *s) { int sum = 0,i; for(i=1;i<=k;i++) sum+=i; *s=sum; } int main() { int n,sum; printf("Valoarea lui n = "); scanf("%d",&n); // Apelul functiei suma(n,&sum); // &sum inseamna adresa lui sum

Page 81: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

157

printf("Suma primelor %d numere naturale este %d\n", n, sum); getch(); }

Programul următor citeşte valorile unor variabile a şi b şi apelează funcţia tipar. Aceasta primeşte prin referinţă variabilele a şi b, modifică valorile de la adresele lor şi tipăreşte noile valori. După apelul funcţiei tipar, în funcţia main se tipăresc din nou valorile celor două variabile. #include <stdio.h> #include <conio.h> void tipar(int *a, int *b) { *a=*a+3; *b=*b+4; printf("In tipar:\n"); printf("a = %d \n b = %d \n",*a,*b); } int main() { int a,b; printf("Valoarea lui a : "); scanf("%d",&a); printf("Valoarea lui b : "); scanf("%d",&b); tipar(&a,&b); printf("Valorile dupa apelul functiei sunt:\n"); printf("a = %d \n b = %d \n",a,b); getch(); }

Ce anume se întâmplă la execuţia programului pentru a=5 şi b=7? Funcţia main apelează funcţia tipar având ca parametri actuali adresele variabilelor a şi b. La adresa variabilei a este memorată valoarea 5, iar la

Funcţii

158

adresa variabilei b este memorată valoarea 7. Funcţia tipar adună valoarea 3 la conţinutul de la adresa lui a şi valoarea 4 la conţinutul de la adresa lui b, astfel încât noile valori ale lui a şi b sunt respectiv 8 şi 11. Aceste valori se păstrează după apelul funcţiei tipar. Valoarea lui a : 5 Valoarea lui b : 7 In tipar: a = 8 b = 11 Valorile dupa apelul functiei sunt: a = 8 b = 11

Pentru parametrii de tip masiv de date, transferul prin referinţă se face în mod implicit, deoarece numele masivului este pointer către zona de memorie unde este stocat masivul. Astfel următoarele prototipuri sînt echivalente: tip identificator1(int masiv[]); tip identificator2(int *masiv);

Următorul exemplu prezintă o funcţie pentru calculul sumei componentelor unui vector. Funcţia suma are doi parametri de intrare. Citirea vectorului se realizează cu funcţia citvector. Parametrul vector este transferat prin referinţă deoarece este un masiv de întregi. #include <stdio.h> #include <conio.h> void citvector(int k,int vector[]) { int i; for(i=0;i<k;i++) {

Page 82: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

159

printf("v [ %d ] = ",i); scanf("%d",&vector[i]); } } int suma(int k,int vector[]) { int s = 0,i; for(i=0; i<k; i++) s+=vector[i]; return s; } int main() { int n,v[100],i; printf("Numar de componente n = "); scanf("%d",&n); citvector(n, v); printf("Suma componentelor este: %d\n", suma(n,v)); getch(); }

Rezultatul execuţiei programului este: Numar de componente n = 3 v [ 0 ] = 11 v [ 1 ] = 22 v [ 2 ] = 33 Suma componentelor este: 66

7.3.2. Transferul prin variabile globale Variabilele globale se declară în afara funcţiilor,

inclusiv în afara funcţiei main() şi pot fi referite din orice alte funcţii, inclusiv din funcţia main(). Astfel, transferul de parametri între funcţia apelantă şi cea apelată se poate face şi prin variabile globale.

Funcţii

160

Pentru exemplificare, reluăm unul dintre exemplele anterioare. Parametrii de intrare ai funcţiei suma sunt declaraţi ca variabile globale. Funcţia suma are lista parametrilor formali vidă. #include <stdio.h> #include <conio.h> int n, vector[100]; // variabile globale void citvector() { int i; for(i=0;i<n;i++) { printf(" v [ %d ] = ",i); scanf("%d", &vector[i]); } } int suma() { int s = 0,i; for(i=0;i<n;i++) s+=vector[i]; return s; } int main() { int i; printf(" Numar de componente n = "); scanf("%d",&n); citvector(); printf("Suma componentelor este: %d\n",suma()); getch(); }

Cele două metode de transmitere a parametrilor (prin variabile globale şi prin parametri) pot fi folosite împreună. De exemplu, declarăm numai vectorul vector

Page 83: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

161

variabilă globală. Funcţia suma are un singur parametru formal k. #include <stdio.h> #include <conio.h> // Declararea variabilei globale vector int vector[100]; void citvector(int k) { int i; for(i=0;i<k;i++) { printf(" v [ %d ] = ",i); scanf("%d", &vector[i]); } } int suma(int k) { int s = 0,i; for(i=0;i<k;i++) s+=vector[i]; return s; } int main() { int n; printf(" Numar de componente n = "); scanf("%d",&n); citvector(n); printf("Suma componentelor: %d\n", suma(n)); getch(); }

7.4. Recursivitate Recursivitatea este o tehnică de programare

frecvent utilizată în implementarea funcţiilor. Tehnica recursivităţii constă în autoapelul funcţiei (de către ea însăşi), în mod direct sau indirect. Tehnica poate fi

Funcţii

162

folosită în cazul problemelor cu natură recursivă şi simplifică scrierea programelor prin scrierea directă a formulelor recursive.

Definiţia unui proces recursiv trebuie să satisfacă condiţia de consistenţă, adică să se termine într-un număr finit de paşi.

Un exemplu de funcţie inconsistentă este următorul: fie funcţia :f N N N× → ,

( ) ( )1 0

1n

f nn f n altfel

== ⋅ +

, ( )n N∀ ∈ . De

exemplu, ( ) ( ) ( )2 2 3 2 3 4 ...f f f= ⋅ = ⋅ ⋅ = L, ceea ce arată că

acest proces recursiv nu este finit. Un exemplu de funcţie consistentă este

următorul: fie funcţia :f N N→ ,

( ) ( )1 0

1n

f nn f n altfel

== ⋅ −

, ( )n N∀ ∈ . Evident:

( ) ( ) ( )(3) 3 2 3 2 1 3 2 1 0 3 2 1 1 3! 6f f f f= ⋅ = ⋅ ⋅ = ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ = = ,

ceea ce arată că acest proces recursiv este finit. La baza recursivităţii stă o zonă de memorie

organizată ca o stivă. La fiecare apel al funcţiei se salvează automat în stivă valorile variabilelor din funcţie şi adresele de revenire (adresa următoarei instrucţiuni care urmează a fi executată după apelul). Acest lucru simplifică programarea, dar implică un consum suplimentar de timp şi memorie datorită operaţiilor cu stiva.

Recursivitatea este de două tipuri: directă şi indirectă. O funcţie recursivă se numeşte direct recursivă,

Page 84: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

163

dacă în definiţia ei există cel puţin un autoapel al ei. O funcţie recursivă se numeşte indirect recursivă dacă, se autoapelează prin intermediul unei alte funcţii, care şi ea se autoapelează prin intermediul primei funcţii.

În cazul recursivităţii indirecte, funcţiile care se autoapelează indirect trebuie făcute cunoscute compilatorului prin prototipurile lor, scrise înaintea definiţiilor lor. Următoarele programe ilustrează cele două tipuri de recursivitate.

De exemplu, funcţia factorial recursivă prezentată anterior, poate fi implementată în limbajul C astfel: long factorial (unsigned int n) { if(n==0) return 1; else return n*factorial(n-1); }

7.5. Exemple 52. Programul următor prezintă o funcţie pentru determinarea celui mai mare divizor comun a două numere naturale cu algoritmul lui Euclid şi un program care testează această funcţie. În program se defineşte funcţia euclid de tip întreg care are parametrii formali p şi q de tip întreg. Funcţia returnează valoarea cmmdc(p,q). Apelul funcţiei cu parametrii actuali m şi n, euclid(m,n) determină calculul cmmdc(m,n). #include <stdio.h> #include <conio.h> int euclid(int p, int q) { int r;

Funcţii

164

while(r=p%q) { p=q; q=r; } return q; } int main() { int m,n; printf("Valoarea lui m = "); scanf("%d",&m); printf("Valoarea lui n = "); scanf("%d",&n); printf("cmmdc(%d,%d) = %d \n", m, n, euclid(m,n)); getch(); }

Pentru m=48 şi n=18 programul afişează: Valoarea lui m = 48 Valoarea lui n = 18 cmmdc(48,18) = 6

53. Exemplul următor prezintă folosirea prototipului funcţiei euclid pentru declararea acesteia înainte de a fi definită. În program este scris mai întâi prototipul funcţiei euclid, iar funcţia este definită abia după definiţia funcţiei main(). #include <stdio.h> #include <conio.h> int euclid(int, int); // Prototipul functiei int main() { int m,n; printf("Valoarea lui m = "); scanf("%d",&m); printf("Valoarea lui n = "); scanf("%d",&n); printf("cmmdc(%d,%d) = %d \n", m, n, euclid(m,n));

Page 85: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

165

getch(); } int euclid(int p, int q) // Definitia functiei { int r; while(r=p%q) { p=q; q=r; } return q; }

54. Fie n un număr natural. Conform criteriului de divizibilitate cu 3, numărul n este divizibil cu 3 dacă suma cifrelor numărului n este un număr divizibil cu 3. În programul următor este definită o funcţie care implementează criteriul de divizibilitate cu 3. // Test divizibilitate cu 3 #include "stdio.h" #include "conio.h" int test3(int n) { int s,uc,nsalv; nsalv=n; s=0; while(n!=0) { uc=n%10; s+=uc; n/=10; } printf(" Suma cifrelor numarului %d este %d \n", nsalv,s); if(s%3) return 0; else return 1; } int main() {

Funcţii

166

int n; printf(" n = "); scanf("%d",&n); if(test3(n)) printf("Numarul %d este divizibil cu 3\n", n); else printf("Numarul %d nu este divizibil cu 3\n", n); getch(); }

Un exemplu de rulare este: n = 123456789 Suma cifrelor numarului 123456789 este 45 Numarul 123456789 este divizibil cu 3 n = 1234 Suma cifrelor numarului 1234 este 10 Numarul 1234 nu este divizibil cu 3

55. Fie n un număr natural. Conform criteriului de divizibilitate cu 9, numărul n este divizibil cu 9 dacă suma cifrelor numărului n este un număr divizibil cu 9. În programul următor este definită o funcţie care implementează criteriul de divizibilitate cu 9. // Test divizibilitate cu 9 #include "stdio.h" #include "conio.h" int test9(int n) { int s,uc,nsalv; nsalv=n; s=0; while(n!=0) { uc=n%10; s+=uc; n/=10; }

Page 86: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

167

printf(" Suma cifrelor numarului %d este %d\n", nsalv,s); if(s%9) return 0; else return 1; } int main() { int n; printf(" n = "); scanf("%d",&n); if(test9(n)) printf(" Numarul %d este divizibil cu 9 \n",n); else printf(" Numarul %d nu este divizibil cu 9 \n",n); getch(); }

Un exemplu de rulare este: n = 123456789 Suma cifrelor numarului 123456789 este 45 Numarul 123456789 este divizibil cu 9 n = 1234 Suma cifrelor numarului 1234 este 10 Numarul 1234 nu este divizibil cu 9

56. Fie n un număr natural. Conform criteriului de divizibilitate cu 4, numărul n este divizibil cu 4 dacă numărul format cu ultimele două cifre ale numărului n este un număr divizibil cu 4. În programul următor este definită o funcţie care implementează criteriul de divizibilitate cu 4. // Test divizibilitate cu 4 #include "stdio.h" #include "conio.h" int test4(int n) {

Funcţii

168

int udc,uc,k,nsalv; nsalv=n; udc=n%10; n/=10; k=1; while(n!=0 && k<=1) { uc=n%10; uc*=10; udc+=uc; k++; n/=10; } printf(" Numarul format cu ultimele doua cifre\n"); printf(" ale numarului %d este egal cu %d \n",nsalv,udc); if(udc%4==0) return 1; else return 0; } int main() { int n; printf(" n = "); scanf("%d",&n); if(test4(n)) printf(" Numarul %d este divizibil cu 4 \n",n); else printf(" Numarul %d nu este divizibil cu 4 \n",n); getch(); }

Un exemplu de rulare este: n = 123456 Numarul format cu ultimele doua cifre ale numarului 123456 este egal cu 56 Numarul 123456 este divizibil cu 4 n = 123

Page 87: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

169

Numarul format cu ultimele doua cifre ale numarului 123 este egal cu 23 Numarul 123 nu este divizibil cu 4

57. Fie n un număr natural cu trei cifre. Conform criteriului de divizibilitate cu 11, numărul n este divizibil cu 11 dacă suma dintre prima şi ultima sa cifră este egală cu a doua cifră. În programul următor este definită o funcţie care implementează criteriul de divizibilitate cu 11. // Test divizibilitate cu 11 #include "stdio.h" #include "conio.h" int test11(int n) { int uc,nsalv,i,cif[3]; nsalv=n; i=0; while(n!=0) { uc=n%10; cif[i]=uc; i++; n/=10; } if(cif[0]+cif[2]==cif[1]) return 1; else return 0; } int main() { int n; printf(" n = "); scanf("%d",&n); if(test11(n)) printf(" Numarul %d este divizibil cu 11 \n",n); else printf(" Numarul %d nu este divizibil cu 11 \n",n); getch(); }

Funcţii

170

Un exemplu de rulare este: n = 154 Numarul 154 este divizibil cu 11 n = 432 Numarul 432 nu este divizibil cu 11

58. Programul următor defineşte o funcţie pentru determinarea sumei elementelor de pe coloana k a unei matrici pătrate, k citit de la tastatură. // Suma elementelor de pe coloana k // a unei matrici patrate cu elemente numere intregi #include "stdio.h" #include "conio.h" int sum(int a[][20], int n,int k) { int i,j,s; s=0; for(i=0;i<n;i++) for(j=0;j<n;j++) if(j==k) s+=a[i][j]; return s; } int main() { int n,i,j,a[20][20],k; printf(" Dimensiunea: "); scanf("%d",&n); printf(" Elementele matricei \n"); for(i=0;i<n;i++) for(j=0;j<n;j++) { printf(" a [ %d ][ %d ] = ",i,j); scanf("%d",&a[i][j]); } printf("Coloana pe care se calculeaza suma:"); scanf("%d",&k); if(k>=0 && k<=n-1)

Page 88: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

171

printf(" Suma elementelor de pe coloana %d este %d \n",k,sum(a,n,k)); else printf(" Coloana %d inexistenta \n",k); getch(); }

Un exemplu de rulare este: Dimensiunea: 3 Elementele matricei a [ 0 ][ 0 ] = 1 a [ 0 ][ 1 ] = 2 a [ 0 ][ 2 ] = 3 a [ 1 ][ 0 ] = 4 a [ 1 ][ 1 ] = 5 a [ 1 ][ 2 ] = 6 a [ 2 ][ 0 ] = 7 a [ 2 ][ 1 ] = 8 a [ 2 ][ 2 ] = 9 Coloana pe care se calculeaza suma: 2 Suma elementelor de pe coloana 2 este 18

59. Programul următor defineşte o funcţie pentru determinarea sumei elementelor de pe linia k a unei matrice pătrate, k citit de la tastatură. // Suma elementelor de pe linia k // a unei matrici patrate cu elemente numere intregi #include "stdio.h" #include "conio.h" int sum(int a[][20], int n,int k) { int i,j,s; s=0; for(i=0;i<n;i++) for(j=0;j<n;j++) if(i==k) s+=a[i][j]; return s; } int main() { int n,i,j,a[20][20],k;

Funcţii

172

printf(" Dimensiunea: "); scanf("%d",&n); printf(" Elementele matricei \n"); for(i=0;i<n;i++) for(j=0;j<n;j++) { printf(" a [ %d ][ %d ] = ",i,j); scanf("%d",&a[i][j]); } printf(" Linia pe care se calculeaza suma: "); scanf("%d",&k); if(k>=0 && k<=n-1) printf(" Suma elementelor de pe linia %d este %d \n",k,sum(a,n,k)); else printf(" Linia %d inexistenta \n",k); getch(); }

Un exemplu de rulare este: Dimensiunea: 3 Elementele matricei a [ 0 ][ 0 ] = 1 a [ 0 ][ 1 ] = 2 a [ 0 ][ 2 ] = 3 a [ 1 ][ 0 ] = 4 a [ 1 ][ 1 ] = 5 a [ 1 ][ 2 ] = 6 a [ 2 ][ 0 ] = 7 a [ 2 ][ 1 ] = 8 a [ 2 ][ 2 ] = 9 Linia pe care se calculeaza suma: 1 Suma elementelor de pe linia 1 este 15

60. Programul următor defineşte o funcţie pentru determinarea sumei elementelor de pe diagonala principală a unei matrice pătrate. Facem observaţia că elementul ija este pe diagonala principală dacă i j= .

// Suma elementelor de pe diagonala principala

Page 89: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

173

// a unei matrici patrate cu elemente numere intregi #include "stdio.h" #include "conio.h" int sdp(int a[][20], int n) { int i,j,s; s=0; for(i=0;i<n;i++) for(j=0;j<n;j++) if(i==j) s+=a[i][j]; return s; } int main() { int n,i,j,a[20][20]; printf(" Dimensiunea: "); scanf("%d",&n); printf(" Elementele matricei \n"); for(i=0;i<n;i++) for(j=0;j<n;j++) { printf(" a [ %d ][ %d ] = ",i,j); scanf("%d",&a[i][j]); } printf(" Suma elementelor de pe diagonala principala este %d \n",sdp(a,n)); getch(); }

Un exemplu de rulare este: Dimensiunea: 3 Elementele matricei a [ 0 ][ 0 ] = 1 a [ 0 ][ 1 ] = 2 a [ 0 ][ 2 ] = 3 a [ 1 ][ 0 ] = 4 a [ 1 ][ 1 ] = 5 a [ 1 ][ 2 ] = 6 a [ 2 ][ 0 ] = 7 a [ 2 ][ 1 ] = 8 a [ 2 ][ 2 ] = 9

Funcţii

174

Suma elementelor de pe diagonala principala este 15

61. Programul următor defineşte o funcţie pentru determinarea sumei elementelor de pe diagonala secundară a unei matrice pătrate cu n linii şi n coloane. Facem observaţia că elementul ija este pe diagonala secundară dacă 1i j n+ = + . // Suma elementelor de pe diagonala secundara // a unei matrici patrate cu elemente numere intregi #include "stdio.h" #include "conio.h" int sum(int a[][20], int n) { int i,j,s; s=0; for(i=0;i<n;i++) for(j=0;j<n;j++) if((i+j)==n-1) s+=a[i][j]; return s; } int main() { int n,i,j,a[20][20]; printf(" Dimensiunea: "); scanf("%d",&n); printf(" Elementele matricei \n"); for(i=0;i<n;i++) for(j=0;j<n;j++) { printf(" a [ %d ][ %d ] = ",i,j); scanf("%d",&a[i][j]); } printf(" Suma elementelor de pe diagonala secundara este %d \n",sum(a,n)); getch(); }

Un exemplu de rulare este:

Page 90: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

175

Dimensiunea: 3 Elementele matricei a [ 0 ][ 0 ] = 1 a [ 0 ][ 1 ] = 2 a [ 0 ][ 2 ] = 3 a [ 1 ][ 0 ] = 4 a [ 1 ][ 1 ] = 5 a [ 1 ][ 2 ] = 6 a [ 2 ][ 0 ] = 7 a [ 2 ][ 1 ] = 8 a [ 2 ][ 2 ] = 9 Suma elementelor de pe diagonala secundara este 15

62. Programul următor defineşte o funcţie pentru determinarea sumei elementelor aflate sub diagonala principală (subdiagonale) a unei matrice pătrate cu n linii şi n coloane. Facem observaţia că elementul ija este subdiagonal dacă i j> . // Suma elementelor subdiagonale // ale unei matrici patrate cu elemente numere intregi #include "stdio.h" #include "conio.h" int sum(int a[][20], int n) { int i,j,s; s=0; for(i=0;i<n;i++) for(j=0;j<n;j++) if(i>j) s+=a[i][j]; return s; } int main() { int n,i,j,a[20][20]; printf(" Dimensiunea: "); scanf("%d",&n); printf(" Elementele matricei \n"); for(i=0;i<n;i++) for(j=0;j<n;j++)

Funcţii

176

{ printf(" a [ %d ][ %d ] = ",i,j); scanf("%d",&a[i][j]); } printf(" Suma elementelor subdiagonale este %d \n",sum(a,n)); getch(); }

Un exemplu de rulare este: Dimensiunea: 3 Elementele matricei a [ 0 ][ 0 ] = 1 a [ 0 ][ 1 ] = 2 a [ 0 ][ 2 ] = 3 a [ 1 ][ 0 ] = 4 a [ 1 ][ 1 ] = 5 a [ 1 ][ 2 ] = 6 a [ 2 ][ 0 ] = 7 a [ 2 ][ 1 ] = 8 a [ 2 ][ 2 ] = 9 Suma elementelor subdiagonale este 19

63. Programul următor defineşte o funcţie pentru determinarea sumei elementelor aflate deasupra diagonalei principale (supradiagonale) a unei matrice pătrate cu n linii şi n coloane. Facem observaţia că elementul ija este supradiagonal dacă i j< .

// Suma elementelor supradiagonale // ale unei matrici patrate cu elemente numere intregi #include "stdio.h" #include "conio.h" int sum(int a[][20], int n) { int i,j,s; s=0; for(i=0;i<n;i++) for(j=0;j<n;j++) if(i<j) s+=a[i][j]; return s;

Page 91: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

177

} int main() { int n,i,j,a[20][20]; printf(" Dimensiunea: "); scanf("%d",&n); printf(" Elementele matricei \n"); for(i=0;i<n;i++) for(j=0;j<n;j++) { printf(" a [ %d ][ %d ] = ",i,j); scanf("%d",&a[i][j]); } printf(" Suma elementelor supradiagonale este %d \n", sum(a,n)); getch(); }

Un exemplu de rulare este: Dimensiunea: 3 Elementele matricei a [ 0 ][ 0 ] = 1 a [ 0 ][ 1 ] = 2 a [ 0 ][ 2 ] = 3 a [ 1 ][ 0 ] = 4 a [ 1 ][ 1 ] = 5 a [ 1 ][ 2 ] = 6 a [ 2 ][ 0 ] = 7 a [ 2 ][ 1 ] = 8 a [ 2 ][ 2 ] = 9 Suma elementelor supradiagonale este 11

64. Un număr natural n nenul se numeşte perfect dacă este egal cu suma divizorilor săi diferiţi de n . Programul următor citeşte un număr natural k şi determină toate numerele perfecte din mulţimea { }1,2, ,kK . Testul de număr perfect este făcut de funcţia perfect. Funcţia are ca parametru de intrare întregul n şi returnează prin numele său valoarea 1 dacă n este număr perfect sau valoarea zero dacă n nu este număr perfect.

Funcţii

178

// Numerele perfecte din multimea {1,2,...,k} #include "stdio.h" #include "conio.h" int perfect(int n) { int i,sum_div; for(sum_div=0,i=1;i<=n/2;i++) if(n%i==0) sum_div+=i; if(n==sum_div) return 1; else return 0; } int main() { int k,n; printf(" k = "); scanf("%d",&k); for(n=1;n<=k;n++) if(perfect(n)) printf(" %d este numar perfect \n",n); getch(); }

Un exemplu de rulare este: k = 10000 6 este numar perfect 28 este numar perfect 496 este numar perfect 8128 este numar perfect

65. Programul din exemplul anterior poate fi modificat astfel încât să determine primele k numere perfecte. O variantă este următoarea: // Primele k numere perfecte #include "stdio.h" #include "conio.h" int perfect(int n) { int i,sum_div; for(sum_div=0,i=1;i<=n/2;i++)

Page 92: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

179

if(n%i==0) sum_div+=i; if(n==sum_div) return 1; else return 0; } int main() { int i,k,n; printf(" k = "); scanf("%d",&k); printf(" Primele %d numere perfecte sunt \n",k); i=1; n=1; while(n<=k) { if(perfect(i)) { printf(" %d este numar perfect \n",i); i++; n++; } else i++; } getch(); }

Un exemplu de execuţie este: k = 4 Primele 4 numere perfecte sunt 6 este numar perfect 28 este numar perfect 496 este numar perfect 8128 este numar perfect

66. Două numere naturale n şi m se numesc numere prietene dacă suma divizorilor lui m (fără numărul m ) este egală cu n şi suma divizorilor lui n (fără numărul n ) este egală cu m . De exemplu numerele 220 şi 284 sunt prietene. Programul următor testează dacă două numere

Funcţii

180

naturale sunt sau nu numere prietene. În program sunt definite două funcţii: sum_div pentru determinarea sumei divizorilor parametrului de intrare şi prietene care testează dacă parametrii de intrare sunt numere prietene. Se poate observa că dacă un număr este perfect atunci el este număr prieten cu el însuşi. // Test numere prietene #include "stdio.h" #include "conio.h" int sum_div(int n) { int i,sd; for(i=1,sd=0;i<=n/2;i++) if(n%i==0) sd+=i; return sd; } int prietene(int n, int m) { if(n==sum_div(m) && m==sum_div(n)) return 1; else return 0; } int main() { int n,m; printf(" n = "); scanf("%d",&n); printf(" m = "); scanf("%d",&m); if(prietene(n,m)) printf(" Numerele %d si %d sunt prietene \n",n,m); else printf(" Numerele %d si %d nu sunt prietene \n",n,m); getch(); }

Un exemplu de rulare este: n = 220

Page 93: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

181

m = 284 Numerele 220 si 284 sunt prietene n = 28 m = 28 Numerele 28 si 28 sunt prietene n = 220 m = 264 Numerele 220 si 264 nu sunt prietene

67. Programul din exemplul anterior poate fi modificat astfel încât să determine toate perechile de numere prietene distincte, mai mici sau egale cu o valoare dată p . // Perechile de numere prietene distincte mai mici decat p #include "stdio.h" #include "conio.h" int sum_div(int n) { int i,sd; for(i=1,sd=0;i<=n/2;i++) if(n%i==0) sd+=i; return sd; } int prietene(int n, int m) { if(n==sum_div(m) && m==sum_div(n)) return 1; else return 0; } int main() { int k,i,j,n,p; printf(" p = "); scanf("%d",&p); n=1; for(i=1;i<=p;i++) for(j=i+1;j<=p;j++) if(prietene(i,j))

Funcţii

182

{ printf("%d Numerele %d si %d sunt prietene \n",n,i,j); n++; } getch(); }

Un exemplu de rulare este: p = 2000 1 Numerele 220 si 284 sunt prietene 2 Numerele 1184 si 1210 sunt prietene

68. Este cunoscut faptul că orice număr natural par se poate reprezenta ca suma adouă numere prime (descompunerea Goldbach). Programul următor descompune numărul natural par în suma a două numere prime. Pentru testul de număr prim am folosit funcţia prim. //Proprietatea Goldbach #include "stdio.h" #include "conio.h" int prim(int n) { int i,p; p=1; for(i=2;i<=n/2;i++) if(n%i==0) p=0; return p; } int main() { int n,i,dif,p; printf(" n = "); scanf("%d",&n); if(n%2==0) { i=2; while(i<n-1) {

Page 94: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

183

if(prim(i)) { p=i; i++; } else i++; } dif=n-p; printf(" %d = %d + %d \n",n,dif,p); } else printf(" %d nu este numar par \n",n); getch(); }

Rularea programului conduce la rezultatele: n = 12 12 = 5 + 7 n = 13 13 nu este numar par

69. Programul următor determină şi afişează descompunerea Goldbach pentru toate numerele pare din mulţimea { }4,5, ,kK , k citit de la tastatură.

//Proprietatea Goldbach pe multimea // {3,4,...,k} #include "stdio.h" #include "conio.h" int prim(int n) { int i,p; p=1; for(i=2;i<=n/2;i++) if(n%i==0) p=0; return p; } int main() {

Funcţii

184

int k,n,i,dif,p; printf(" k = "); scanf("%d",&k); for(n=4;n<=k;n++) { if(n%2==0) { i=2; while(i<n-1) { if(prim(i)) { p=i; i++; } else i++; } dif=n-p; printf(" %d = %d + %d \n",n,dif,p); } } getch(); }

O rulare a programului produce rezultatele: k = 30 4 = 2 + 2 6 = 3 + 3 8 = 3 + 5 10 = 3 + 7 12 = 5 + 7 14 = 3 + 11 16 = 3 + 13 18 = 5 + 13 20 = 3 + 17 22 = 3 + 19 24 = 5 + 19 26 = 3 + 23 28 = 5 + 23 30 = 7 + 23

Page 95: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

185

70. Este cunoscut faptul că produsul a oricăror două numere naturale consecutive este un număr natural cuprins între două numere impare dintre care cel puţin unul este număr prim. Programul următor verifică acest rezultat pentru fiecare număr natural din mulţimea { }2,3, ,nK , n citit de la tastatură. Testul de număr prim este făcut de funcţia prim. //Produs numere consecutive #include "stdio.h" #include "conio.h" int prim(int n) { int i,p; p=1; for(i=2;i<=n/2;i++) if(n%i==0) p=0; return p; } int main() { int k,n,produs; printf(" n = "); scanf("%d",&n); for(k=2;k<=n;k++) { produs=k*(k+1); printf(" %d < %d * %d = %d < %d \n",produs-1,k,k+1,produs,produs+1); if(prim(produs-1)) printf(" %d este numar prim \n",produs-1); if(prim(produs+1)) printf(" %d este numar prim \n",produs+1); } getch(); }

Rezultatele unei rulări sunt: n = 5 5 < 2 * 3 = 6 < 7

Funcţii

186

5 este numar prim 7 este numar prim 11 < 3 * 4 = 12 < 13 11 este numar prim 13 este numar prim 19 < 4 * 5 = 20 < 21 19 este numar prim 29 < 5 * 6 = 30 < 31 29 este numar prim 31 este numar prim

71. Program pentru determinarea tuturor numerelor de cinci cifre, din intervalul [ ],li ls , li numar cu cinci cifre, care au următoarele proprietăţi: 1. ultima cifră este egală cu 7; 2. printre primele patru cifre, cifra 1 apare o singură dată; 3. suma tuturor cifrelor este un număr impar; 4. primele patru cifre sunt distincte. În program sunt definite trei funcţii utilizator pentru determinarea ultimei cifre, sumei cifrelor, numărului de cifre şi două funcţii care testează dacă primele patru cifre sunt distincte şi respectiv dacă printre primele patru cifre, cifra 1 apare o singură dată. Programul este prezentat în continuare: // Determinarea tuturor numerelor cu 5 cifre cu proprietatile // 1. Ultima cifra este 7 // 2. Printre primele patru cifre, cifra 1 apare exact o singura data // 3. Suma tuturor cifrelor este un numar impar // 4. Primele patru cifre sunt distincte #include "stdio.h" #include "conio.h" int v[4]; // Prototipurile functiilor utilizator

Page 96: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

187

int uc(int); int sc(int); int nc(int); int dis(void); int c1(void); // Functia care determina ultima cifra int uc(int n) { int u; u=n%10; return u; } // Functia care determina suma cifrelor si care // construieste vectorul format cu primele patru cifre int sc(int n) { int sc, uc, i; sc=0; i=0; uc=n%10; sc=sc+uc; while(n) { n=n/10; uc=n%10; sc=sc+uc; v[i]=uc; i++; } return sc; } // Functia care determina numarul de cifre ale numarului n int nc(int n) { int s; s=0;

Funcţii

188

while(n) { n=n/10; s++; } return s; } // Functia care testeaza daca primele patru cifre sunt distincte int dis(void) { int i,j,ok; ok=1; for(i=0;i<4;i++) for(j=i+1;j<4;j++) if(v[i]==v[j]) ok=0; return ok; } // Functia care testeaza daca printre primele patru cifre // exista cifra 1 int c1(void) { int i, ok; ok=0; for(i=0;i<3;i++) if(v[i]==1) ok=1; return ok; } // Functia principala int main() { int li,ls,i,nt; nt=0; printf(" li = "); scanf("%d",&li); printf(" ls = "); scanf("%d",&ls);

Page 97: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

189

if(li <= ls && nc(li)==5) { for(i=li;i<=ls;i++) { if(sc(i)%2==1) { if(dis() && c1() && uc(i)==7) { nt++; printf(" %d. i = %d \n",nt,i); } } } printf(" Numar total de numere %d \n",nt); } else printf(" Numarul %d nu are 5 cifre sau %d > %d \n",li,li,ls); getch(); }

Un exemplu de rulare este: li = 20000 ls = 21000 1. i = 20137 2. i = 20157 3. i = 20177 4. i = 20197 5. i = 20317 6. i = 20517 7. i = 20717 8. i = 20917 Numar total de numere 8

72. Funcţie pentru determinarea lungimii unui şir de caractere. // Functie pentru determinarea lungimii unui sir de caractere #include "stdio.h" #include "conio.h" void strlen(char *s,int &n) {

Funcţii

190

int i; for(i=0;*(s+i)!='\0';i++); n=i; } int main() { char s[100]; int lung; puts(" Sirul "); gets(s); strlen(s,lung); printf(" Sirul %s are %d caractere \n",s,lung); getch(); }

Un exemplu de rulare este următorul: Sirul Determinarea lungimii Sirul Determinarea lungimii are 21 caractere

73. Funcţie pentru copierea unui şir de caractere. //Functie pentru copierea unui sir de caractere #include "stdio.h" #include "conio.h" char * strcpy(char *sd, char *ss) { int i; for(i=0;*(ss+i)!='\0';*(sd+i)=*(ss+i),i++); *(sd+i)='\0'; return sd; } int main() { char sirs[20],sird[100],*sd; puts(" Sir sursa "); gets(sirs); sd=strcpy(sird,sirs); puts(" Sirul sursa "); printf(" %s \n",sirs); puts(" Sirul destinatie ");

Page 98: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

191

printf(" %s \n",sird); printf(" Adresa sir destinatie returnata de functie este %x \n",sd); printf(" Adresa sir destinatie din functia main este %x \n",sird); getch(); }

Sir sursa functie pentru copiere sir de caractere Sirul sursa functie pentru copiere sir de caractere Sirul destinatie functie pentru copiere sir de caractere Adresa sir destinatie returnata de functie este 22fec0 Adresa sir destinatie din functia main este 22fec0

74. Funcţie pentru concatenarea a două şiruri de caractere. //Functie pentru concatenarea a doua siruri #include "stdio.h" #include "conio.h" char * strcat(char *sd, char *ss) { int i,ld; for(i=0;*(sd+i)!='\0';i++); ld=i; i=0; while(*(ss+i)!='\0') { *(sd+ld+i)=*(ss+i); i++; } *(sd+ld+i)='\0'; return sd; } int main() { char sirs[20],sird[100],*sd; puts(" Sir sursa "); gets(sirs);

Funcţii

192

puts(" Sir destinatie "); gets(sird); sd=strcat(sird,sirs); puts(" Sirul concatenat "); printf(" %s \n",sird); printf(" Adresa sir destinatie returnata de functie este %x \n",sd); printf(" Adresa sir destinatie din functia main este %x \n",sird); getch(); }

Sir sursa concatenare Sir destinatie functie pentru Sirul concatenat functie pentru concatenare Adresa sir destinatie returnata de functie este 22fec0 Adresa sir destinatie din functia main este 22fec0

75. Funcţie pentru determinarea primei apariţii a unui caracter într-un şir de caractere. //Functie de cautare a unui caracter intr-un sir //Functia returneaza adresa primei aparitii a caracterului sau NULL #include "stdio.h" #include "conio.h" char * strchr(char *s, char c) { int i,gasit=0; char *adr; i=0; while(*(s+i)!='\0') { if(*(s+i)==c) { adr=s+i; gasit=1; break;

Page 99: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

193

} else i++; } if(gasit) return adr; else return NULL; } int main() { char sir[20],c,*s; int n; puts(" Sirul "); gets(sir); puts(" Caracterul "); scanf("%c",&c); s=strchr(sir,c); if(s) { n=s-sir; printf(" Caracterul %c are prima aparitie in sirul %s pe pozitia %d \n",c,sir,n); } else printf(" Caracterul %c nu apare in sirul %s \n",c,sir); getch(); }

Un exemplu de rulare este: Sirul abecedar Caracterul c Caracterul c are prima aparitie in sirul abecedar pe pozitia 3 Sirul abecedar Caracterul # Caracterul # nu apare in sirul abecedar

Funcţii

194

76. Variantă recursivă a algoritmului lui Euclid. Determinarea celui mai mare divizor comun cu algoritmul lui Euclid poate fi făcută recursive, folosind relaţia de recurenţă:

( )( )

≠=

=0%,,0

),(ndacanmncmmdcmcmmdcndacam

nmcmmdc .

Programând această relaţie de recurenţă se obţine următorul cod: // Varianta recursiva a algoritmului lui Euclid #include "stdio.h" #include "conio.h" int cmmdc(int m, int n) { if(n==0) return m; else return cmmdc(n,m%n); } int main() { int m,n; printf(" m = "); scanf("%d",&m); printf(" n = "); scanf("%d",&n); if(cmmdc(m,n)==1) printf(" Numerele %d si %d sunt prime intre ele \n",m,n); else printf(" Numerele %d si %d nu sunt prime intre ele \n",m,n); printf(" cmmdc( %d , %d ) = %d \n",m,n,cmmdc(m,n)); getch(); }

Se obţin rezultatele: m = 124

Page 100: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

195

n = 24 Numerele 124 si 24 nu sunt prime intre ele cmmdc( 124 , 24 ) = 4

77. Program următor afişează primii n termeni ai şirului lui Fibonacci, definit prin: :fib N N→ , ( )0 1fib = ,

( )1 1fib = , ( ) ( )( 1) 2fib n fib n fib n= − + − , ( ) 2n∀ ≥ .

#include <stdio.h> #include <conio.h> int fib(int n) { if (n==0 || n==1) return 1; else return fib(n-1)+fib(n-2); } int main() { int n,i; printf("Generarea recursiva a termenilor sirului lui Fibonacci \n"); printf("Rangul maxim n = "); scanf("%d",&n); printf("Primii %d termeni din sirul Fibonacci:\n", n); for(i=0;i<n;i++) printf("Termenul de rang %d in sirul Fibonacci este %d \n",i,fib(i)); getch(); }

Generarea recursiva a termenilor sirului lui Fibonacci Rangul maxim n = 5 Primii 6 termeni ai sirului Fibonacci sunt: Termenul de rang 0 in sirul Fibonacci este 1 Termenul de rang 1 in sirul Fibonacci este 1 Termenul de rang 2 in sirul Fibonacci este 2 Termenul de rang 3 in sirul Fibonacci este 3 Termenul de rang 4 in sirul Fibonacci este 5

Funcţii

196

78. Programul afişează primii n termeni din şirurile aritmetic-geometric-armonic ale lui Gauss, definite de relaţiile de recurenţă: 0 0a p= > , 0 0b q= > , 0 0c r= > ,

1 1 1

3n n n

na b ca − − −+ +

= , 31 1 1n n n nb a b c− − −= ⋅ ⋅ ,

1 1 1

31 1 1n

n n n

c

a b c− − −

=+ +

, ( ) *n N∀ ∈ .

#include <stdio.h> #include <conio.h> #include <math.h> double p,q,r; double a(int); double b(int); double c(int); double a(int n) { if (n==0) return p; else return (a(n-1)+b(n-1)+c(n-1))/3; } double b(int n) { if (n==0) return q; else return pow(a(n-1)*b(n-1)*c(n-1), 1./3); } double c(int n) { if (n==0) return r; else return 3/(1/a(n-1)+1/b(n-1)+1/c(n-1)); } int main() { int n,i;

Page 101: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

197

printf("Sirurile aritmetic-geometric-armonic ale lui Gauss\n"); printf("a( 0 ) = p = "); scanf("%lf", &p); printf("b( 0 ) = q = "); scanf("%lf", &q); printf("c( 0 ) = r = "); scanf("%lf", &r); printf(" Rangul maxim al termenilor n = "); scanf("%i", &n); for(i=0;i<n;i++) printf("a( %d ) = %.4f\tb( %d ) = %.4f\tc( %d ) = %.4f\n", i, a(i), i, b(i), i, c(i)); getch(); }

Sirurile aritmetic-geometric-armonic ale lui Gauss a( 0 ) = p = 1 b( 0 ) = q = 2 c( 0 ) = r = 3 Rangul maxim al termenilor n = 4 a( 0 ) = 1.0000 b( 0 ) = 2.0000 c( 0 ) = 3.0000 a( 1 ) = 2.0000 b( 1 ) = 1.8171 c( 1 ) = 1.6363 a( 2 ) = 1.8178 b( 2 ) = 1.8117 c( 2 ) = 1.8056 a( 3 ) = 1.8117 b( 3 ) = 1.8117 c( 3 ) = 1.8117

79. Programul următor afişează pentru orice pereche ( ),m n N N∈ × , pentru 0,1,...,i m= , 0,1,...,j n= , valorile funcţiei lui Ackerman definită astfel: :Ack N N N× → ,

( ) ( )( )( )

1, 0, 1,1 , 0

1, , 1 ,

n mAck m n Ack m n

Ack m Ack m n altfel

+ =

= − = − −

,

( )( ),m n N N∀ ∈ × .

Funcţii

198

i Pentru m>3, funcţia lui Ackerman este puternic recursivă şi determinarea valorilor ei poate să dureze foarte mult.

#include <stdio.h> #include <conio.h> int ack(int m,int n) { if (m==0) return n+1; else if (n==0) return ack(m-1,1); else return ack(m-1,ack(m,n-1)); } int main() { int n,m,i,j; printf(" Functia lui Ackerman \n"); printf(" Tastati valoarea lui m = "); scanf("%d",&m); printf(" Tastati valoarea lui n = "); scanf("%d",&n); for(i=0;i<=m;i++) for(j=0;j<=n;j++) printf("Ackerman ( %d , %d ) = %d\n", i, j, ack(i,j)); getch(); }

Functia lui Ackerman Tastati valoarea lui m = 3 Tastati valoarea lui n = 2 Ackerman ( 0 , 0 ) = 1 Ackerman ( 0 , 1 ) = 2 Ackerman ( 0 , 2 ) = 3 Ackerman ( 1 , 0 ) = 2 Ackerman ( 1 , 1 ) = 3 Ackerman ( 1 , 2 ) = 4 Ackerman ( 2 , 0 ) = 3 Ackerman ( 2 , 1 ) = 5 Ackerman ( 2 , 2 ) = 7 Ackerman ( 3 , 0 ) = 5

Page 102: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

199

Ackerman ( 3 , 1 ) = 13 Ackerman ( 3 , 2 ) = 29

80. Programul afişează pentru orice interval [ ],a b R⊂ ,

şi pentru orice *n N∈ , toate valorile b af a in− +

,

0,1,...,i n= , ale funcţiei Manna-Pnueli definită prin:

:f R R→ , ( )( )1, 12

( )2 ,

x xf x

f f x altfel− ≥= +

, ( ) x R∀ ∈ .

#include <stdio.h> #include <conio.h> double f(double x) { if (x>=12) return x-1; else return f(f(x+2)); } int main() { float x,a,b,h; int n,i; printf(" Intervalul [a,b] \n"); printf("a = "); scanf("%lf", &a); printf("b = "); scanf("%lf", &b); printf(" Numarul de puncte n = "); scanf("%i", &n); h=(b-a)/n; for(i=0;i<=n;i++) { x=a+i*h; printf("MP( %g ) = %g\n", x, f(x)); } getch(); }

Intervalul [a,b]

Funcţii

200

a = 11.5 b = 13.5 Numarul de puncte n = 10 MP( 11.5 ) = 11.5 MP( 11.7 ) = 11.7 MP( 11.9 ) = 11.9 MP( 12.1 ) = 11.1 MP( 12.3 ) = 11.3 MP( 12.5 ) = 11.5 MP( 12.7 ) = 11.7 MP( 12.9 ) = 11.9 MP( 13.1 ) = 12.1 MP( 13.3 ) = 12.3 MP( 13.5 ) = 12.5

81. Problema turnurilor din Hanoi. Se dau trei tije notate A, B, C, aşezate în poziţie verticală. Pe tija A se găsesc n discuri care au razele r1>r2>...>rn, aşezate în ordine descrescătoare a razelor începând de la baza tijei. Problema constă în determinarea mutărilor necesare transferului celor n discuri de pe tija A pe tija B, în final discurile fiind aşezate pe tija B în aceeaşi ordine ca pe tija A. O mutare constă în aşezarea unui singur disc pe o tijă goală, sau peste un disc cu raza mai mare, pe oricare dintre tije. Nu se poate aşeza un disc cu rază mai mare peste un disc cu raza mai mică. . Rezolvarea problemei se poate face astfel: dacă 1=n se mută discul de pe tija sursă pe tija destinaţie, problema fiind astfel rezolvată. Dacă 1≠n se mută primele 1−n discuri de pe tija s pe tija m folosind ca tijă de manevră d , apoi se mută discul rămas pe tija s pe tija d , şi în final se mută cele 1−n discuri de pe tija m pe tija d , folosind ca tijă de manevră tija s . Notăm cu ( )mdsnh ,,, şirul mutărilor necesare rezolvării problemei şi cu ba → mutarea primului disc de pe tija a pe tija b . Se obţine relaţia de recurenţă a şirului de mutări din problema turnurilor din Hanoi:

Page 103: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

201

( ) ( ) ( )

>−→−=→

=1,,,1,,,11

,,,ndacasdmnhdsdmsnhndacads

mdsnh

. Rezolvarea constă în programarea relaţii de recurenţă a şirului de mutări din problema turnurilor din Hanoi. Programul afişează pentru orice *n N∈ dat, mutările care conduc la soluţia problemei. #include <stdio.h> #include <conio.h> void hanoi(int n, char a, char b, char c) { if (n==1) { printf("Se muta discul %d de pe %c pe %c\n", n, a, b); } else { hanoi(n-1,a,c,b); printf("Se muta discul %d de pe %c pe %c\n", n, a, b); hanoi(n-1,c,b,a); } } int main() { int n; printf("Problema turnurilor din Hanoi\n"); printf("Numarul de discuri n = "); scanf("%d",&n); hanoi(n,'A','B','C'); getch(); }

Problema turnurilor din Hanoi Numarul de discuri n = 3 Se muta discul 1 de pe A pe B Se muta discul 2 de pe A pe C Se muta discul 1 de pe B pe C Se muta discul 3 de pe A pe B Se muta discul 1 de pe C pe A Se muta discul 2 de pe C pe B

Funcţii

202

Se muta discul 1 de pe A pe B

7.6. Exerciţii 60. Să se scrie funcţia void citirevector(int v[], int n) care citeşte de la tastatură n numere întregi şi le memorează în vectorul v. 61. Să se scrie funcţia void afişarevector(int

v[], int n) care afişează pe ecran primele n numere întregi din vectorul v. 62. Scrieţi un program C care să testeze cele două funcţii (adică citeşte un vector de la tastatură şi îl afişează pe ecran). 63. Scrieţi o funcţie care afişează suma elementelor pare şi produsul elementelor impare dintr-un vector şi un program C care să o testeze. 64. Scrieţi o funcţie care afişează elementele maxim şi minim dintr-un vector şi un program C care să o testeze. 65. Scrieţi o funcţie care întoarce poziţia primei apariţii a unui număr m într-un vector şi un program C care să o testeze. 66. Scrieţi o funcţie care adună doi vectori şi pune rezultatul în al treilea vector, şi un program C care să o testeze. 67. Scrieţi o funcţie care sortează valorile unui vector de numere reale şi un program care să o testeze. 68. Scrieţi o funcţie nerecursivă care returnează valoarea n! (n este parametrul funcţiei) şi un program C care să o testeze (Indicaţie: folosiţi formula n! = 1 * 2 * ... * n). 69. Scrieţi o funcţie recursivă care returnează valoarea n! (n este parametrul funcţiei) şi un program C care să o testeze (Indicaţie: folosiţi formula recursivă n! = n * (n-1)! pentru n>1 şi n!=1 pentru n=0 sau 1).

Page 104: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

203

70. Scrieţi o funcţie nerecursivă care returnează al n-lea termen din şirul lui Fibonacci (n este parametrul funcţiei) şi un program C care să o testeze (Indicaţie: folosiţi formula t1=1, t2=1, ti+2=ti+ti+1 pentru i>1). 71. Scrieţi o funcţie recursivă care returnează al n-lea termen din şirul lui Fibonacci (n este parametrul funcţiei) şi un program C care să o testeze (Indicaţie: folosiţi formula recursivă ti=ti-1+ti-2 pentru n>2 şi ti=1 altfel). 72. Scrieţi o funcţie nerecursivă care returnează valoarea cmmdc(m,n) (m şi n sunt parametrii funcţiei) şi un program C care să o testeze (Indicaţie: cât timp m este diferit de n, se scade din variabila mai mare valoarea variabilei mai mici; când m=n atunci m e cmmdc). 73. Scrieţi o funcţie nerecursivă care returnează valoarea cmmdc(m,n) (m şi n sunt parametrii funcţiei) şi un program C care să o testeze (Indicaţie: folosiţi formula recursivă cmmdc(a,b) = a, daca b=0; cmmdc(a,b) = cmmdc(b, a%b), daca a>b; cmmdc(a,b) = cmmdc(a, b%a), daca a<=b;).

Page 105: Bazele_programarii_calculatoarelor

205

888... PPPOOOIIINNNTTTEEERRRIII ŞŞŞIII GGGEEESSSTTTIIIUUUNNNEEEAAA DDDIIINNNAAAMMMIIICCCĂĂĂ AAA MMMEEEMMMOOORRRIIIEEEIII

8.1. Pointeri Un pointer este o variabilă a cărei valoare este o

adresă din memoria calculatorului. Prin intermediul pointerului, programatorul poate accesa şi modifica informaţia stocată în memorie la adresa indicată de acesta. Pentru a accesa într-un mod coerent informaţia stocată în memorie, fiecare pointer are un tip de bază care determină tipul informaţiei aflate în memorie.

Pentru a declara o variabilă de tip pointer se foloseşte sintaxa: tip *nume;

unde tip reprezintă tipul de bază al pointerului, iar nume reprezintă numele variabilei pointer. Tipul informaţiei referite de către un pointer poate fi: un tip fundamental, tipul void, o structură, o uniune, un tablou, o funcţie sau alt pointer. Un pointer cu tipul de bază void este un pointer către o informaţie al cărei tip nu se cunoaşte exact.

De exemplu, declaraţiile următoare

Pointeri şi gestiunea dinamică a memoriei

206

int *varsta; char *nume;

declară pointerul varsta care va conţine adresa unui număr întreg şi pointerul nume care va conţine adresa unui caracter.

8.2. Operatorii &, * şi -> Operatorul unar & (denumit în acest context

operatorul de referenţiere) se poate folosi pentru a obţine adresa unei variabile. Această adresă poate fi memorată într-un pointer. În sens invers, informaţia din memorie de la o anumită adresă (memorată într-un pointer) poate fi accesată folosind operatorul unar * (denumit în acest context operatorul de dereferenţiere).

De exemplu, în urma execuţiei secvenţei de program int x = 3, *px; px = &x; *px = 5;

variabila x capătă valoarea 5, deoarece pointerul px conţine adresa variabilei x, deci instrucţiunea *px = 5 este echivalentă cu x = 5.

Dacă p este un pointer către o structură sau o uniune, rezultatul expresiei *p este structura (sau uniunea) a cărei adresă este memorată în p. Pentru a accesa membrii acesteia folosind operatorul punct (care are o prioritate mai mare decât operatorul *), expresia *p trebuie scrisă între paranteze rotunde astfel: (*p).membru. Pentru a evita această construcţie, putem

Page 106: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

207

folosi operatorul -> (caracterul minus urmat de caracterul mai mare) astfel: p->membru.

i Operatorii * şi -> nu se pot aplica pointerilor de tip void.

i Indiferent de tipul pointerului, acestuia i se poate atribui valoarea 0, cu semnificaţia "pointer neiniţializat".

i În biblioteca stdio.h, este definită constanta simbolică NULL cu valoarea 0, a cărei folosire este recomandată în locul valorii 0 în cazul pointerilor.

8.3. Aritmetica pointerilor În afară de operatorul &, un pointer poate fi folosit

în diverse expresii cu operatorii de adunare, scădere şi indexare, astfel: adunarea/scaderea unui pointer şi întreg, scăderea a doi pointeri şi indexarea unui pointer cu un întreg. În fiecare dintre aceste operaţii se ţine cont de tipul informaţiei pe care o adresează pointerul, deci ele nu pot fi aplicate pointerilor de tip void.

Dacă p este un pointer cu tipul de bază t şi n est un număr întreg, atunci expresiile p+n şi p-n sunt corecte, rezultatul lor fiind adrese de tipul t. Astfel, dacă privim memoria calculatorului ca un vector cu elemente de tipul t, atunci rezultatul expresiei p+n este adresa elementului aflat la n elemente distanţă după p, iar rezultatul expresiei p-n este adresa elementului aflat la n elemente distanţă înaintea lui p.

Dacă p şi q sunt pointeri cu tipul de bază t, atunci expresiile p-q şi q-p sunt corecte, rezultatul lor fiind un

Pointeri şi gestiunea dinamică a memoriei

208

număr întreg. Astfel, dacă privim memoria calculatorului ca un vector cu elemente de tipul t, atunci rezultatul expresiei p-q este numărul de elemente de la p la q, iar rezultatul expresiei q-p este numărul de elemente de la q la p,

Dacă p este un pointer cu tipul de bază t şi n un

număr întreg, atunci expresia p[n] este corectă, rezultatul ei fiind o valoare de tipul t. Astfel, dacă privim memoria calculatorului ca un vector cu elemente de tipul t, atunci rezultatul expresiei p[n] este valoarea elementului aflat la n elemente distanţă după lui p. Practic, expresia p[n] este echivalentă cu expresia *(p+n).

De exemplu, în urma execuţiei instrucţiunilor următoare: int vec[10]; // un vector cu 10 elemente intregi int *p = &vec[2]; // p este pointer la al 3-lea element din vec int *q = &vec[4]; // q este pointer la al 5-lea element din vec

p+2 este egal cu q, p-q este egal cu 2, q-2 este egal cu p, iar p[2], q[0], *q şi vec[4] reprezintă aceeaşi informaţie.

p+3 p p-3

q-p=4

q

Page 107: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

209

8.4. Legătura între tablouri şi pointeri Aşa cum s-a observat în secţiunea anterioară, un

pointer poate fi folosit pentru a accesa elementele unui masiv. Acest lucru este posibil deoarece numele unui masiv este un pointer către zona de memorie unde sunt stocate elementele acestuia. Totuşi, valoarea acestui pointer nu poate fi modificată (deci este un pointer constant).

Din acest motiv, un şir de caractere (care este de fapt un vector cu elemente de tip char şi ultimul caracter NUL) poate fi privit ca un pointer la informaţii de tip char (vezi capitolul 8).

Această relaţie se poate generaliza şi pentru masive multidimensionale. De exemplu, pentru următoarele instrucţiuni: int tab[10][20][30]; int *pt = &tab[0][0][0];

elementul tab[m][n][p] poate fi accesat şi prin construcţiile (echivalente) *(pt+(m*30+n)*20+p) (pt+((m*30+n)*20)[p] (pt+m*30*20)[n*20 + p] pt[(m*30+n)*20 + p]

8.5. Gestiunea dinamică a memoriei În limbajul C, dimensiunea fiecărei variabile

trebuie cunoscută în momentul compilării programului; pentru variabilele cu tip fundamental, dimensiunea este furnizată implicit de tipul acestor; pentru masive,

Pointeri şi gestiunea dinamică a memoriei

210

dimensiunea este furnizată explicit prin dimensiunile acestora (şi tipul elementelor), etc.

Pentru a lucra cu informaţii ale căror dimensiuni nu sunt cunoscute la momentul compilării programului există două alternative: declararea la compilare a unor dimensiuni care acoperă în mod sigur necesarul de memorie sau alocarea memoriei necesare în timpul execuţiei programului.

Funcţiile standard pentru gestiunea dinamică a memoriei se găsesc în biblioteca malloc.h. Pentru a fi generale, aceste funcţii lucrează cu pointeri de tip void, deci este necesar ca programatorul să execute conversii explicite de tip.

Tabel 8.1 Funcţii standard pentru gestiunea dinamică a memoriei

Prototip void* calloc (unsigned cnt,

unsigned size)

Descriere Efect: alocă o zonă de memorie de dimensiune cnt*size octeţi şi completează cu valoarea 0 conţinutul acesteia. Rezultat: adresa zonei de memorie alocate sau NULL în caz de eroare

Prototip void* malloc (unsigned size) Descriere Efect: alocă o zonă de memorie de

dimensiune size octeţi Rezultat: adresa zonei de memorie alocate sau NULL în caz de eroare

Prototip void* realloc(void * ptr, unsigned

size)

Page 108: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

211

Descriere Efect: realocă zona de memorie indicată de pointerul ptr astfel încât să aibă dimensiunea size octeţi Rezultat: adresa zonei de memorie realocate sau NULL în caz de eroare

Prototip void free (void *ptr) Descriere Efect: eliberează zona de memorie indicată

de pointerul ptr Algoritmul general de lucru în cazul folosirii

dinamice a memoriei constă în alocarea memorie, folosirea ei şi, în final, eliberarea acesteia.

De exemplu, dacă vrem să citim de la tastatură un număr variabil de valori întregi putem folosi următoarea secvenţă: int i, nrvalori, *valori; printf(“Introduceti numarul de valori: “); scanf(“%d”, &nrvalori); // citim numarul de valori valori = (int *) calloc(nrvalori, sizeof(int)); if(valori == NULL) { printf(“Memorie insuficienta\n”); } else { for (i = 0; i < nrvalori; ++i) { printf(“Introduceti valoarea a %d-a: “, i+1); scanf(“%d”, &valori[i]); } // alte operatii... free(valori); }

Pointeri şi gestiunea dinamică a memoriei

212

8.6. Funcţii pentru operaţii cu blocuri de memorie Aceste funcţii permit prelucrarea simplă

informaţiei din memorie la nivel de octeţi, dar cu o viteză ridicată. Funcţiile standard pentru manipularea blocurilor de memorie se găsesc în biblioteca string.h. În prototipurile acestor funcţii, parametrul c de tip int reprezintă de fapt un octet (sau un caracter). Ele pot fi folosite atât pentru zone de memorie alocate dinamic, cât şi pentru zone de memorie declarate static.

Tabel 8.2 Funcţii standard pentru manipularea blocurilor de memorie

Prototip void* memcpy (void *dest, void

*src, unsigned cnt)

Descriere Efect: copiază cnt octeţi din zona de memorie src în dest (src şi dest trebuie să fie disjuncte) Rezultat: adresa destinaţie dest

Prototip void* memccpy (void *dest, void

*src, int c, unsigned cnt)

Descriere Efect: copiază cnt octeţi din zona de memorie src în dest sau până la apariţia primului octet c. Rezultat: adresa din destinaţie a octetului care urmează după c sau NULL dacă c nu apare în destinaţie (şi s-au copiat cnt octeţi)

Prototip void* memmove (void *dest, void

*src, unsigned cnt)

Descriere Efect: copiază cnt octeţi din zona de

Page 109: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

213

memorie src în dest (nu neapărat disjuncte). Rezultat: adresa sursă src.

Prototip void* memchr (void *src, int c,

unsigned cnt)

Descriere Efect: caută valoarea c în primii cnt octeţi din zona de memorie src Rezultat: adresa octetului c sau NULL dacă c nu a fost găsit

Prototip void* memset (void *dest, int c,

unsigned cnt)

Descriere Efect: scrie valoarea c în primii cnt octeţi din zona de memorie dest Rezultat: adresa destinaţie dest

Prototip int memcmp (void *src1, void *src2,

unsigned cnt)

Descriere Efect: compară în ordine cel mult cnt octeţi din zonele de memorie src1 şi src2 Rezultat: valoarea 0 dacă informaţia din src1 este identică cu cea din src2; o valoare negativă dacă primul octet diferit din src1 este mai mic decât octetul corespunzător din src2; o valoare pozitivă dacă primul octet diferit din src1 este mai mare decât octetul corespunzător din src2

Prototip int memicmp (void *src1, void

*src2, unsigned cnt)

Descriere Efect: compară în ordine cel mult cnt octeţi din zonele de memorie src1 şi src2, fără a face distincţie între literele mari şi mici.

Pointeri şi gestiunea dinamică a memoriei

214

Rezultat: valoarea 0 dacă informaţia din src1 este la fel cu cea din src2; o valoare negativă dacă primul octet diferit din src1 este mai mic decât octetul corespunzător din src2; o valoare pozitivă dacă primul octet diferit din src1 este mai mare decât octetul corespunzător din src2

De exemplu, pentru a copia conţinutul unui vector de numere întregi cu 20 de elemente în alt vector putem folosi următoare secvenţă de program: int src[20], dest[20]; ... memcpy(dest, src, sizeof(src));

8.7. Exemple 82. Programul următor citeşte un vector de dimensiune n (n citit de la tastatura) şi îl afişează în ordine inversă. Dimensiunea maximă a vectorului depinde doar de memoria calculatorului, nefiind fixată la compilare. Accesul la elementele vectorului se face prin pointeri şi operatorul de dereferenţiere. #include<stdio.h> #include<stdlib.h> #include<conio.h> int main() { int n, i; int *vector; printf("Dimensiunea vectorului n:"); scanf("%i", &n); vector = (int*) malloc(sizeof(int) * n); for(i=0; i<n; ++i) { printf("Introduceti v[%d]:", i);

Page 110: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

215

scanf("%i", vector + i); } for(i=n - 1; i>=0; --i) printf("v[%i] = %i\n", i, *(vector + i)); free(vector); getch(); }

Rezultatul execuţiei programului este: Dimensiunea vectorului n:6 Introduceti v[0]:1 Introduceti v[1]:2 Introduceti v[2]:3 Introduceti v[3]:4 Introduceti v[4]:5 Introduceti v[5]:6 v[5] = 6 v[4] = 5 v[3] = 4 v[2] = 3 v[1] = 2 v[0] = 1

83. Următorul program citeşte un vector de n numere şi verifică dacă este palindrom. Dimensiunea maximă a vectorului depinde doar de memoria calculatorului, nefiind fixată la compilare. Accesul la elementele vectorului se face prin pointeri şi operatorul de indexare. #include<stdio.h> #include<stdlib.h> #include<conio.h> int main() { int n, i, pal=1; int *vector; printf("Dimensiunea vectorului n:"); scanf("%i", &n); vector = (int*) malloc(sizeof(int) * n); for(i=0; i<n; ++i) {

Pointeri şi gestiunea dinamică a memoriei

216

printf("Introduceti v[%d]:", i); scanf("%i", &vector[i]); } for(i=0; i<n/2 && pal == 1; ++i) if(vector[i] != vector[n-i-1]) pal = 0; if(pal == 1) printf("Vectorul este palindrom.\n"); else printf("Vectorul nu este palindrom.\n"); free(vector); getch(); }

Rezultatul execuţiei programului este: Dimensiunea vectorului n:5 Introduceti v[0]:1 Introduceti v[1]:5 Introduceti v[2]:3 Introduceti v[3]:5 Introduceti v[4]:1 Vectorul este palindrom.

84. Următorul program determină ordinea valorilor x şi y într-un vector de întregi, folosind pointeri către tipul întreg. // Ordinea aparitiei valorilor x si y intr-un vector de intregi #include "stdio.h" #include "conio.h" int main() { int *p,*q,a[50],x,y,i,n; p=a; q=a; printf(" n = "); scanf("%d",&n); for(i=0;i<n;i++) { printf(" a[%d]= ",i); scanf("%d",a+i); } printf(" x = ");

Page 111: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

217

scanf("%d",&x); printf(" y = "); scanf("%d",&y); if(x!=y) { while( *p!=x && p<=a+n ) p++; while( *q!=y && q<=a+n ) q++; if(p==a+n+1 || q==a+n+1) printf(" %d sau %d nu este componenta a sirului \n",x,y); else if(p>q) printf(" y = %d precede x = %d \n",y,x); else printf(" x = %d precede y = %d \n",x,y); } else printf(" x = y \n"); printf(" Sfarsit \n"); getch(); }

Un exemplu de rulare este următorul: n = 5 a[0]= -12 a[1]= 3 a[2]= 21 a[3]= 54 a[4]= -2 x = 54 y = 3 y = 3 precede x = 54

85. Algoritmul de sortare prin numarare count_sort. Presupunem că dorim să sortăm n numere naturale din intervalul [1, m]. Algoritmul constă în construirea unui vector v al frecventelor de apariţie, în care v[i] este egal cu numărul apariţiilor numărului i. Cele n numere pot fi ordonate folosind frecventele lor de apariţie. De exemplu

Pointeri şi gestiunea dinamică a memoriei

218

(n=7 şi m=7) să presupunem că dorim să sortăm elementele din mulţimea {7, 2, 2, 2, 3, 4, 4}. Se construieşte vectorul frecventelor v[1]=0 (1 nu este în mulţime), v[2]=3 (2 apare de trei ori), v[3]=1, v[4]=2, v[5]=0, v[6]=0, v[7]=1. Pentru fiecare i cu proprietatea v[i]!=0, se afişează i de v[i] ori. Vectorul sortat va fi 2, 2, 2, 3, 4, 4, 7. //Sortare crescatoare prin metoda de numarare count_sort #include <stdio.h> #include <conio.h> #include <malloc.h> // Prototipurile functiilor folosite in program void citeste_numere(int *, int, int); // citirea celor n numere naturale cuprinse intre 1 si 100 void init_v(int *, int); // initializarea vectorului frecventelor void count_sort(int *, int *, int,int); //functia pentru sortare prin numarare void afis_sortc(int *, int); //functia de afisare crescatoare a celor n numere sortate void afis_sortd(int *, int); //functia de afisare descrescatoare a celor n numere sortate // Functia pentru citirea vectorului void citeste_numere(int x[],int n, int m) { int i; for(i=0;i<n;i++) { printf(" Numarul %d = ",i+1); scanf("%d",x+i);

Page 112: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

219

if(x[i] <= 0 || x[i] > m) { printf(" Numarul %d nu este in domeniu \n",x[i]); break; } } } // Functia de initializare a vectorului frecventelor void init_v(int v[], int m) { int i; for(i=1;i<=m;v[i]=0,i++); } // Functia count de sortare void count_sort(int x[], int v[], int n, int m) { int i,f,k; printf(" Sortare \n"); for( k= 1 ; k <= m ; k++ ) { f=0; for(i = 0;i < n;i++) if(x[i]==k) f++; v[k]=f; } } // Functia pentru afisarea crescatoare a numerelor void afis_sortc(int v[], int m) { int i,k; printf(" Afisare crescatoare \n"); for(i=1;i <= m;i++) { if(v[i]!=0) for(k=1;k<=v[i];k++)

Pointeri şi gestiunea dinamică a memoriei

220

printf("%d ",i); } printf("\n"); } // Functia pentru afisarea descrescatoare a numerelor void afis_sortd(int v[], int m) { int i,k; printf(" Afisare descrescatoare \n"); for(i=m;i >= 1 ;i--) { if(v[i]!=0) for(k=1;k<=v[i];k++) printf("%d ",i); } printf("\n"); } // Vectorul numere (alocat dinamic) contine numerele citite // Vectorul v este vectorul (alocat dinamic) // frecventelor numerelor in vectorul numere // n numarul de numere citite // nmax si m limitele lui n respectiv a valorilor numerelor citite int main() { long int nmax = 1000000; int n, m = 100, *numere, *v; char c;// variabila folosita in evitarea utilizarii functiei getch() printf(" n = "); scanf("%d",&n); //testul valorii lui n if(n > 0 && n < nmax) { //alocare memorie pentru vectorul de numere care se va citi if(numere=(int *)malloc(n*sizeof(int))) { // citire numere

Page 113: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

221

citeste_numere(numere,n,m); //alocare memorie pentru vectorul frecventelor if(v=(int *)malloc(m*sizeof(int))) { //initializare vector frecvente init_v(v,m); //sortarea prin numarare count_sort(numere,v,n,m); //afisarea crescatoare a vectorului sortat afis_sortc(v,m); //afisarea descrescatoare a vectorului sortat afis_sortd(v,m); //eliberarea zonelor de memorie alocate free(numere); free(v); } else printf(" Alocare esuata \n"); } else printf(" Alocare esuata \n");} else printf(" Numarul %d nu este in domeniu \n",n); getch(); }

Un exemplu de rulare a programului este: n = 7 Numarul 1 = 7 Numarul 2 = 2 Numarul 3 = 2 Numarul 4 = 2 Numarul 5 = 3 Numarul 6 = 4 Numarul 7 = 4 Sortare Afisare crescatoare 2 2 2 3 4 4 7 Afisare descrescatoare 7 4 4 3 2 2 2

Pointeri şi gestiunea dinamică a memoriei

222

86. Următorul program citeşte un vector de n numere întregi (n citit de la tastatură) şi calculează valoarea SAU exclusiv între toate elementele vectorului. #include<stdio.h> #include<stdlib.h> #include<conio.h> int main() { int n, i, r = 0; int *vector; printf("Dimensiunea vectorului n:"); scanf("%i", &n); vector = (int*) malloc(sizeof(int) * n); for(i=0; i<n; ++i) { printf("Introduceti v[%d]:", i); scanf("%i", vector + i); } for(i=0; i<n; ++i) r ^= *(vector + i); printf("Rezultat: %d", r); free(vector); getch(); }

Rezultatul execuţiei programului este: Dimensiunea vectorului n:5 Introduceti v[0]:1 Introduceti v[1]:2 Introduceti v[2]:3 Introduceti v[3]:7 Introduceti v[4]:2 Rezultat: 5

8.8. Exerciţii 74. Scrieţi o funcţie care primeşte ca parametri două numere naturale m şi n şi citeşte de la tastatură o matrice

Page 114: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

223

de numere reale de dimensiune m x n. Memoria necesară pentru stocarea matricei trebuie alocată dinamic de către funcţie. Scrieţi un program C care testează funcţia. 75. Scrieţi o funcţie care permite adunarea a două matrice şi un program C care testează funcţia. Memoria necesară pentru stocarea rezultatului trebuie alocată dinamic de către funcţie. 76. Scrieţi o funcţie care permite înmulţirea a două matrice şi un program C care testează funcţia. Memoria necesară pentru stocarea rezultatului trebuie alocată dinamic de către funcţie. 77. Scrieţi o funcţie care primeşte ca parametru un pointer la caractere şi returnează numărul de caractere de la acea locaţie până la primul caracter NUL (cu codul ASCII 0). 78. Scrieţi o funcţie care primeşte ca parametru doi pointeri la caractere şi copiază caracter cu caracter, conţinutul zonei de memorie indicate de primul pointer în zona de memorie indicată de al doilea pointer. Copierea se opreşte după primul caracter NUL (cu codul ASCII 0).

Page 115: Bazele_programarii_calculatoarelor

225

999... FFFUUUNNNCCCŢŢŢIIIIII PPPEEENNNTTTRRRUUU ŞŞŞIIIRRRUUURRRIII DDDEEE CCCAAARRRAAACCCTTTEEERRREEE

9.1. Şiruri de caractere În limbajul C, un şir de caractere este de fapt un

vector de caractere, ultimul caracter fiind caracterul NUL, care are codul ASCII 0. În general, şirurile de caractere se scriu între ghilimele sub formă de secvenţe de caractere normale şi secvenţe escape (e.g. "acesta este un sir"). Tipul şirurilor de caractere este char[], adică vector de caractere, dar având în vedere relaţia între tablouri şi pointer, putem scrie char*, adică pointer la date de tip caracter. De exemplu, conform tabelului cu codurile ASCII din Anexa 3 – Setul de caractere ASCII, şirul de caractere "Limbajul C" este stocat în memoria calculatorului astfel: 76 105 109 98 97 106 117 108 32 67 0

Acest lucru poate fi verificat uşor cu următorul program: #include <stdio.h> #include <conio.h> int main() { int i; char sir[] = "Limbajul C"; for(i=0; i<=10; ++i)

Funcţii pentru şiruri de caractere

226

printf("%d ", sir[i]); getch(); }

9.2. Funcţii pentru şiruri de caractere Dacă dorim ca şirurile de caractere pe care le

afişăm să fie create dinamic, în funcţie de anumite condiţii, sau să fie preluate de la utilizator, sau să efectuăm anumite operaţii cu ele, atunci trebuie să folosim funcţii specializate pentru aceste lucruri.

Funcţiile standard pentru prelucrarea şirurilor de caractere se găsesc în biblioteca string.h. În prototipurile acestor funcţii, parametrul c de tip int reprezintă de fapt un caracter (sau un octet).

Tabel 9.1 Funcţii standard pentru şiruri de caractere

Prototip unsigned strlen (char *sir)

Descriere Efect: calculează lungimea şirului de caractere sir Rezultat: lungimea şirului de caractere sir

Prototip char* strlwr (char *sir)

Descriere Efect: transformă majusculele din şirul sir în minuscule Rezultat: adresa şirului modificat (adică sir)

Prototip char* strupr (char *sir)

Descriere Efect: transformă minusculele din şirul sir în majuscule Rezultat: adresa şirului modificat (adică sir)

Prototip char* strcat (char *dest, char

Page 116: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

227

*src)

Descriere Efect: adaugă şirul src la sfârşitul şirului dest Rezultat: adresa şirului modificat (adică dest)

Prototip char* strncat (char *dest, char

*src, unsigned cnt)

Descriere Efect: adaugă cel mult cnt caractere din şirul src la sfârşitul şirului dest Rezultat: adresa şirului modificat (adică dest)

Prototip char* strchr (char *src, int c)

Descriere Efect: caută prima apariţie în şirul src a caracterului c. Rezultat: adresa caracterului c sau NULL dacă c nu a fost găsit

Prototip char* strrchr (char *src, int c)

Descriere Efect: caută ultima apariţie în şirul src a caracterului c. Rezultat: adresa caracterului c sau NULL dacă c nu a fost găsit

Prototip char* strstr (char *src, char *sub)

Descriere Efect: caută prima apariţie în şirul src a subşirului sub. Rezultat: adresa şirului sub în src sau NULL dacă sub nu a fost găsit

Prototip char* strrev (char *sir)

Descriere Efect: inversează şirul (va fi scris de la coada la cap)

Funcţii pentru şiruri de caractere

228

Rezultat: adresa şirului modificat (adică sir) Prototip char* strcpy (char *dest, char

*src)

Descriere Efect: copiază şirul src în şirul dest Rezultat: adresa şirului modificat (adică dest)

Prototip char* strncpy (char *dest, char

*src, unsigned cnt)

Descriere Efect: copiază cel mult cnt caractere din şirul src în şirul dest (numărul de caractere copiate este minimul dintre cnt şi lungimea şirului src). Rezultat: adresa şirului modificat (adică dest)

Prototip char* strdup (char *src)

Descriere Efect: copiază şirul src într-o zonă de memorie alocată cu malloc; programatorul trebuie să elibereze cu funcţia free zona de memorie când nu o mai foloseşte. Rezultat: adresa zonei de memorie alocată

Prototip char* strset (char *dest, int c)

Descriere Efect: scrie caracterul c în fiecare poziţie din şirul dest. Rezultat: adresa şirului modificat (adică dest)

Prototip char* strnset (char *dest, int c,

unsigned cnt)

Descriere Efect: scrie caracterul c în primele cnt poziţii din şirul dest.

Page 117: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

229

Rezultat: adresa şirului modificat (adică dest)

Prototip char* strcmp (char *src1, char

*src2)

Descriere Efect: compară caracterele din şirurile src1 şi src2 Rezultat: valoarea 0 dacă şirurile src1 şi src2 sunt identice; o valoare negativă dacă primul caracter diferit din src1 este mai mic decât caracterul corespunzător din src2; o valoare pozitivă dacă primul caractere diferit din src1 este mai mare decât caracterul corespunzător din src2.

Prototip char* stricmp (char *src1, char

*src2)

Descriere Efect: compară caracterele din şirurile src1 şi src2, fără a face distincţie între literele mari şi mici. Rezultat: valoarea 0 dacă şirurile src1 şi src2 sunt la fel; o valoare negativă dacă primul caracter diferit din src1 este mai mic decât caracterul corespunzător din src2; o valoare pozitivă dacă primul caractere diferit din src1 este mai mare decât caracterul corespunzător din src2.

Prototip char* strncmp (char *src1, char

*src2, unsigned cnt)

Descriere Efect: compară cel mult cnt caractere din şirurile src1 şi src2

Funcţii pentru şiruri de caractere

230

Rezultat: valoarea 0 dacă şirurile src1 şi src2 sunt identice în zona comparată; o valoare negativă dacă primul caracter diferit din src1 este mai mic decât caracterul corespunzător din src2; o valoare pozitivă dacă primul caractere diferit din src1 este mai mare decât caracterul corespunzător din src2.

Prototip char* strnicmp (char *src1, char

*src2, unsigned cnt)

Descriere Efect: compară cel mult cnt caractere din şirurile src1 şi src2, fără a face distincţie între literele mari şi mici. Rezultat: valoarea 0 dacă şirurile src1 şi src2 sunt la fel în zona comparată; o valoare negativă dacă primul caracter diferit din src1 este mai mic decât caracterul corespunzător din src2; o valoare pozitivă dacă primul caractere diferit din src1 este mai mare decât caracterul corespunzător din src2.

Secvenţa următoare de program exemplifică utilizarea funcţiilor pentru şiruri de caractere prezentate anterior: char s1[] = "Limba", s2="Limbajul C", *s3; int n; n = strlen(s1); // n este 5 n = strcmp(s1, s2); // n este -1 strlwr(s1); // s1 este "limba" n = strcmp(s1, s2); // n este 1 strupr(s1); // s1 este "LIMBA" n = strnicmp(s1, s2, 5); // n este 0

Page 118: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

231

s3 = strstr(s2, " "); // s3 este subsirul " C" din s2 strcat(s1, s3); // s1 este "LIMBA C" strnset(s1, '#'); // s1 este "#######" strcpy(s3, s1); // s3 este "#######", deci // s2 este "Limbajul#######"

9.3. Funcţii pentru clasificarea caracterelor În afară de funcţiile pentru şiruri de caractere,

biblioteca standard ctype.h oferă diverse funcţii pentru testarea categoriei unui caracter (dacă e majusculă, minusculă, cifră, etc.) şi eventual transformarea acestuia (din majuscule în minuscule şi invers). Aceste funcţii primesc ca unic parametru caracterul care trebuie testat sau modificat.

Tabel 9.2 Funcţii standard pentru clasificarea caracterelor

Prototip int isalpha (int c)

Descriere Efect: testează dacă c este o literă Rezultat: 1 dacă c este literă, 0 altfel

Prototip int islower (int c)

Descriere Efect: testează dacă c este o minusculă Rezultat: 1 dacă c este minusculă, 0 altfel

Prototip int isupper (int c)

Descriere Efect: testează dacă c este o majusculă Rezultat: 1 dacă c este majusculă, 0 altfel

Prototip int isdigit (int c)

Descriere Efect: testează dacă c este o cifră Rezultat: 1 dacă c este cifră, 0 altfel

Prototip int isxdigit (int c)

Funcţii pentru şiruri de caractere

232

Descriere Efect: testează dacă c este o cifră hexazecimală Rezultat: 1 dacă c este hexazecimală, 0 altfel

Prototip int isalnum (int c)

Descriere Efect: testează dacă c este literă sau cifră (echivalent cu expresia isalpha(c) || isdigit(c)) Rezultat: 1 dacă c este literă sau cifră, 0 altfel

Prototip int isblank (int c)

Descriere Efect: testează dacă c este caracter gol (spaţiu sau tab) Rezultat: 1 dacă c este caracter gol, 0 altfel

Prototip int isspace (int c)

Descriere Efect: testează dacă c este caracter de spaţiere (spaţiu, tab, new line, vertical tab, form feed sau carriage return) Rezultat: 1 dacă c este caracter de spaţiere, 0 altfel

Prototip int iscntrl (int c)

Descriere Efect: testează dacă c este caracter de control (adică are codul ASCII între 0 şi 31) Rezultat: 1 dacă c este caracter de control, 0 altfel

Prototip int ispunct (int c)

Descriere Efect: testează dacă c este caracter de punctuaţie Rezultat: 1 dacă c este caracter de

Page 119: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

233

punctuaţie, 0 altfel Prototip int isgraph (int c)

Descriere Efect: testează dacă c are o reprezentare grafică (adică este caracter tipăribil vizibil). Rezultat: 1 dacă c are reprezentare grafică, 0 altfel

Prototip int isprint (int c)

Descriere Efect: testează dacă c este un caracter afişabil Rezultat: 1 dacă c este caracter afişabil, 0 altfel

Prototip int tolower (int c)

Descriere Efect: testează dacă c este majusculă şi returnează minuscula corespunzătoare Rezultat: minuscula corespunzătoare lui c, dacă c este majusculă, sau valoarea lui c, altfel

Prototip int toupper (int c)

Descriere Efect: testează dacă c este minusculă şi returnează majuscula corespunzătoare Rezultat: majuscula corespunzătoare lui c, dacă c este minusculă, sau valoarea lui c, altfel

9.4. Exemple 87. Programul următor citeşte un mesaj de la tastatură şi afişează un extras din acesta. Extrasul este specificat prin poziţia de început şi lungimea maximă admisă. #include <stdio.h>

Funcţii pentru şiruri de caractere

234

#include <string.h> #include <conio.h> int main() { char mesaj[256], extras[256]; int i, n, l; printf("Introduceti un text:\n"); gets(mesaj); printf("Pozitie de start: "); scanf("%i", &n); printf("Lungime: "); scanf("%i", &l); i = 0; do { extras[i] = mesaj[i+n]; ++i; } while(extras[i-1] != 0 && i<l); printf("Extras: %s", extras); getch(); }

Introduceti un text: Limbajul C are doar 32 de instructiuni! Pozitie de start: 9 Lungime: 10 Extras: C are doar

88. Programul următor citeşte un mesaj de la tastatură şi afişează mesajul în oglindă (de la dreapta la stânga). #include <stdio.h> #include <conio.h> #include <string.h> int main() { char mesaj[256]; int i, len; printf("Introduceti un text:\n"); gets(mesaj); len = strlen(mesaj); for(i = 0; i<len/2; ++i) { char temp = mesaj[i];

Page 120: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

235

mesaj[i] = mesaj[len - i - 1]; mesaj[len - i - 1] = temp; } printf("Rezultat:\n%s", mesaj); getch(); }

Introduceti un text: Limbajul C Rezultat: C lujabmiL

89. Programul următor citeşte un mesaj de la tastatură şi un număr întreg n şi afişează mesajul codificat prin înlocuirea fiecărui caracter cu caracterul aflat la distanţă n în tabela codurilor ASCII. #include <stdio.h> #include <string.h> #include <conio.h> int main() { char mesaj[256]; int i, len, n; printf("Introduceti un text:\n"); gets(mesaj); printf("Introduceti n:"); scanf("%d", &n); len = strlen(mesaj); for(i = 0; i<len; ++i) mesaj[i] += n; printf("Rezultat:\n%s", mesaj); getch(); }

Introduceti un text: Limbajul C Introduceti n:1 Rezultat: Mjncbkvm!D

De remarcat, că un mesaj codificat cu valoarea n poate fi decodificat cu valoarea -n.

Funcţii pentru şiruri de caractere

236

Introduceti un text: Mjncbkvm!D Introduceti n:-1 Rezultat: Limbajul C

90. Programul următor verifică dacă un şir este palindrom (adică citit de la dreapta la stânga şi de la stânga la dreapta obţinem acelaşi cuvânt). #include <stdio.h> #include <string.h> #include <conio.h> int main() { char mesaj[256]; int i, len, pal = 1; printf("Introduceti un text:\n"); gets(mesaj); len = strlen(mesaj); for(i = 0; i<len/2; ++i) if(mesaj[i] != mesaj[len - i - 1]) pal = 0; if(pal == 1) printf("Sirul este palindron."); else printf("Sirul nu este palindrom."); getch(); }

Introduceti un text: capac Sirul este palindrom.

91. Programul următor verifică dacă un şir este palindrom fără a face diferenţe între majuscule şi minuscule, folosind funcţiile bibliotecii string.h. #include <stdio.h> #include <string.h> #include <conio.h> int main() { char mesaj[256], oglinda[256];

Page 121: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

237

int i, len; printf("Introduceti un text:\n"); gets(mesaj); strcpy(oglinda, mesaj); strrev(oglinda); if(stricmp(oglinda, mesaj) == 0) printf("Sirul este palindron."); else printf("Sirul nu este palindron."); getch(); }

92. Programul următor citeşte un mesaj de la tastatură şi elimină din acesta prima apariţie a unui subşir citit tot de la tastatură. #include <stdio.h> #include <string.h> #include <conio.h> int main() { char mesaj[256], subsir[50], *inceput; int i, len; printf("Introduceti un text:\n"); gets(mesaj); printf("Introduceti subsirul:\n"); gets(subsir); inceput = strstr(mesaj, subsir); if(inceput != NULL) { len = strlen(subsir); strcpy(inceput, inceput+len); } printf("Rezultat:\n%s", mesaj); getch(); }

Introduceti un text: Limbajul C are doar 32 de instructiuni! Introduceti subsirul: doar 32 de Rezultat: Limbajul C are instructiuni!

Funcţii pentru şiruri de caractere

238

93. Programul următor afişează cuvintelor dintr-un şir de caractere câte unul pe linie. În acest context, un cuvânt este un şir de caractere cuprins între doi separatori. Prin separator se înţelege aici, orice caracter. Programul primeşte la intrare un şir de caractere, o listă de separatori şi afişează cuvintele din şirul introdus. #include <stdio.h> #include <string.h> #include <conio.h> int main() { char sir[100], sep[10], *pozitie; printf("Introduceti textul"); gets(sir); printf("Introduceti separatorii"); gets(sep); printf(" Cuvintele din text sunt: \n"); pozitie=strtok(sir, sep); while(pozitie) { printf("%s\n", pozitie); pozitie=strtok(NULL,sep); } getch(); }

Un exemplu de rulare este: Introduceti textul: Limbajul C are doar 32 de instructiuni! Introduceti separatorii: a d Cuvintele din text sunt: Limb jul C re o r 32 e

Page 122: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

239

instructiuni!

94. Programul următor citeşte un mesaj de la tastatură şi afişează statistici despre acesta (lungimea totală, numărul de majuscule, minuscule, cifre, semne de punctuaţie şi caractere de spaţiere): #include <stdio.h> #include <string.h> #include <ctype.h> #include <conio.h> int main() { char mesaj[256]; int i, len, maj, min, cif, pct, spa; printf("Introduceti un text:\n"); gets(mesaj); len = strlen(mesaj); maj = min = cif = pct = spa = 0; for(i = 0; i<len; ++i) if(islower(mesaj[i])) ++maj; else if(isupper(mesaj[i])) ++min; else if(isdigit(mesaj[i])) ++cif; else if(ispunct(mesaj[i])) ++pct; else if(isspace(mesaj[i])) ++spa; printf("Lungime: %d\n", len); printf("Majuscule: %d\n", maj); printf("Minuscule: %d\n", min); printf("Cifre: %d\n", cif); printf("Semne de punctuatie: %d\n", pct); printf("Spatii: %d\n", spa); getch(); }

Introduceti un text: Limbajul C are doar 32 de instructiuni! Lungime: 39 Majuscule: 28 Minuscule: 2 Cifre: 2 Semne de punctuatie: 1 Spatii: 6

Funcţii pentru şiruri de caractere

240

95. Programul următor transformă un şir de caractere citit de la tastatură astfel încât fiecare cuvânt să înceapă cu o majusculă şi să conţină în rest doar minuscule. Variabila cuvantnou este folosită pentru afla dacă prelucrăm caracterele din interiorul unui cuvânt, sau urmează să încep un cuvânt nou. #include <stdio.h> #include <string.h> #include <ctype.h> #include <conio.h> int main() { char mesaj[256]; int i, len, cuvantnou = 1; printf("Introduceti un text:\n"); gets(mesaj); len = strlen(mesaj); for(i = 0; i<len; ++i) if(isalpha(mesaj[i])) if(cuvantnou) { mesaj[i] = toupper(mesaj[i]); cuvantnou = 0; }else { mesaj[i] = tolower(mesaj[i]); } else cuvantnou = 1; printf("Rezultat:\n%s", mesaj); getch(); }

Introduceti un text: limbajul c are doar 32 de instructiuni! Rezultat: Limbajul C Are Doar 32 De Instructiuni!

96. Funcţie pentru înlocuirea unui caracter c dintr-un şir de caractere, cu un alt caracter. Funcţia înlocuieşte toate

Page 123: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

241

apariţiile caracterului c în şir. Dacă c nu a fost găsit se emite un mesaj. / Inlocuire caracter in sir de caractere #include "stdio.h" #include "conio.h" #include "string.h" int inlocuire(char *x, char cs, char cd) { int i,k,gasit; i=0; gasit=0; for(i=0;i<strlen(x);i++) { if(*(x+i)==cs) { gasit=1; *(x+i)=cd; } } if(gasit) return 1; else return 0; } int main() { char sir[200],cars,card; puts(" Sirul "); gets(sir); puts(" Caracterul de inlocuit "); cars=getche(); puts("\n Caracterul cu care inlocuiesc "); card=getche(); if(inlocuire(sir,cars,card)) { puts("\n Sirul modificat "); puts(sir); } else printf("\n Caracterul %c nu a fost gasit in sirul %s \n",cars,sir); getch();

Funcţii pentru şiruri de caractere

242

}

Un exemplu de rulare este următorul: Sirul inlocuire caracter Caracterul de inlocuit c Caracterul cu care inlocuiesc ? Sirul modificat inlo?uire ?ara?ter Sirul abac Caracterul de inlocuit ! Caracterul cu care inlocuiesc ? Caracterul ! nu a fost gasit in sirul abac

97. Funcţie pentru inserarea unui şir sursă într-un şir destinaţie după caracterul aflat pe poziţia n în şirul destinaţie. Dacă n=-1 se inserează şirul sursă în faţa şirului destinaţie. Dacă n este egal cu lungimea şirului destinaţie minus 1 se obţine concatenarea celor două şiruri. //Inserare sir sursa in sir destinatie la pozitia n // Pentru n=-1 se insereaza in fata destinatiei // Pentru n=(lungimea destinatiei - 1) se obtine concatenare #include "stdio.h" #include "conio.h" void insert(char *d, char *s, int n) { int is,id,im; char m[100]; im=0; id=n+1; while(*(d+id)!='\0')

Page 124: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

243

{ m[im]=*(d+id); id++; im++; } id=n+1; m[im]='\0'; is=0; while(*(s+is)!='\0') { *(d+id)=*(s+is); id++; is++; } im=0; while(*(m+im)!='\0') { *(d+id)=*(m+im); id++; im++; } *(d+id)='\0'; } int main() { char sird[100],sirs[100]; int n; puts(" Sirul sursa "); gets(sirs); puts(" Sirul destinatie "); gets(sird); puts(" Pozitia de inceput a inserarii "); scanf("%d",&n); insert(sird,sirs,n); puts(sird); getch(); }

Un exemplu de rulare este: Sirul sursa sir sursa Sirul destinatie sir destinatie

Funcţii pentru şiruri de caractere

244

Pozitia de inceput a inserarii -1 sir sursa sir destinatie Sirul sursa sir sursa Sirul destinatie sir destinatie Pozitia de inceput a inserarii 3 sir sir sursa destinatie Sirul sursa sir sursa Sirul destinatie sir destinatie Pozitia de inceput a inserarii 14 sir destinatie sir sursa

98. Funcţie pentru ştergerea tuturor apariţiilor unui caracter dintr-un şir de caractere. Dacă şirul nu conţine caracterul respectiv se emite un mesaj corespunzător. // Stergere caracter #include "stdio.h" #include "conio.h" #include "string.h" int sterg(char *x, char c) { int i,k,j,este; i=0;este=0; while(i<strlen(x)) { j=i; while(*(x+j)==c) { este=1; for(k=j;*(x+k)!='\0';k++) *(x+k)=*(x+k+1); *(x+k+1)='\0'; } i++;

Page 125: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

245

} return este; } int main() { char sir[200],sirm[200],car; puts(" Sirul "); gets(sir); strcpy(sirm,sir); puts(" Caracterul care va fi sters "); scanf("%c",&car); if(sterg(sir,car)) { printf("Caracterul %c a fost garsit in sirul %s \n", car,sirm); puts(" Sirul modificat "); puts(sir); } else printf(" Caracterul %c nu a fost gasit in sirul %s \n",car,sir); getch(); }

Un exemplu de rulare este: Sirul stergere caracter Caracterul care va fi sters c Caracterul c a fost garsit in sirul stergere caracter Sirul modificat stergere arater Sirul stergere caracter Caracterul care va fi sters # Caracterul # nu a fost gasit in sirul stergere caracter

Funcţii pentru şiruri de caractere

246

99. Programul următor transformă toate minusculele unui şir de caractere în majuscule şi apoi, toate majusculele în minuscule. // Minuscule majuscule #include "stdio.h" #include "conio.h" #include "ctype.h" void schimb_m_M(char x[])// minuscule in majuscule { int i; i=0; while(x[i]!='\0') { x[i]=toupper(x[i]); i++; } } void schimb_M_m(char x[])// majuscule in minuscule { int i; i=0; while(x[i]!='\0') { x[i]=tolower(x[i]); i++; } } int main() { char s[100]; puts(" Tastati un sir de caractere "); gets(s); schimb_m_M(s); printf(" Transformarea tuturor minusculelor in majuscule\n"); puts(" Sirul modificat este "); puts(s); getch(); schimb_M_m(s);

Page 126: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

247

printf(" Transformarea tuturor majusculelor in minuscule\n"); puts(" Sirul modificat este "); puts(s); getch(); }

Un exemplu de rulare este: Tastati un sir de caractere TransFormare minuscule - MAJUSCULE Transformarea tuturor minusculelor in majuscule Sirul modificat este TRANSFORMARE MINUSCULE - MAJUSCULE Transformarea tuturor majusculelor in minuscule Sirul modificat este transformare minuscule - majuscule

100. Programul următor transformă un şir de caractere citit de la tastatură astfel încât primul cuvânt din fiecare frază să înceapă cu majusculă, restul literelor fiind minuscule. Variabila frazanoua este folosită pentru afla dacă prelucrăm caracterele din interiorul unei fraze, sau urmează să începem o frază nouă. #include <stdio.h> #include <string.h> #include <ctype.h> #include <conio.h> int main() { char mesaj[256]; int i, len, frazanoua = 1; printf("Introduceti un text:\n"); gets(mesaj); len = strlen(mesaj); for(i = 0; i<len; ++i) if(isalpha(mesaj[i])) if(frazanoua) { mesaj[i] = toupper(mesaj[i]);

Funcţii pentru şiruri de caractere

248

frazanoua = 0; }else { mesaj[i] = tolower(mesaj[i]); } else if(ispunct(mesaj[i])) frazanoua = 1; printf("Rezultat:\n%s", mesaj); getch(); }

Introduceti un text: limbajul c are putine instructiuni. asta e bine. Rezultat: Limbajul c are putine instructiuni. Asta e bine.

9.5. Exerciţii 79. Scrieţi un program C care citeşte de la tastatură un şir de caractere A şi un caracter B, şi afişează numărul de apariţii ale caracterului B în şirul A. 80. Scrieţi un program C care citeşte de la tastatură două şiruri de caractere A şi B, şi afişează numărul de apariţii ale şirului B în şirul A. 81. Scrieţi un program C care citeşte de la tastatură două şiruri de caractere A şi B, şi un număr natural n, şi afişează pe ecran şirul A în care a fost inserat şirul B la poziţia n. 82. Scrieţi un program C care citeşte de la tastatură şiruri de caractere şi afişează pe ecran lungimea şi şirul respectiv. Citirea se opreşte la introducerea şirului vid (cu lungime 0) de către utilizator. 83. Scrieţi un program C care citeşte de la tastatură şiruri de caractere şi le afişează pe ecran înlocuind majusculele cu minuscule. Citirea se opreşte la introducerea şirului vid (cu lungime 0) de către utilizator.

Page 127: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

249

84. Scrieţi un program C care citeşte o succesiune de cuvinte. Citirea se opreşte la introducerea şirului vid (cu lungime 0) de către utilizator. Programul afişează apoi cuvântul cel mai lung. 85. Scrieţi un program C care citeşte o succesiune de cuvinte. Citirea se opreşte la introducerea şirului vid (cu lungime 0) de către utilizator. Programul afişează apoi cuvântul cel mai mare din punct de vedere lexicografic (de exemplu, "casa" este mai mare din punct de vedere lexicografic decât "dar"). 86. Scrieţi un program C care citeşte o succesiune de cuvinte, le sortează în ordine crescătoare şi apoi le afişează în această ordine.

Page 128: Bazele_programarii_calculatoarelor

251

111000... FFFUUUNNNCCCŢŢŢIIIIII MMMAAATTTEEEMMMAAATTTIIICCCEEE

Biblioteca limbajului C pune la dispoziţia utilizatorului un număr de funcţii matematice. Aceste funcţii au prototipurile în fişierul antet math.h, stdlib.h şi/sau complex.h. Orice program care foloseşte astfel de funcţii trebuie să conţină directivele compilator: #include <math.h> #include <stdlib.h> #include <complex.h>

Împreună cu aceste funcţii, sunt disponibile valorile unor constante remarcabile din matematică, definite prin constante simbolice. Vom prezenta în continuare constantele simbolice şi cele mai utilizate dintre aceste funcţii.

Tabel 10.1 Constante simbolice

Nume Valoare Descriere M_E 2.7182818284590452354 e M_LOG2E 1.4426950408889634074 2log e M_LOG10E 0.43429448190325182765 lg e M_LN2 0.69314718055994530942 ln 2 M_LN10 2.30258509299404568402 ln10

Funcţii matematice

252

M_PI 3.14159265358979323846 π M_PI_2 1.57079632679489661923

M_PI_4 0.78539816339744830962 4π

M_1_PI 0.31830988618379067154 1π

M_2_PI 0.63661977236758134308 2π

M_2_SQRTPI 1.12837916709551257390 2π

M_SQRT2 1.41421356237309504880 2 M_SQRT1_2 0.70710678118654752440 1

2

Tabel 10.2 Funcţii trigonometrice

Prototip double sin (double x)

Descriere Returnează ( )sin x în [ ]1,1−

Prototip double cos (double x)

Descriere Returnează ( )cos x în [ ]1,1−

Prototip double asin (double x)

Descriere Returnează ( )arcsin x în ,2 2π π −

.

Argumentul [ ]1,1x ∈ −

Prototip double acos (double x)

Descriere Returnează ( )arccos x în ( )0,π . Argumentul

[ ]1,1x ∈ −

Prototip double tan (double x)

Page 129: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

253

Descriere Returnează ( )tg x în R , ( )2 12

x k π≠ + ,

( )k Z∀ ∈

Prototip double atan (double x)

Descriere Returnează ( )arctg x în ,2 2π π −

Prototip double atan2 (double x, double y)

Descriere Returnează xarctg

y

în ,2 2π π −

Tabel 10.3 Funcţii putere şi radical

Prototip double pow (double x, double y)

Descriere Returnează yx , 0x > Prototip double sqrt (double x)

Descriere Returnează x , 0x ≥ Tabel 10.4 Funcţii exponenţiale, logaritmice,

hiperbolice

Prototip double exp (double x)

Descriere Returnează xe Prototip double log (double x)

Descriere Returnează ln x , 0x > Prototip double log10 (double x)

Descriere Returnează lg x , 0x > Prototip double log2 (double x)

Descriere Returnează 2l go x , 0x > Prototip double sinh (double x)

Descriere Returnează sh( )2

x xe ex−−=

Funcţii matematice

254

Prototip double cosh (double x)

Descriere Returnează ch( )

2

x xe ex−+=

Prototip double tanh (double x)

Descriere Returnează ( )th( )( )

sh xxch x

=

Prototip double sinh (double x)

Descriere Returnează inversa funcţiei sh( )

2

x xe ex−−=

Prototip double cosh (double x)

Descriere Returnează inversa funcţiei ch( )

2

x xe ex−+=

Prototip double tanh (double x)

Descriere Returnează inversa funcţiei ( )th( )( )

sh xxch x

=

Tabel 10.5 Funcţii de conversie

Prototip int atoi (const char* c)

Descriere Conversie şir de caractere în întreg Prototip double atof (const char* c)

Descriere Conversie şir de caractere în double Prototip long atoll (const char* c)

Descriere Conversie şir de caractere în întreg lung Tabel 10.6 Funcţii de rotunjire, trunchiere,

modul

Prototip double floor (double x)

Descriere Returnează cel mai mare întreg mai mic decât x , x

Prototip double ceil (double x)

Page 130: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

255

Descriere Returnează cel mai mic întreg mai mare decât x , x

Prototip double round (double x)

Descriere Funcţie de rotunjire a lui x Prototip double trunc (double x)

Descriere Returnează trunchierea lui x Prototip int abs (int x)

Descriere Returnează x , x Z∈

Prototip double fabs (double x)

Descriere Returnează x , x R∈

Tabel 10.7 Funcţii pentru generare numere aleatoare

Prototip int rand ()

Descriere Returnează la fiecare apel un număr natural aleatoriu

Prototip void srand (unsigned s)

Descriere Iniţializează valoarea de start a secvenţei de numere aleatoare produse cu rand.

10.1. Exemple 101. Programul următor calculează valorile unei funcţii reale de o singură variabilă reală, într-un interval dat. Fie funcţia :f R R→ , definită astfel:

( )( )

( ) [ ]( ) ( ) ( )

21

23

, , 1

1ln 1 1,1

sin * 1,

xxe x

f x x x x

x sh x x

− ∈ −∞ −= + + ∈ −

∈ ∞

Funcţii matematice

256

Vom scrie programul care calculează valorile funcţiei f în 2n + puncte din intervalul [ ],a b . Folosind expresia analitică a funcţiei f definim funcţia f în limbajul C astfel: double f(double x) { if(x<-1) return x*exp(1-pow(x,2)); if(x>=-1 && x<=1) return pow(x+1,1.0/3)* log(1+pow(x,2)); if(x>1) return sin(x)*sinh(x); }

Datele de intrare sunt extremităţile intervalului [ ],a b şi numărul n de puncte din intervalul ( ),a b în care

se calculează valorile funcţiei. Pentru lucrul cu o altă funcţie se modifică în mod corespunzător definiţia funcţiei f. #include <stdio.h> #include <math.h> #include <stdlib.h> #include <conio.h> double f(double x) { if(x<-1) return x*exp(1-pow(x,2)); if(x>=-1 && x<=1) return pow(x+1,1.0/3)* log(1+pow(x,2)); if(x>1) return sin(x)*sinh(x); } int main() { double a,b,h,x; int n,k; printf("Extremitatile intervalului [a,b] \n");

Page 131: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

257

printf(" a = "); scanf(“%lf”, &a); printf(" b = "); scanf(“%lf”, &b); printf("Numarul de puncte din intervalul (%f, %f) in care se calculeaza f(x)=", a, b); scanf("%d",&n); h=(b-a)/n; for(k=0,x=a; k<=n+1; x=a+k*h,k++) printf("%d. f ( %f ) = %f \n",k+1,x,f(x)); getch(); }

Ca exemplu am folosit intervalul [ ]2,2− şi 10n = .

Extremitatile intervalului [a,b] a = -2 b = 2 Numarul de puncte din intervalul (-2.000000, 2.000000) in care se calculeaza f(x) = 10 1. f ( -2.000000 ) = -0.099574 2. f ( -2.000000 ) = -0.099574 3. f ( -1.600000 ) = -0.336218 4. f ( -1.200000 ) = -0.772844 5. f ( -0.800000 ) = 0.289300 6. f ( -0.400000 ) = 0.125182 7. f ( 0.000000 ) = 0.000000 8. f ( 0.400000 ) = 0.166036 9. f ( 0.800000 ) = 0.601769 10. f ( 1.200000 ) = 1.406877 11. f ( 1.600000 ) = 2.374555 12. f ( 2.000000 ) = 3.297895

102. Program următor calculează valorile unei funcţii reale de două variabile reale, într-un domeniu dat. Fie funcţia 2:f R R→ , ( ) ( ),f x y arctg x y= + , ( ) 2,x y R∀ ∈ . Vom scrie programul care calculează valorile funcţiei f în domeniul [ ] [ ] 2, ,a b c d R× ⊂ . Folosind expresia analitică a funcţiei f definim funcţia f în limbajul C astfel: double f(double x, double y)

Funcţii matematice

258

{ return atan(x+y); }

Datele de intrare sunt extremităţile a,b,c,d ale domeniului, numărul n de puncte din intervalul ( ),a b şi numărul m de puncte din intervalul ( ),c d în care se

calculează valorile funcţiei. Pentru lucrul cu o altă funcţie se modifică în mod corespunzător definiţia funcţiei f. #include <stdio.h> #include <math.h> #include <stdlib.h> #include <conio.h> double f(double x, double y) { return atan(x+y); } int main() { double x,y,a,b,c,d,hx,hy; int n,m,k,p,nrp; printf("Extremitatile intervalului [a,b] \n"); printf("a = "); scanf(“%lf”, &a); printf("b = "); scanf(“%lf”, &b); printf("Extremitatile intervalului [c,d] \n"); printf("c = "); scanf(“%lf”, &c); printf("d = "); scanf(“%lf”, &d); printf("Numarul de puncte din intervalul (%f, %f) in care se calculeaza f(x,y) = ",a,b); scanf("%d",&n); printf("Numarul de puncte din intervalul (%f, %f) in care se calculeaza f(x,y) = ",c,d);

Page 132: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

259

scanf("%d",&m); hx=(b-a)/n; hy=(d-c)/m; for(k=0,x=a,nrp=1;k<=n;k++,x=a+k*hx) for(p=0,y=c;p<=m;p++,y=c+p*hy,nrp++) printf("%d. f ( %f, %f ) = %f\n", nrp, x, y, f(x,y)); getch(); }

Ca exemplu am folosit domeniul [ ] [ ]2,1 3,4− × − , 2n = şi 2m = .

Extremitatile intervalului [a,b] a = -2 b = 1 Extremitatile intervalului [c,d] c = -3 d = 4 Numarul de puncte din intervalul (-2.000000, 1.000000) in care se calculeaza f(x) = 2 Numarul de puncte din intervalul (-3.000000, 4.000000) in care se calculeaza f(x) = 2 1. f ( -2.000000, -3.000000 ) = -1.373401 2. f ( -2.000000, 0.500000 ) = -0.982794 3. f ( -2.000000, 4.000000 ) = 1.107149 4. f ( -0.500000, -3.000000 ) = -1.292497 5. f ( -0.500000, 0.500000 ) = 0.000000 6. f ( -0.500000, 4.000000 ) = 1.292497 7. f ( 1.000000, -3.000000 ) = -1.107149 8. f ( 1.000000, 0.500000 ) = 0.982794 9. f ( 1.000000, 4.000000 ) = 1.373401

103. Următorul program prezintă aproximarea soluţiilor ecuaţiilor neliniare cu metoda lui Newton. Fie

[ ]: ,f a b R→ , [ ]( )2 ,f C a b∈ , ( ) 0f x′ ≠ , ( ) ( ),x a b∀ ∈ şi

ecuaţia ( ) 0f x = , care are o singură rădăcină ξ în

intervalul [ ],a b . Metoda lui Newton (a tangentei) constă în construirea şirului de aproximaţii (şirul lui Newton):

Funcţii matematice

260

[ ]0 ,x a b∈ , care satisface condiţia Fourier,

( ) ( )0 0 0f x f x′′⋅ > , ( )( )1

nn n

n

f xx x

f x+ = −′

, ( )n N∀ ∈ . Acest şir

este convergent şi tinde la soluţia exactă ξ a ecuaţiei ( ) 0f x = . Dacă ε este pragul de eroare, generarea

termenilor şirului de aproximaţii se termină când 1n nx x ε+ − < . Ca exemplu am considerat funcţia

[ ]: 0,1f R→ , ( ) 3 3 1f x x x= − + şi ecuaţia ( ) 0f x = , care

are în intervalul [ ]0,1 o singură soluţie. Funcţiile f, fd,

fd2 codifică funcţiile ( ) ( ),f x f x′ şi respectiv ( )f x′′ . Funcţia recursivă x calculează termenii şirului aproximaţiilor. #include <stdio.h> #include <conio.h> #include <math.h> #include <stdlib.h> double f(double x) { return pow(x,3)-3*x+1; } double fd(double x) { return 3*pow(x,2)-3; } double fd2(double x) { return 6*x; } double x(int n, double p) { if(n==0) return p; else return x(n-1,p)-f(x(n-1,p))/fd(x(n-1,p)); }

Page 133: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

261

int main() { double p, e, eps, a, b; int n; printf("Intervalul [a,b]\n"); printf("a = "); scanf("%lf",&a); printf("b = "); scanf("%lf",&b); if(f(a)*fd2(a)>0) p=a; if(f(b)*fd2(b)>0) p=b; printf("Eroarea de aproximare eps : "); scanf("%d",&eps); e=eps+1; n=0; while(e>eps) { printf("x ( %d ) = %f\n",n,x(n,p)); e=fabs(x(n+1,p)-x(n,p)); n++; } printf("Numar total de iteratii = %d \n",n-1); printf("Solutia ecuatiei cu eroarea %f este %f\n", eps, x(n,p)); getch(); }

Rezultatele rulării programului sunt următoarele: Intervalul [a,b] a = 0 b = 1 Eroarea de aproximare eps : 0.00001 x ( 0 ) = 0.000000 x ( 1 ) = 0.333333 x ( 2 ) = 0.347222 x ( 3 ) = 0.347296 Numar total de iteratii = 3 Solutia ecuatiei cu eroarea 0.000010 este 0.347296

Funcţii matematice

262

104. Următorul program prezintă o metodă pentru

aproximarea integralei ( )b

a

f x dx∫ cu metoda Simpson

“1/3”. Fie [ ]: ,f a b R→ , continuă pe [ ],a b ,

[ ] { }0 1 2, , , , na b x x x∆ = L o diviziune echidistantă a intervalului

[ ],a b , 0 1 2... na x x x b= < < < = , 2

b ahn

−= , 0ix x ih= + ,

1,2,..., 2i n= . În aceste condiţii, se poate demonstra că are loc aproximarea

( ) ( ) ( ) ( ) ( )1

2 2 11 1

~ 2 43

b n n

k kk ka

hf x dx f a f b f x f x−

−= =

+ + +

∑ ∑∫

, cunoscută ca fiind formula lui Simpson “1/3”, care poate fi uşor programată. Datele de intrare sunt ,a b şi n . Programul furnizează valoarea aproximativă a integralei

( )b

a

f x dx∫ . Ca exemplu vom aproxima 1

20

11

dxx+∫ , care are

valoarea exactă M_PI_4, adică 0.78539816339744830962. Modificând în program definiţia funcţiei f, programul poate fi folosit pentru aproximarea oricărei integrale. #include <stdio.h> #include <conio.h> #include <math.h> #include <stdlib.h> double f(double x) { return 1/(x*x+1); } int main() {

Page 134: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

263

double a,b,h,xi,yi,sp,si,valint; int n, i; printf("Intervalul de integrare \n"); printf(" a = "); scanf(“%lf”, &a); printf(" b = "); scanf(“%lf”, &b); printf("Numarul nodurilor n = "); scanf(“%i”, &n); h=(b-a)/(2*n); for(i=0,xi=a,sp=0,si=0;i<=2*n;i++) { if(i%2==0) si=si+f(xi); else sp=sp+f(xi); xi=xi+h; } valint=(f(a)+f(b)+4*si+2*sp)*h/3; printf("Valoarea integralei este %f\n",valint); getch(); }

Rezultatul rulării programului este: Intervalul de integrare a = 0 b = 1 Numarul nodurilor n = 1000000 Valoarea integralei este 0.785399

105. Programul următor prezintă aproximarea valorilor soluţiei ecuaţiei diferenţiale ( ) ( )( ),y x f x y x′ = , [ ],x a b∈ ,

cu condiţia iniţială ( ) 0y a y= , cu metoda Euler. Fie ecuaţia

diferenţială ( ) ( )( ),y x f x y x′ = , [ ],x a b∈ , cu condiţia

iniţială ( ) 0y a y= . Fie [ ] { }0 1, , , ..., na b x x x∆ = o diviziune

echidistantă a intervalului [ ],a b , 0 1 ... na x x x b= < < < = ,

Funcţii matematice

264

b ahn−

= , 0ix x ih= + , ( )i iy y x= 1, 2,...,i n= . În aceste

condiţii, se poate demonstra că valorile funcţiei necunoscute y , în punctele diviziunii [ ],a b∆ , sunt date de

termenii şirului lui Euler: ( )0y y a= , ( )1 ,i i i iy y hf x y+ = + , 1, 2,...,i n= . Valorile aproximative sunt cu atât mai

apropiate de valorile exacte cu cat n este mai mare. Ca exemplu am considerat ecuaţia diferenţială

( ) ( )2 , 0 1y x xy y′ = = . În acest exemplu ( ), 2f x y xy= .

Această ecuaţie are soluţia exactă ( ) 2xy x e= . Programul listează, pentru comparaţie, valorile exacte ale soluţiei şi pe cele aproximative. Pentru calculul valorilor funcţiei

( ), 2f x y xy= , am definit în program funcţia f. Modificând corespunzător funcţia f, programul poate aproxima soluţia oricărei ecuaţii diferenţiale de acest tip. #include <stdio.h> #include <conio.h> #include <math.h> #include <stdlib.h> double f(double x,double y) { return 2*x*y; } int main() { double y0,a,b,h,xi,yi,ye; int n, i; printf(" Intervalul [a,b] \n"); printf(" a = "); scanf("%lf", &a); printf(" b = "); scanf("%lf", &b); printf(" Numarul de puncte n = "); scanf("%i", &n); printf(" Conditia initiala y0 = ");

Page 135: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

265

scanf("%lf", &y0); h=(b-a)/n; printf("Valoare aproximativa\tValoare exacta\n"); for(i=0,xi=a,yi=y0;i<=n;i++) { yi=yi+h*f(xi,yi); ye=exp(pow(xi,2.)); printf("y(%f) = %f\ty(%f) = %f \n", xi, yi, xi, ye); xi=xi+h; } getch(); }

Rezultatul rulării programului este: Intervalul [a,b] a = 0 b = 1 Numarul de puncte n = 10 Conditia initiala y0 = 1 Valoare aproximativa Valoare exacta y(0.000000) = 1.000000 y(0.000000) = 1.000000 y(0.100000) = 1.020000 y(0.100000) = 1.010050 y(0.200000) = 1.060800 y(0.200000) = 1.040811 y(0.300000) = 1.124448 y(0.300000) = 1.094174 y(0.400000) = 1.214404 y(0.400000) = 1.173511 y(0.500000) = 1.335844 y(0.500000) = 1.284025 y(0.600000) = 1.496146 y(0.600000) = 1.433329 y(0.700000) = 1.705606 y(0.700000) = 1.632316 y(0.800000) = 1.978503 y(0.800000) = 1.896481 y(0.900000) = 2.334633 y(0.900000) = 2.247908

Funcţii matematice

266

y(1.000000) = 2.801560 y(1.000000) = 2.718282

i Deoarece am introdus pentru n o valoare mică, aproximaţia este destul de slabă. Invităm cititorul să compare rezultatele pentru n>1000.

106. Scrieţi un program C care aproximează numărul e

folosind formula 0

1!

n

ni

ei=

= ∑ pentru un număr natural n.

#include <stdio.h> #include <math.h> #include <conio.h> int main() { int i; double s = 1, ifact= 1, err; printf("Introduceti eroarea minima: "); scanf("%lf", &err); printf("Calculat Corect Eroare\n"); for(i=1; fabs(s-M_E) > err; ++i) { ifact*= i; s += 1/ifact; printf("%f %f %f\n", s, M_E, fabs(s-M_E)); } printf("Minim de pasi: %d", i); getch(); }

Introduceti eroarea minima: 0.00001 Calculat Corect Eroare 2.000000 2.718282 0.718282 2.500000 2.718282 0.218282 2.666667 2.718282 0.051615 2.708333 2.718282 0.009948 2.716667 2.718282 0.001615 2.718056 2.718282 0.000226 2.718254 2.718282 0.000028 2.718279 2.718282 0.000003

Page 136: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

267

Minim de pasi: 9

10.2. Exerciţii 87. Scrieţi un program C care determină perimetrul şi aria unui triunghi cunoscând lungimile laturilor. 88. Scrieţi un program C care citeşte un unghi în grade sexagesimale şi calculează valoarea funcţiei sinus, cosinus şi tangentă. 89. Scrieţi un program C care determină valoarea minimă a numărului natural n astfel încât eroarea

numărului π aproximat cu formula 21

16n

ni i

π=

= ∑ să scadă

sub un anumit prag specificat de utilizator. 90. Scrieţi un program C care afişează valoarea funcţiei

1( ) sin( ) cos( )

n

if n i i

=

= +∑ , pentru un număr n citit de la

tastatură. 91. Scrieţi un program C care afişează valoarea funcţiei

1( )

ni

if n e i

=

= ∑ , pentru un număr n citit de la tastatură.

92. Scrieţi un program C care afişează valoarea funcţiei

1

sin( ) cos( )( )n

i

i

i if ni=

⋅= ∏ , pentru un număr n citit de la

tastatură. 93. Scrieţi un program C care afişează valoarea funcţiei

1 1

( ) ( 1)n i

j j

i jf n j

= =

= − ⋅∑∑ , pentru un număr n citit de la

tastatură. 94. Scrieţi un program C care determină dacă un număr natural n citit de la tastatură este număr narcisist. Un

Funcţii matematice

268

număr 2 1...kn a a a= este narcisist dacă 1

kki

in a

=

= ∑ (de

exemplu, 7, 153, 1634, 54748). 95. Scrieţi un program C care alege aleator un număr între 0 şi 9. Utilizatorul are trei încercări să ghicească acest număr. La fiecare tentativă, programul îl anunţă dacă valoarea aleasă este corectă, prea mică sau prea mare.

Page 137: Bazele_programarii_calculatoarelor

269

111111... FFFUUUNNNCCCŢŢŢIII III PPPEEENNNTTTRRRUUU GGGEEESSSTTTIIIUUUNNNEEEAAA FFFIIIŞŞŞIIIEEERRREEELLLOOORRR

Limbajul C nu are instrucţiuni definite special pentru operaţii de intrare/ieşire, dar pentru astfel de operaţii, există o gamă completă de funcţii oferite de biblioteca standard C. Limbajul C oferă două clase de funcţii pentru lucrul cu fişiere, denumite generic funcţii de nivel înalt şi funcţii de nivel scăzut. Totuşi, dintre acestea doar nivelul înalt este standardizat, fiind similar pe toate platformele. Funcţiile de nivel înalt pentru lucru cu fişiere sunt declarate în biblioteca standard stdio.h, o parte dintre ele fiind similare cu cele prezentate în capitolul 4).

11.1. Structura FILE Majoritatea funcţiilor standard pentru operaţii cu

fişiere necesită un parametru de tip pointer la o structură FILE. Tipul structură FILE este definit de asemenea în biblioteca standard stdio.h. Structura propriu-zisă conţine informaţii despre poziţia curentă de la care se citesc sau la care se scriu date în fişier, identificatorul fişierului, modul de acces, etc. Ea este creată automat la

Funcţii pentru gestiunea fişierelor

270

deschiderea fişierului şi este distrusă automat la închiderea acestuia.

Algoritmul general de lucru cu fişiere constă în deschiderea sau crearea fişierului, citirea sau scrierea datelor şi, în final, închiderea acestuia.

11.2. Fişiere standard de intrare/ieşire Când un program este pornit se deschid automat

trei fişiere (adică se creează structurile FILE corespunzătoare lor). Aceste fişiere sunt intrarea standard, ieşirea standard şi ieşirea standard pentru erori. Variabilele pointeri către structurile FILE ale acestor fişiere se numesc stdin, stdout şi stderr (definite in stdio.h).

11.3. Deschiderea şi închiderea fişierelor Înainte de a se citi sau scrie date într-un fişier,

acesta trebuie deschis cu funcţia fopen. În funcţie de modul de acces stabilit la deschidere, asupra fişierului se pot efectua doar operaţii de citire, doar operaţii se scriere, sau operaţii de citire şi scriere. După încheierea operaţiilor de citire şi/sau scriere a datelor, fişierul trebuie închis cu funcţia fclose.

Tabel 11.1 Funcţii standard pentru gestiunea dinamică a memoriei

Prototip FILE* fopen (char *name, char

*mode)

Descriere Efect: deschide fişierul name în modul de

Page 138: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

271

accesare mode. Rezultat: returnează un pointer la o structura FILE sau NULL în caz de eroare

Prototip int fclose ( FILE *fptr)

Descriere Efect: închide fişierul corespunzător lui fptr. Rezultat: 0 în caz de succes şi diferit de 0 altfel

Al doilea argument al funcţie fopen este un şir de caractere care determină modul de acces. Acesta indică cum va fi utilizat fişierul: citire ("r"), scriere("w") sau adăugare ("a"). În plus, există şi modurile extinse: citire cu posibilitatea de scriere ("r+"), scriere cu posibilitatea de citire ("w+") sau adăugare cu posibilitatea de citire ("a+").

Dacă se deschide un fişier care nu există pentru scriere ("w") sau adăugare ("a"), el este creat. Dacă se deschide un fişier existent pentru scriere ("w"), conţinutul său este şters. Încercarea de a deschide pentru citire un fişier care nu există produce o eroare.

11.4. Funcţii pentru caractere Funcţiile pentru caractere au o utilizare limitată,

fiind folosite pentru citirea şi scrierea informaţiei caracter cu caracter, fără nici o prelucrare în prealabil.

Tabel 11.2 Funcţii standard de intrare/ieşire la nivel de caracter

Prototip int fputc (int c, FILE * fptr)

Funcţii pentru gestiunea fişierelor

272

Descriere Efect: scrie caracterul cu codul ASCII c în fişierul fptr Rezultat: valoarea c sau EOF în caz de eroare

Prototip int fgetc (FILE * fptr)

Descriere Efect: citeşte din fişierul fptr un singur caracter Rezultat: codul ASCII al caracterului citit sau EOF dacă s-au terminat datele sau a apărut o eroare

De exemplu: FILE *fp = fopen("test.txt", "w"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); }else { int c = 'A'; fputc(c, fp); fclose(fp); }

Un alt exemplu: FILE *fp = fopen("test.txt", "r"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { int c=fgetc(fp); if(c==EOF) printf("Fisierul este gol.\n"); else printf("Am citit caracterul %c", c); fclose(fp); }

Page 139: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

273

11.5. Funcţii pentru şiruri de caractere Funcţiile pentru şiruri de caractere permit citirea

şi scrierea informaţiei sub formă de şir de caracter, fără alte prelucrări.

Tabel 11.3 Funcţii standard de intrare/ieşire pentru şiruri de caractere

Prototip int fputs (char *s, FILE *fptr)

Descriere Efect: scrie şirul s în fişierul fptr Rezultat: un număr nenegativ în caz de succes sau EOF în caz de eroare

Prototip char* fgets (char *s, int cnt, FILE

*fptr)

Descriere Efect: citeşte un şir de maximum cnt-1 caractere din fişierul fptr şi îl pune în memorie la adresa s Rezultat: adresa şirului sau NULL daca s-au terminat datele sau a apărut o eroare

Exemplul următor scrie un mesaj în fişierul test.txt. FILE *fp = fopen("test.txt", "w"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { char linie="Aceasta este o linie de text".; fputs(linie, fp); fclose(fp); }

Exemplul următor citeşte un mesaj din fişierul test.txt.

Funcţii pentru gestiunea fişierelor

274

FILE *fp = fopen("test.txt", "r"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { char linie[100]; fgets(linie, 100, fp); printf("Am citit linia: %s.\n", linie); fclose(fp); }

11.6. Funcţii cu formatare Funcţiile cu formatare permit citirea şi scrierea

informaţiei după o prelucrare în prealabil a acesteia. Prelucrarea se realizează conform codurilor de format descrise în secţiunea 5.3.

Tabel 11.4 Funcţii standard de intrare/ieşire cu formatare

Prototip int fprintf (FILE *fptr, char *fmt,

...);

Descriere Efect: scrie în fişierul fptr şirul fmt în care codurile de format sunt înlocuite cu valorile expresiilor marcate prin trei puncte; Rezultat: numărul de caractere scrise sau EOF în caz de eroare

Prototip int scanf (FILE *fptr, char *fmt,

...);

Descriere Efect: citeşte din fişierul fptr date conform şirului fmt şi le pune în memorie la adresele marcate prin trei puncte; Rezultat: numărul de coduri de format

Page 140: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

275

prelucrate corect sau EOF în caz de eroare De exemplu:

FILE *fp = fopen("test.txt", "w"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { fprintf(fp, "%d|%c|%f|%s", 3, 'A', 3.14, "Test"); fclose(fp); }

De exemplu: FILE *fp = fopen("test.txt", "r"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { char cuvant[100]; fscanf(fp, "%s", cuvant); printf("Am citit %s.\n", cuvant); fclose(fp); }

11.7. Funcţii pentru blocuri de date Funcţiile fread şi fwrite permit citirea şi scrierea

informaţiilor binare (adică non-textuale) dintr-un fişier. În acest caz, parametrul mod de acces al funcţiei fopen trebuie să conţină sufixul "b". Dacă sufixul lipseşte sau se foloseşte sufixul "t", modul implicit de lucru este cel textual (adică non-binar), caz în care informaţia poate fi transformată într-un mod nedorit în timpul operaţiilor de citire/scriere.

Funcţii pentru gestiunea fişierelor

276

Tabel 11.5 Funcţii standard de intrare/ieşire cu blocuri de date

Prototip unsigned fread (void *dest,

unsigned size, unsigned cnt, FILE

*fptr)

Descriere Efect: citeşte maxim cnt elemente de dimensiune size din fişierul fptr în zona de memorie dest. Rezultat: numărul de elemente citite complet

Prototip unsigned fwrite (void *src,

unsigned size, unsigned cnt, FILE

*fptr)

Descriere Efect: scrie maxim cnt elemente de dimensiune size în fişierul fptr din zona de memorie src. Rezultat: numărul de elemente scrise complet

De exemplu: FILE *fp = fopen("test.txt", "wb"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { int valori[] = {1, 2, 3, 5}; fwrite(valori, sizeof(int), 4, fp); fclose(fp); }

De exemplu: FILE *fp = fopen("test.txt", "rb"); if(fp == NULL) {

Page 141: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

277

printf("Eroare la deschiderea fisierului"); } else { int valori[20]; int citite = fread(valori, sizeof(int), 20, fp); printf("Am citit %d intregi.\n", citite); fclose(fp); }

11.8. Funcţii pentru acces aleator în fişiere În momentul deschiderii unui fişier în modul de

citire sau scriere, poziţia curentă la care se scriu date sau de la care se citesc date este începutul fişierului. Pentru fişierele deschise în modul de adăugare poziţia curentă la care se scriu date în fişier este sfârşitul fişierului. Ulterior, în urma operaţiilor de citire/ scriere, poziţia curentă avansează automat cu numărul de octeţi corespunzător.

Tabel 11.6 Funcţii standard de intrare/ieşire cu blocuri de date

Prototip long ftell ( FILE *fptr)

Descriere Rezultat: poziţia curentă în fişier sau EOF în caz de eroare

Prototip int fseek (FILE *fptr, long offset,

int origin)

Descriere Efect: mută poziţia curentă pentru fişierul fptr cu offset octeţi relativ la poziţia specificata de parametrul origin. Rezultat: numărul de elemente scrise complet

Funcţii pentru gestiunea fişierelor

278

Parametrul origin al funcţiei fseek poate fi una din constantele SEEK_CUR (denotă poziţia curentă), SEEK_END (denotă sfârşitul fişierului), SEEK_SET (denotă începutul fişierului).

De exemplu: FILE *fp = fopen("test.txt", "wb"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { fseek(fp, 0, SEEK_END); // la sfarsitul fisierului printf("Fisierul are %li octeti.\n", ftell(fp)); fseek(fp, 0, SEEK_SET); // la inceputul fisierului fclose(fp); }

11.9. Alte funcţii pentru fişiere şi directoare Folosirea acestor funcţii într-un program necesită

includerea bibliotecii direct.h. Tabel 11.7 Funcţii standard de intrare/ieşire cu

blocuri de date

Prototip int remove (char *nume)

Descriere Efect: şterge fişierul nume Rezultat: 0 în caz de succes sau diferit de zero altfel

Prototip int rename (char *vechi, char *nou)

Descriere Efect: redenumeşte fişierul vechi în nou Rezultat: 0 în caz de succes sau diferit de

Page 142: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

279

zero altfel Prototip int chdir (char *nume)

Descriere Efect: face nume directorul curent de lucru Rezultat: 0 în caz de succes sau diferit de zero altfel

Prototip char* getcwd (char *dest, int cnt)

Descriere Efect: copiază în zona de memorie dest (cu dimensiune maximă de cnt octeţi) calea directorului curent. Rezultat: dest în caz de succes şi NULL altfel

Prototip int mkdir (char *nume)

Descriere Efect: creează directorul nume Rezultat: 0 în caz de succes sau diferit de zero altfel

Prototip int rmdir (char *nume)

Descriere Efect: şterge directorul nume Rezultat: 0 în caz de succes sau diferit de zero altfel

11.10. Exemple 107. Programul următor copiază conţinutul unui fişier în alt fişier folosind funcţiile pentru blocuri de date. Numele fişierelor sunt citite de la tastatură. #include <stdio.h> #include <conio.h> int main() { char sursa[256], destinatie[256]; printf("Fisierul sursa: "); gets(sursa);

Funcţii pentru gestiunea fişierelor

280

printf("Fisierul destinatie: "); gets(destinatie); FILE *fpin = fopen(sursa, "rb"); FILE *fpout = fopen(destinatie, "wb"); if (fpin==NULL || fpout==NULL) { printf("Eroare la deschiderea fisierelor"); } else { char data[80]; int cnt; do { cnt=fread(data, sizeof(char), 80, fpin); fwrite(data, sizeof(char), cnt, fpout); } while(cnt > 0); } fclose(fpin); fclose(fpout); getch(); }

108. Programul următor redenumeşte un fişier sau un director al cărui nume a fost citit de la tastatură. #include <stdio.h> #include <conio.h> int main() { char numeVechi[256], numeNou[256]; printf("Numele vechi: "); gets(numeVechi); printf("Numele nou: "); gets(numeNou); if(rename(numeVechi, numeNou) != 0) printf("Redenumirea nu s-a efectuat"); else printf("Redenumirea s-a efectuat"); getch(); }

Page 143: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

281

109. Programul următor şterge un fişier al cărui nume a fost citit de la tastatură. #include <stdio.h> #include <conio.h> int main() { char nume[256]; printf("Numele: "); gets(nume); if(remove(nume) != 0) printf("Stergerea nu s-a efectuat"); else printf("Stergerea s-a efectuat"); getch(); }

110. Programul următor afişează conţinutul unui fişier al cărui nume este citit de la tastatură folosind funcţii pentru blocuri de date. #include <stdio.h> #include <conio.h> int main() { char nume[50]; printf("Nume fisier:"); gets(nume); FILE *fp = fopen(nume, "r"); if(fp == NULL) { printf("Eroare la deschiderea fisierului"); } else { char data[80]; int cnt; do { cnt=fread(data, sizeof(char), 80, fp); fwrite(data, sizeof(char), cnt, stdout);

Funcţii pentru gestiunea fişierelor

282

} while(cnt > 0); fclose(fp); } getch(); }

111. Programul următor exemplifică folosirea funcţiilor pentru operaţii cu foldere. Programul afişează folderul în care este pornit, schimbă folderul cu unul specificat de utilizator şi creează în acesta un fişier cu numele "test.txt". #include <stdio.h> #include <direct.h> #include <conio.h> int main() { char buffer[256]; getcwd(buffer, 256); printf("Folderul curent %s\n", buffer); printf("Noul folder: "); gets(buffer); chdir(buffer); getcwd(buffer, 256); printf("Acum suntem in folderul %s\n", buffer); fclose(fopen("test.txt", "w")); getch(); }

112. Programul următor creează un folder nou al cărui nume este introdus de utilizator. #include <stdio.h> #include <direct.h> #include <conio.h> int main() { printf("Folder nou: "); char nume[256]; gets(nume); if(mkdir(nume) != 0)

Page 144: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

283

printf("Directorul nu a fost creat."); else printf("Directorul a fost creat."); getch(); }

113. Programul următor şterge un folder al cărui nume este introdus de utilizator. Folderul trebuie să fie gol, adică să nu conţină fişiere sau alte foldere. #include <stdio.h> #include <direct.h> #include <conio.h> int main() { printf("Folder: "); char nume[256]; gets(nume); if(rmdir(nume) != 0) printf("Directorul nu a fost sters."); else printf("Directorul a fost sters."); getch(); }

114. Programul următor gestionează o agendă telefonică simplă. Programul oferă funcţii pentru creare, listare, căutare, adăugare, ştergere, sortare, salvare şi încărcare din fişier. #include <stdio.h> #include <conio.h> #include <string.h> #include <ctype.h> struct contact { char nume[50]; char adresa[100]; char telefon[15]; } agenda[100]; int intrari; char * numefisier = "agenda.dat";

Funcţii pentru gestiunea fişierelor

284

void incarcare(void) { puts("Incarcare agenda"); FILE *f = fopen(numefisier,"rb"); if(f == NULL) { printf("Fisierul %s lipseste. Incep o agenda noua.\n", numefisier); intrari = 0; } else { intrari = fread(agenda, sizeof(contact), 100, f); fclose(f); } } void salvare(void) { puts("Salvare agenda"); FILE *f = fopen(numefisier, "wb"); if(f == NULL) { printf("Fisierul %s nu poate fi modificat.\n", numefisier); } else { int i = fwrite(agenda, sizeof(contact), intrari, f); if(i != intrari) printf("Doar %d contacte din %d au fost salvate.", i, intrari); fclose(f); } } void afisarecontact(struct contact c) { printf("Nume: %s\n", c.nume); printf("Adresa: %s\n", c.adresa); printf("Telefon: %s\n", c.telefon); }

Page 145: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

285

void citirecontact(struct contact *c) { fflush(stdin); printf("Nume: "); gets(c->nume); printf("Adresa: "); gets(c->adresa); printf("Telefon: "); gets(c->telefon); } void cautarenume(void) { int i, gasite = 0; char date[50]; fflush(stdin); printf("Nume: "); gets(date); for(i = 0; i<intrari; ++i) if(stricmp(date, agenda[i].nume)==0) { printf("Intrarea %d:\n", i); afisarecontact(agenda[i]); ++gasite; } printf("Au fost gasite %d contacte.\n", gasite); } void cautareadresa(void) { int i, gasite = 0; char date[100]; fflush(stdin); printf("Adresa partiala: "); gets(date); for(i = 0; i<intrari; ++i) if(strstr(agenda[i].adresa, date)!=NULL) { printf("Intrarea %d:\n", i); afisarecontact(agenda[i]); ++gasite;

Funcţii pentru gestiunea fişierelor

286

} printf("Au fost gasite %d contacte.\n", gasite); } void cautaretelefon(void) { int i, gasite = 0; char date[100]; fflush(stdin); printf("Telefon cautat: "); gets(date); for(i = 0; i<intrari; ++i) if(strcmp(date, agenda[i].telefon)==0) { printf("Intrarea %d:\n", i); afisarecontact(agenda[i]); ++gasite; } printf("Au fost gasite %d contacte.\n", gasite); } void adaugare(void) { char corect; struct contact c; puts("Introduceti datele:"); citirecontact(&c); puts("Ati introdus:"); afisarecontact(c); do { printf("Sigur doriti sa adaugati? (d/n)\n"); corect = toupper(getchar()); } while(corect !='D' && corect !='N'); if(corect == 'D') agenda[intrari++] = c; } void stergere(void) { char corect;

Page 146: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

287

int i; struct contact c; puts("Introduceti numarul contactului:"); scanf("%i", &i); if(i < 0 || i>= intrari) { puts("Valoare incorecta"); return; } puts("Ati selectat:"); afisarecontact(agenda[i]); do { printf("Sigur doriti sa stergeti? (d/n)\n"); corect = toupper(getchar()); }while(corect !='D' && corect !='N'); if(corect == 'D') { --intrari; for(; i<intrari; ++i) agenda[i] = agenda[i+1]; } } void listare(void) { int i; for(i = 0; i<intrari; ++i) { printf("Intrarea %d:\n", i); afisarecontact(agenda[i]); } printf("Au fost gasite %d contacte.\n", intrari); } void sortare(void) { int i, j; struct contact c; for(i=0;i < intrari; ++i) for(j=i+1; j < intrari; ++j)

Funcţii pentru gestiunea fişierelor

288

if(stricmp(agenda[i].nume, agenda[j].nume)>0) { c = agenda[i]; agenda[i] = agenda[j]; agenda[j] = c; } } int main() { int optiune; incarcare(); do { puts("Functia dorita:"); puts("\t[1] Cautare in agenda dupa nume"); puts("\t[2] Cautare in agenda dupa numar"); puts("\t[3] Cautare in agenda dupa adresa"); puts("\t[4] Adaugare in agenda"); puts("\t[5] Listare agenda"); puts("\t[6] Stergere din agenda"); puts("\t[7] Sortare agenda"); puts("\t[0] Terminare"); scanf("%d", &optiune); switch(optiune) { case 0: break; case 1: cautarenume(); break; case 2: cautaretelefon(); break; case 3: cautareadresa(); break; case 4: adaugare(); break; case 5: listare(); break; case 6: stergere(); break; case 7: sortare(); break; default: puts(" Functie necunoscuta"); } }while(optiune!=0); salvare(); getch(); }

Page 147: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

289

La prima rulare programul afişează mesajele corespunzătoare creării unei noi agende. Incarcare agenda Fisierul agenda.dat lipseste. Incep o agenda noua. Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 5 Au fost gasite 0 contacte. Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 4 Introduceti datele: Nume: Andrei Bautu Adresa: Constanta Telefon: 123456 Ati introdus: Nume: Andrei Bautu Adresa: Constanta Telefon: 123456 Sigur doriti sa adaugati? (d/n) d Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda

Funcţii pentru gestiunea fişierelor

290

[6] Stergere din agenda [7] Sortare agenda [0] Terminare 4 Introduceti datele: Nume: Paul Vasiliu Adresa: Constanta Telefon: 654321 Ati introdus: Nume: Paul Vasiliu Adresa: Constanta Telefon: 654321 Sigur doriti sa adaugati? (d/n) d Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 0 Salvare agenda

La rulări succesive, programul încarcă datele salvate la ultima rulare. Incarcare agenda Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 5 Intrarea 0: Nume: Andrei Bautu Adresa: Constanta

Page 148: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

291

Telefon: 123456 Intrarea 1: Nume: Paul Vasiliu Adresa: Constanta Telefon: 654321 Au fost gasite 2 contacte. Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 4 Introduceti datele: Nume: Elena Bautu Adresa: Constanta Telefon: 123654 Ati introdus: Nume: Elena Bautu Adresa: Constanta Telefon: 123654 Sigur doriti sa adaugati? (d/n) d Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 5 Intrarea 0: Nume: Andrei Bautu Adresa: Constanta Telefon: 123456 Intrarea 1: Nume: Paul Vasiliu Adresa: Constanta

Funcţii pentru gestiunea fişierelor

292

Telefon: 654321 Intrarea 2: Nume: Elena Bautu Adresa: Constanta Telefon: 123654 Au fost gasite 3 contacte. Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 0 Salvare agenda

La următoarea rulare, agenda conţine deja 3 contacte. Incarcare agenda Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 5 Intrarea 0: Nume: Andrei Bautu Adresa: Constanta Telefon: 123456 Intrarea 1: Nume: Paul Vasiliu Adresa: Constanta Telefon: 654321 Intrarea 2: Nume: Elena Bautu Adresa: Constanta

Page 149: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

293

Telefon: 123654 Au fost gasite 3 contacte. Functia dorita: [1] Cautare in agenda dupa nume [2] Cautare in agenda dupa numar [3] Cautare in agenda dupa adresa [4] Adaugare in agenda [5] Listare agenda [6] Stergere din agenda [7] Sortare agenda [0] Terminare 0 Salvare agenda

11.11. Exerciţii 96. Scrieţi un program C care afişează pe ecran doar literele şi spaţiile din conţinutul unui fişier al cărui nume a fost citit de la tastatură. 97. Scrieţi un program C care copiază conţinutul unui fişier în alt fişier caracter cu caracter (folosind funcţiile fgetc şi fputc), exceptând semnele de punctuaţie care sunt înlocuite cu spaţiu. 98. Scrieţi un program C care copiază conţinutul unui fişier în alt fişier linie cu linie (folosind funcţiile fgets şi fputs), transformând majusculele în minuscule. 99. Scrieţi un program C care scrie într-un fişier şiruri de caractere citite de la tastatură. Citirea se opreşte la citirea şirului de caractere vid (cu lungime 0). 100. Scrieţi un program C care afişează statistici despre conţinutul unui fişier (număr total de caractere, majuscule, minuscule, cifre, semne de punctuaţie, caractere de spaţiere). 101. Scrieţi un program C care afişează media numerelor citite dintr-un fişier. 102. Scrieţi un program C care gestionează catalogul unei discipline. Programul trebuie să permită adăugarea,

Funcţii pentru gestiunea fişierelor

294

ştergerea, afişarea şi căutarea studenţilor, adăugarea şi afişarea notelor, salvarea şi încărcarea datelor în fişiere diferite).

Page 150: Bazele_programarii_calculatoarelor

295

111222... MMMEEETTTOOODDDEEE DDDEEE PPPRRROOOGGGRRRAAAMMMAAARRREEE

12.1. Metoda “Divide et Impera” Metoda constă în descompunerea problemei de

rezolvat în două sau mai multe subprobleme care, la rândul lor se descompun în două sau mai multe subprobleme, până când se obţin subprobleme a căror rezolvare este directă şi nu mai necesită alte descompuneri. Soluţia problemei iniţiale se obţine prin combinarea soluţiilor problemelor cu rezovare directă în care a fost descompusă.

Să presupunem că urmează procesarea şirului 1, , ,k k ps s s+ K şi există m cu proprietatea k m p≤ ≤ . Şirul

1, , ,k k ps s s+ K se descompune în subşirurile 1, , ,k k ms s s+ K şi

1 2, , ,m m ps s s+ + K . Se procesează cele două subşiruri, cu

eventuala lor descompunere, şi apoi se combină rezultatele procesării tuturor subşirurilor pentru a obţine rezultatul procesării şirului 1, , ,k k ps s s+ K . Astfel, funcţia care procesează şirul 1, , ,k k ps s s+ K are parametrii de intrare k şi p şi generează rezultatul procesării şirului

1, , ,k k ps s s+ K .

Metode de programare

296

12.2. Exemple 115. Program pentru calculul sumei primelor n numere naturale

Fie suma ( ) 1 21, nS n a a a= + + +L şi

( ) 1, k k p

k

a a a k pS k p

a k p++ + + ≠

= =

L. Evident se poate defini

relaţia de recurenţă:

( ),, 1,

2 2

ka k pS k p k p k pS k S p k p

== + + + + ≠

,

relaţie care este o descompunere a lui ( )1,S n . Astfel, pentru 5n = se obţine:

( ) ( ) ( ) ( ) ( )( ) ( ) ( )( )1,5 1,3 4,5 1,2 3,3 4,4 5,5S S S S S S S= + = + + + =

( ) ( )( ) ( )( ) ( ) ( )( ) ( )( ) ( )1 2 3 4 5 1 2 3 4 51,1 2,2 3,3 4,4 5,5S S S S S a a a a a a a a a a= + + + + = + + + + = + + + + Problema propusă este cazul particular în care

ka k= . Fie ( )1, 1 2S n n= + + +L suma primelor n numere

naturale şi ( ) ( )1,

k k p k pS k p

k k p + + + + ≠

= =

L. Evident se

poate defini relaţia de recurenţă:

( ),, 1,

2 2

k k pS k p k p k pS k S p k p

== + + + + ≠

.

Astfel, pentru 5n = se obţine: ( ) ( ) ( ) ( ) ( )( ) ( ) ( )( )1,5 1,3 4,5 1,2 3,3 4,4 5,5S S S S S S S= + = + + + =

( ) ( )( ) ( )( ) ( ) ( )( ) ( )( ) ( )1,1 2,2 3,3 4,4 5,5 1 2 3 4 5 1 2 3 4 5 15S S S S S= + + + + = + + + + = + + + + =

Page 151: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

297

Programul afişează soluţiile subproblemelor şi soluţia problemei. // Suma primelor n numere naturale cu metoda "Divide et Impera" #include "stdio.h" #include "conio.h" int n; int s(int k,int p) { if(k==p) return k; else return s(k,(k+p)/2)+s((k+p)/2+1,p); } void afis(int k,int p) { if(k==p) printf(" S(%d,%d)=%d \n",k,k,s(k,k)); else { afis(k,(k+p)/2); afis((k+p)/2+1,p); printf(" S(%d,%d)=%d\n",k,(k+p)/2, s(k,(k+p)/2)); printf("S(%d,%d)=%d \n",(k+p)/2+1,p, s((k+p)/2+1,p)); } } int main() { int k,p; printf(" n = "); scanf("%d",&n); afis(1,n); printf(" S = %d ",s(1,n)); getch(); }

n = 5 S(1,1)=1 S(2,2)=2 S(1,1)=1

Metode de programare

298

S(2,2)=2 S(3,3)=3 S(1,2)=3 S(3,3)=3 S(4,4)=4 S(5,5)=5 S(4,4)=4 S(5,5)=5 S(1,3)=6 S(4,5)=9 S = 15

116. Program pentru calculul lui !n . Fie produsul

( ) 1 21, nP n a a a= ⋅ ⋅ ⋅L şi ( ) 1, k k p

k

a a a k pP k p

a k p+⋅ ⋅ ⋅ ≠

= =

L.

Evident se poate defini relaţia de recurenţă:

( ),, 1,

2 2

ka k pP k p k p k pP k P p k p

== + + ⋅ + ≠

,

relaţie care este o descompunere a lui ( )1,P n . Astfel, pentru 5n = se obţine:

( ) ( ) ( ) ( ) ( )( ) ( ) ( )( )1,5 1,3 4,5 1,2 3,3 4,4 5,5P P P P P P P= ⋅ = ⋅ ⋅ ⋅ =

( ) ( )( ) ( )( ) ( ) ( )( ) ( )( ) ( )1 2 3 4 5 1 2 3 4 51,1 2,2 3,3 4,4 5,5P P P P P a a a a a a a a a a= ⋅ ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ ⋅ Problema propusă este cazul particular în care

ka k= . Fie ( )1, 1 2P n n= ⋅ ⋅ ⋅L suma primelor n numere

naturale şi ( ) ( )1,

k k p k pP k p

k k p ⋅ + ⋅ ⋅ ≠

= =

L. Evident se

poate defini relaţia de recurenţă:

( ),, 1,

2 2

k k pP k p k p k pP k P p k p

== + + ⋅ + ≠

.

Page 152: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

299

Astfel, pentru 5n = se obţine: ( ) ( ) ( ) ( ) ( )( ) ( ) ( )( )1,5 1,3 4,5 1,2 3,3 4,4 5,5P P P P P P P= ⋅ = ⋅ ⋅ ⋅ =

( ) ( )( ) ( )( ) ( ) ( )( ) ( )( ) ( )1,1 2,2 3,3 4,4 5,5 1 2 3 4 5 1 2 3 4 5P P P P P= ⋅ ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ ⋅

1,1 2,2 3,3 4,4 5,5 1 2 3 4 5 1 2 3 4 5= ⋅ ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ ⋅ = ⋅ ⋅ ⋅ ⋅

Programul afişează soluţiile subproblemelor şi soluţia problemei. // Calculul lui n! cu metoda "Divide et Impera" #include "stdio.h" #include "conio.h" int n; int fact(int k,int p) { if(k==p) return k; else return fact(k,(k+p)/2)*fact((k+p)/2+1,p); } void afis(int k,int p) { if(k==p) printf("fact(%d,%d)=%d \n",k,k,fact(k,k)); else { afis(k,(k+p)/2); afis((k+p)/2+1,p); printf("fact(%d,%d)=%d \n",k,(k+p)/2, fact(k,(k+p)/2)); printf(" fact(%d,%d)=%d \n", (k+p)/2+1, p, fact((k+p)/2+1,p)); } } int main() { int k,p; printf(" n = "); scanf("%d",&n);

Metode de programare

300

afis(1,n); printf(" S = %d ",fact(1,n)); getch(); }

n = 5 fact(1,1)=1 fact(2,2)=2 fact(1,1)=1 fact(2,2)=2 fact(3,3)=3 fact(1,2)=2 fact(3,3)=3 fact(4,4)=4 fact(5,5)=5 fact(4,4)=4 fact(5,5)=5 fact(1,3)=6 fact(4,5)=20 S = 120

117. Program pentru determinarea maximului dintr-un şir de numere reale.

Programul determină maximul dintre termenii şirului 1, , ,k k px x x+ K folosind relaţia de recurenţă:

{ } 11 21 2 2 2

max max , , , ,max , , ,max , , , k k pk p k p k p

k k p

k

x x x x x x k px x x

x k p

+ + + + + + +

=

K KK

// Determinarea maximului dintr-un sir de numere reale cu metoda "Divide et Impera" #include "stdio.h" #include "conio.h" #include "stdlib.h" int n; typedef float sir[100]; sir x; float max(sir x,int k,int p) { float m1,m2;

Page 153: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

301

if(k==p) return x[k]; else { m1=max(x,k,(k+p)/2); m2=max(x,(k+p)/2+1,p); if(m1>=m2) return m1; else return m2; } } int main() { int k,p,i; char c[10]; printf(" Numarul de termeni din sir n = "); scanf("%d",&n); for(i=1;i<=n;i++) { printf(" Termenul %d = ",i); scanf("%f",&x[i]); } printf(" Maximul din sir este %4f \n",max(x,1,n)); getch(); }

Numarul de termeni din sir n = 5 Termenul 1 = 23 Termenul 2 = 21 Termenul 3 = 44 Termenul 4 = 55 Termenul 5 = 32 Maximul din sir este 55.000000

12.3. Metoda Backtracking Metoda Backtracking este o metodă generală de

elaborare a algoritmilor. Pentru rezolvarea anumitor probleme este necesară desfăşurarea unui proces de

Metode de programare

302

căutare a soluţiei aflate într-o anumită mulţime, numită spaţiul stărilor. Pentru fiecare element din spaţiul stărilor este definită o mulţime de acţiuni sau alternative. Momentul iniţial în rezolvarea problemei corespunde unei stări, numită iniţială, iar soluţiile corespund drumurilor în spaţiul stărilor, de la cea iniţială până la una finală. Procesul de rezolvare a problemei poate fi imaginat ca o secvenţă de acţiuni care asigură deplasarea, prin intermediul unei secvenţe de stări, în spaţiul stărilor, din starea iniţială la cea finală. În cazul anumitor probleme se doreşte obţinerea unei singure soluţii, altele solicită determinarea tuturor soluţiilor sau determinarea unei soluţii optime, dintr-un anumit punct de vedere, numită soluţie optimală.

Considerăm un labirint având una sau mai multe ieşiri. Starea iniţială poate fi considerată orice cameră a labirintului, problema revenind la găsirea unui drum din camera respectivă către una dintre ieşiri. Desfăşurarea procesului de căutare a unei stări finale presupune, la fiecare etapă, alegerea opţiunii pentru o alternativă posibilă a stării curente şi detectarea acelor stări capcană din care nu mai este posibilă continuarea procesului, sau deja se cunoaşte excluderea atingerii unei stări finale. Detectarea stării capcană trebuie să determine revenirea la starea din care s-a ajuns la ea şi selectarea unei noi opţiuni de continuare. În cazul în care nu mai există alternative care să nu fi fost selectate anterior, o astfel de stare devine la rândul ei capcană şi pentru ea se aplică acelaşi tratament.

Page 154: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

303

Prin soluţie a problemei se înţelege o secvenţă de acţiuni care determină tranziţia din starea iniţială într-o stare finală, fiecare componentă a unui drum soluţie reprezentând o alternativă din mulţimea de variante posibile. Cu alte cuvinte, 1x este alternativa aleasă pentru starea iniţială, 2x este alternativa selectată pentru starea în care s-a ajuns pe baza opţiunii 1x , 3x este alternativa selectată pentru starea în care s-a ajuns pe baza opţiunii

2x ,…, nx este alternativa selectată pentru starea în care s-a ajuns pe baza opţiunii 1nx − . După efectuarea acţiunii corespunzătoare alegerii alternativei nx rezultă o stare finală.

Procesul de căutare a unui drum soluţie revine la tentativa de extindere a porţiunii de drum construit, alegând prima alternativă disponibilă pentru starea curentă atinsă. Continuarea drumului poate fi realizată până la atingerea unei stări finale sau până la întâlnirea unei stări capcană (mulţimea vidă de alternative). Dacă este atinsă o stare capcană, atunci este necesară revenirea la starea anterioară şi selectarea următoarei alternative disponibile acestei stări. Dacă nu mai există alternative disponibile, atunci se iniţiază o nouă revenire şi aşa mai departe. În cazul în care există cel puţin încă o alternativă disponibilă, atunci se reia procesul de extindere a drumului rezultat. În condiţiile în care revenirea poate conduce la atingerea stării iniţiale şi pentru ea nu mai există alternative disponibile, se consideră că problema nu are soluţie.

Metode de programare

304

Pentru implementarea căutării este necesară reţinerea alternativei selectate pentru fiecare stare atinsă până la cea curentă, astfel încât, în cazul unei reveniri să fie posibilă alegerea alternativei următoare. Cu alte cuvinte, procesul de căutare revine la tentativa de extindere a drumului curent (pasul de continuare), cu eventuala revenire în cazul atingerii unei stări capcană (pasul de revenire - back), memorând alternativele selectate pentru fiecare stare intermediară atinsă (track). De aici îşi are geneza numele metodei backtracking.

Pentru determinarea unei singure soluţii, metoda presupune parcurgerea următorilor paşi: Pasul 1: starea iniţială a problemei este prima alternativă posibilă pentru starea curentă ; fie aceasta 1 1x S∈ ; Pasul 2: dacă starea curentă rezultată prin alternativa 1x este finală, atunci vectorul x are o singură componentă,

( )1x x= , este vectorul soluţie şi stop;

Pasul 3: altfel, este selectată prima alternativă din mulţimea de acţiuni posibile pentru starea curentă,

2 2x S∈ ; Pasul 4: dacă secvenţa de alternative care a condus la starea curentă este ( )1 2, , , kx x x x= K , atunci:

Pasul 5: dacă starea curentă este finală, soluţia este vectorul ( )1 2, , , kx x x x= K şi stop;

Pasul 6: dacă starea curentă nu este starea finală atunci: Pasul 7: dacă pentru starea curentă există alternative disponibile, atunci se alege prima dintre ele şi se continuă;

Page 155: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

305

Pasul 8: altfel, se revine la starea anterioară celei curente, soluţia parţial construită devine ( )1 2 1, , , kx x x x −= K

şi se face salt la Pasul 7. Pasul 9: dacă, în urma unui pas de revenire, s-a ajuns la starea iniţială şi nu mai sunt alternative disponibile, atunci problema nu are soluţie şi stop.

În cazul în care trebuie determinate toate soluţiile problemei, căutarea continuă după determinarea fiecărei soluţii prin efectuarea de reveniri succesive.Terminarea căutării este decisă în momentul în care s-a revenit la starea iniţială şi nu mai există alternative disponibile.

Dacă se doreşte obţinerea numai a soluţiilor care optimizează o funcţie

criteriu, atunci metoda se aplică pentru determinarea tuturor soluţiilor problemei,

fiecare nouă soluţie rezultată fiind comparată cu cea mai bună soluţie determinată anterior. Pentru aceasta este necesară reţinerea celei mai bune soluţii calculate la fiecare moment.

Astfel, metoda se aplică problemelor a căror soluţie se poate reprezenta sub forma unui vector soluţie

( )1 2, , , mX x x x= K , care aparţine spaţiului soluţiilor posibile sau alternativelor 1 2 mS S S S= × × ×L , k kx S∈ , şi

( ) , 1,2, ,k kcard S n k m= = K . Pentru fiecare problemă, se

dau anumite condiţii interne pe care trebuie să le satisfacă componentele vectorului soluţie. O soluţie care satisface condiţiile interne se numeşte soluţie rezultat. Metoda Backtracking generează toate soluţiile rezultat, fără generarea tuturor soluţiilor posibile. Se atribuie pe

Metode de programare

306

rând valori componentelor vectorului X astfel: se atribuie o valoare din mulţimea kS componentei kx numai dacă au fost atribuite valori componentelor 1 2 1, , , kx x x −K ; dacă s-a atribuit o valoare componentei kx , se verifică dacă componentele 1 2, , , kx x xK satisfac condiţiile interne de continuare; dacă condiţiile interne de continuare sunt satisfăcute se atribuie valoare componentei 1kx + ; dacă condiţiile interne de continuare nu sunt satisfăcute se atribuie o altă valoare componentei kx sau dacă mulţimea

kS a fost epuizată, se decrementează k .

12.4. Backtracking nerecursiv Pentru implementarea metodei definim

următoarele funcţii. Funcţia succesor testează dacă mulţimea kS mai are elemente şi care atribuie variabilei as (am succesor) valoarea 1 dacă mulţimea kS mai are elemente sau valoarea 0 în caz contrar. void succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; }

Funcţia validare verifică dacă sunt satisfăcute condiţiile interne specifice problemei şi care atribuie variabilei ev (este valid) valoarea 1 dacă sunt satisfăcute condiţiile interne sau valoarea 0 în caz contrar.

Page 156: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

307

Implementarea acestei funcţii diferă de la o problemă la alta, în funcţie de condiţiile de continuare. void validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if(!conditie) ev=0; }

Funcţia afişare care afişează o soluţie rezultat: void afisare(sir x, int k) { int i; printf(" ( "); for(i=1;i<=k-1;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); }

Aceste funcţii sunt gestionate de secvenţa Backtracking care codifică mecanismul de generare a soluţiilor rezultat. k=1; x[k]=0; while(k>0) { do { succesor(x,k,as); if(as) validare(x,k,ev); }while(as && !ev); if(as) if(k==n) afisare(x,k); else { k=k+1; x[k]=0; }

Metode de programare

308

else k=k-1; }

12.5. Exemple Prezentăm în continuare câteva exemple clasice

de aplicare a metodei backtracking. Pentru fiecare exemplu este furnizat codul sursă şi un exemplu de execuţie a programului. 118. Program pentru generarea permutărilor unei mulţimi de n obiecte // Generare permutari de n obiecte si a numarului lor nf #include "stdio.h" #include "conio.h" typedef int sir[100]; sir a,x; int i,k,n,as,ev,nf; void succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; } void validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if(!(x[k]!=x[i])) ev=0; } void afisare(sir x, int k) { int i;

Page 157: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

309

printf(" %d ( ",nf); for(i=1;i<=k-1;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); } int main() { printf("Generarea permutarilor de n obiecte \n"); nf=0; printf(" Numarul de obiecte n = "); scanf("%d",&n); printf(" Tastati obiectele \n"); for(i=1;i<=n;i++) { printf(" a [ %d ] = ",i); scanf("%d",&a[i]); } k=1;x[k]=0; while(k>0) { do { succesor(x,k,as); if(as) validare(x,k,ev); } while(as && !ev); if(as) if(k==n) { nf++; afisare(x,k); } else { k=k+1; x[k]=0; } else k=k-1; } printf(" Numarul permutarilor de %d obiecte este egal cu %d \n",n,nf); getch(); }

Metode de programare

310

Pentru un număr de trei obiecte programul furnizează rezultatele: Generarea permutarilor de n obiecte Numarul de obiecte n = 3 Tastati obiectele a [ 1 ] = 22 a [ 2 ] = 11 a [ 3 ] = 33 1 ( 22, 11, 33 ) 2 ( 22, 33, 11 ) 3 ( 11, 22, 33 ) 4 ( 11, 33, 22 ) 5 ( 33, 22, 11 ) 6 ( 33, 11, 22 ) Numarul permutarilor de 3 obiecte este egal cu 6

119. Program pentru generarea combinărilor de n obiecte luate câte p şi a numărului lor // Generare combinari de n luate cate p si a numarului lor // cnp #include "stdio.h" #include "conio.h" typedef int sir[100]; sir a,x; int p,i,k,n,as,ev,cnp; void succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; } void validare(sir x,int k, int &ev) { ev=1; if((k>=2) && !(a[x[k]]>a[x[k-1]]))

Page 158: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

311

ev=0; } void afisare(sir x, int k) { int i; printf(" %d ( ",cnp); for(i=1;i<=k-1;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); } int main() { printf(" Generarea combinarilor de n luate cate p \n"); cnp=0; printf(" Numarul de obiecte n = "); scanf("%d",&n); printf(" Tastati obiectele \n"); for(i=1;i<=n;i++) { printf(" a [ %d ] = ",i); scanf("%d",&a[i]); } printf(" Combinari luate cate p = "); scanf("%d",&p); if(p<=n) { k=1; x[k]=0; while(k>0) { do { succesor(x,k,as); if(as) validare(x,k,ev); }while(as && !ev); if(as) if(k==p) { cnp++; afisare(x,k);

Metode de programare

312

} else { k=k+1; x[k]=0; } else k=k-1; } printf(" Numarul combinarilor de %d obiecte luate cate %d este egal cu %d \n",n,p,cnp); getch(); } else printf(" Eroare in date: p>n \n"); getch(); }

Generarea combinarilor de n luate cate p Numarul de obiecte n = 4 Tastati obiectele a [ 1 ] = 11 a [ 2 ] = 22 a [ 3 ] = 33 a [ 4 ] = 44 Combinari luate cate p = 3 1 ( 11, 22, 33 ) 2 ( 11, 22, 44 ) 3 ( 11, 33, 44 ) 4 ( 22, 33, 44 ) Numarul combinarilor de 4 obiecte luate cate 3 este egal cu 4

120. Program pentru generarea aranjamentelor de n obiecte luate câte p şi a numărului lor // Generare aranjamente de n luate cate p si a numarului // lor anp #include "stdio.h" #include "conio.h" typedef int sir[100]; sir a,x; int p,i,k,n,as,ev,anp; void succesor(sir x,int k,int &as) { if(x[k]<n)

Page 159: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

313

{ as=1; x[k]=x[k]+1; } else as=0; } void validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if(x[k]==x[i]) ev=0; } void afisare(sir x, int k) { int i; printf("%d ( ",anp); for(i=1;i<=k-1;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); } int main() { printf(" Generarea aranjamentelor de n luate cate p \n"); anp=0; printf(" Numar de obiecte n = "); scanf("%d",&n); printf(" Tastati obiectele \n"); for(i=1;i<=n;i++) { printf(" a [ %d ] = ",i); scanf("%d",&a[i]); } printf(" Aranjamente luate cate p = "); scanf("%d",&p); if(p<=n) { k=1; x[k]=0;

Metode de programare

314

while(k>0) { do { succesor(x,k,as); if(as) validare(x,k,ev); } while(as && !ev); if(as) if(k==p) { anp++; afisare(x,k); } else { k=k+1; x[k]=0; } else k=k-1; } printf(" Numarul de aranjamente de %d luate cate %d este egal cu %d\n",n,p,anp); getch(); } else printf(" Eroare in date: p>n \n"); getch(); }

Generarea aranjamentelor de n luate cate p Numar de obiecte n = 4 Tastati obiectele a [ 1 ] = 11 a [ 2 ] = 32 a [ 3 ] = 21 a [ 4 ] = 43 Aranjamente luate cate p = 2 1 ( 11, 32 ) 2 ( 11, 21 ) 3 ( 11, 43 ) 4 ( 32, 11 ) 5 ( 32, 21 ) 6 ( 32, 43 )

Page 160: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

315

7 ( 21, 11 ) 8 ( 21, 32 ) 9 ( 21, 43 ) 10 ( 43, 11 ) 11 ( 43, 32 ) 12 ( 43, 21 ) Numarul de aranjamente de 4 luate cate 2 este egal cu 12

121. Program pentru generarea produsului cartezian a m mulţimi // Produsul cartezian a m multimi finite #include "stdio.h" #include "conio.h" typedef int sir[100]; sir x,n; int a[50][50],k,as,ev,i,j,m,nel; void succesor(sir x,int k,int &as) { if(x[k]<n[k]) { as=1; x[k]=x[k]+1; } else as=0; } void validare(int &ev) { ev=1; } void afisare(sir x,int k) { printf(" %d (",nel); for(i=1;i<=k;i++) printf(" %d ",a[i][x[i]]); printf(")\n"); } int main()

Metode de programare

316

{ int i; nel=0; printf(" Produs cartezian \n"); printf(" Numarul de multimi m = "); scanf("%d",&m); printf(" Tastati elementele multimilor \n"); for(i=1;i<=m;i++) { printf(" Numarul de elemente ale multimii %d n[%d] = ",i,i); scanf("%d",&n[i]); printf(" Tastati elementele multimii %d \n",i); for(j=1;j<=n[i];j++) { printf(" a%d[%d] =",i,j); scanf("%d",&a[i][j]); } } printf(" Produsul cartezian a celor %d multimi are elementele :\n",m); k=1; x[k]=0; while(k>0) { do { succesor(x,k,as); if(as) validare(ev); } while(as&&!ev); if(as) if(k==m) { nel++; afisare(x,k); } else { k=k+1; x[k]=0; } else

Page 161: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

317

k=k-1; } printf(" Numarul total de elemete este egal cu %d \n",nel); getch(); }

Ca exemplu de control am considerat cazul a trei mulţimi cu câte două, trei şi respectiv patru elemente. După rulare se obţin rezultatele: Produs cartezian Numarul de multimi m = 3 Tastati elementele multimilor Numarul de elemente ale multimii 1 n[1] = 2 Tastati elementele multimii 1 a1[1] =1 a1[2] =2 Numarul de elemente ale multimii 2 n[2] = 3 Tastati elementele multimii 2 a2[1] =11 a2[2] =22 a2[3] =33 Numarul de elemente ale multimii 3 n[3] = 4 Tastati elementele multimii 3 a3[1] =66 a3[2] =77 a3[3] =88 a3[4] =99 Produsul cartezian a celor 3 multimi are elementele 1 ( 1 11 66 ) 2 ( 1 11 77 ) 3 ( 1 11 88 ) 4 ( 1 11 99 ) 5 ( 1 22 66 ) 6 ( 1 22 77 ) 7 ( 1 22 88 ) 8 ( 1 22 99 ) 9 ( 1 33 66 ) 10 ( 1 33 77 ) 11 ( 1 33 88 ) 12 ( 1 33 99 )

Metode de programare

318

13 ( 2 11 66 ) 14 ( 2 11 77 ) 15 ( 2 11 88 ) 16 ( 2 11 99 ) 17 ( 2 22 66 ) 18 ( 2 22 77 ) 19 ( 2 22 88 ) 20 ( 2 22 99 ) 21 ( 2 33 66 ) 22 ( 2 33 77 ) 23 ( 2 33 88 ) 24 ( 2 33 99 ) Numarul total de elemete este egal cu 24

122. Program pentru rezolvarea problemei celor n dame Pe o tablă de şah cu n linii şi coloane se găsesc

n dame. Problema constă în determinarea poziţiilor celor n dame pe tabla de şah astfel încât acestea să nu se atace, pe fiecare linie, coloană şi diagonală a tablei de şah să existe o singură damă. Fie k şi kx , 1,2, ,k n= K , linia şi respectiv coloana pe care se găseşte dama k . Evident două dame i şi k , i k≠ , se găsesc pe aceeaşi coloană dacă şi numai dacă k ix x= , pe aceeaşi linie dacă şi numai dacă k i= (condiţie falsă prin ipoteza i k≠ ) şi pe aceeaşi diagonală dacă şi numai dacă k ix x k i− = − .

Astfel, condiţiile interne care sunt verificate de funcţia validare sunt: k ix x≠ (pe aceeaşi coloană să nu existe două dame) şi k ix x k i− ≠ − (pe aceeaşi diagonală să nu existe două dame), pentru fiecare 1,2, , 1i k= −K . Programul generează toate soluţiile rezultat şi numărul total de soluţii. // Problema celor n dame si numarul solutiilor rezultat #include "stdio.h"

Page 162: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

319

#include "conio.h" #include "math.h" #include "stdlib.h" typedef int sir[100]; sir x; int i,n,k,as,ev; void succesor(sir x,int k,int&as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; } void validare(sir x,int k,int &ev) { ev=1; for(i=1;i<=k-1;i++) if(x[k]==x[i] || (k-i==abs(x[k]-x[i]))) ev=0; } void afisare(sir x,int k) { for(i=1;i<=k;i++) printf(" Dama %d este pe linia %d si coloana %d \n",i,i,x[i]); } int main() { int nsol; printf(" Problema celor n dame \n"); printf(" Numarul de dame n = "); scanf("%d",&n); k=1;x[k]=0;nsol=1; while(k>0) { do { succesor(x,k,as);

Metode de programare

320

if(as) validare(x,k,ev); } while(as&&!ev); if(as) if(k==n) { printf("Solutia %d\n",nsol); nsol++; afisare(x,k); getch(); } else { k++; x[k]=0; } else k--; } printf(" Numarul total de solutii rezultat este %d \n",nsol-1); getch(); }

Problema celor n dame Numarul de dame n = 4 Solutia 1 Dama 1 este pe linia 1 si coloana 2 Dama 2 este pe linia 2 si coloana 4 Dama 3 este pe linia 3 si coloana 1 Dama 4 este pe linia 4 si coloana 3 Solutia 2 Dama 1 este pe linia 1 si coloana 3 Dama 2 este pe linia 2 si coloana 1 Dama 3 este pe linia 3 si coloana 4 Dama 4 este pe linia 4 si coloana 2 Numarul total de solutii rezultat este 2

12.6. Backtracking recursiv În variantă recursivă se defineşte funcţia

backtracking: void back(int k)

Page 163: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

321

{ if(sol(k)) afisare(x,k); else { init(k); while(succesor(x,k,as)) if(validare(x,k,ev)) back(k+1); } }

Funcţia are un singur parametru de tip întreg, care este indice al vectorului soluţie. Funcţia testează dacă s-a generat o soluţie prin apelul funcţiei sol. Definiţia funcţiei sol este: int sol(int k) { return k==n+1; }

Dacă s-a obţinut o soluţie, aceasta este afişată de funcţia afisare, cu aceeaşi definiţie ca în cazul nerecursiv. Dacă nu s-a obţinut o soluţie, se iniţializează nivelul k cu valoarea aflată înaintea tuturor valorilor posibile. Iniţializarea este făcută de funcţia init: void init(int k) { x[k]=0; }

Funcţia init efectuează iniţializarea lui kx cu o valoare prin care se indică faptul că, până la acel moment, nu a fost selectată nici o alternativă pentru poziţia k a vectorului x ;

Metode de programare

322

După iniţializare, se generează succesiv toate valorile din mulţimea kS . Pentru generarea acestor valori se foloseşte funcţia succesor, modificată faţă de cazul nerecursiv în sensul că returnează valoarea variabilei de tip întreg as: int succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; return as; }

Pentru fiecare valoare generată se testează cu funcţia validare condiţiile de continuare. Funcţia validare este modificată faţă de cazul nerecursiv prin returnarea valorii de tip întreg a variabilei ev: int validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if(!conditie) ev=0; return ev; }

Dacă condiţiile de continuare sunt îndeplinite se generează următoarea valoare pentru componenta k prin apelul recursiv al funcţiei back.

În funcţia main() se apelează funcţia back cu parametrul 1 deoarece algoritmul pleacă de la componenta de indice 1.

Page 164: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

323

12.7. Exemple Reluăm exemplele din secţiunea 12.3.2 pentru

care folosim metoda backtracking în variantă recursivă. 123. Program pentru generarea permutărilor unei mulţimi de n obiecte. În acest caz, { }1 2 1 2, ,n nS S S a a a= = =L K . Alternativele posibile pentru starea iniţială corespund alegerilor pentru prima poziţie dintr-un vector soluţie.

Funcţia init(k) realizează iniţializarea elementului kx cu valoarea 0, pentru a marca faptul că, până la momentul curent, nu a fost selectată nici o alternativă pentru kx . Funcţia succesor returnează valoarea 1 a variabilei as dacă elementul kx are succesor în mulţimea { }1 2, , na a aK , sau 0 dacă elementul

kx nu are succesor în mulţimea { }1 2, , na a aK . Funcţia

validare returnează valoarea 1 a variabilei ev dacă şi numai dacă vectorul ( )1 2, , , kx x x x= K calculat până la

momentul curent satisface condiţia internă a problemei, adică are componentele distincte. // Generare permutari de n obiecte si a numarului lor nf #include "stdio.h" #include "conio.h" typedef int sir[100]; sir a,x; int i,k,n,as,ev,nf; void init(int k) { x[k]=0; } int sol(int k) { return k==n+1; }

Metode de programare

324

int succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; return as; } int validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if(!(x[k]!=x[i])) ev=0; return ev; } void afisare(sir x, int k) { int i; printf(" %d ( ",nf); for(i=1;i<=k-2;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); } void back(int k) { if(sol(k)) { nf++; afisare(x,k); } else { init(k); while(succesor(x,k,as)) if(validare(x,k,ev)) back(k+1); } } int main() {

Page 165: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

325

printf(" Generarea permutarilor de n obiecte \n"); nf=0; printf(" Numarul de obiecte n = "); scanf("%d",&n); printf(" Tastati obiectele \n"); for(i=1;i<=n;i++) { printf(" a [ %d ] = ",i); scanf("%d",&a[i]); } back(1); printf(" Numarul permutarilor de %d obiecte este egal cu %d \n",n,nf); getch(); }

Pentru trei obiecte programul furnizează rezultatele: Generarea permutarilor de n obiecte Numarul de obiecte n = 3 Tastati obiectele a [ 1 ] = 1 a [ 2 ] = 2 a [ 3 ] = 3 1 ( 1, 2, 3 ) 2 ( 1, 3, 2 ) 3 ( 2, 1, 3 ) 4 ( 2, 3, 1 ) 5 ( 3, 1, 2 ) 6 ( 3, 2, 1 ) Numarul permutarilor de 3 obiecte este egal cu 6

124. Program pentru generarea combinărilor de n obiecte luate câte p şi a numărului lor // Generare combinari de n luate cate p si a numarului lor // cnp #include "stdio.h" #include "conio.h"

Metode de programare

326

typedef int sir[100]; sir a,x; int p,i,k,n,as,ev,cnp; void init(int k) { x[k]=0; } int sol(int k) { return k==p+1; } int succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; return as; } int validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if((k>=2) && !(a[x[k]]>a[x[k-1]])) ev=0; return ev; } void afisare(sir x, int k) { int i; printf(" %d ( ",cnp); for(i=1;i<=k-2;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); } void back(int k) { if(sol(k)) { cnp++; afisare(x,k); } else

Page 166: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

327

{ init(k); while(succesor(x,k,as)) if(validare(x,k,ev)) back(k+1); } } int main() { printf(" Generarea combinarilor de n luate cate p \n"); cnp=0; printf(" Numarul de obiecte n = "); scanf("%d",&n); printf(" Tastati obiectele \n"); for(i=1;i<=n;i++) { printf(" a [ %d ] = ",i); scanf("%d",&a[i]); } printf(" Combinari luate cate p = "); scanf("%d",&p); if(p<=n) { back(1); printf(" Numarul combinarilor de %d obiecte luate cate %d este egal cu %d \n",n,p,cnp); } else printf(" Eroare %d > %d \n ",p,n); getch(); }

Pentru exemplificare am generat toate combinările de patru obiecte luate câte două. Generarea combinarilor de n luate cate p Numarul de obiecte n = 4 Tastati obiectele a [ 1 ] = 1 a [ 2 ] = 2 a [ 3 ] = 3

Metode de programare

328

a [ 4 ] = 4 Combinari luate cate p = 2 1 ( 1, 2 ) 2 ( 1, 3 ) 3 ( 1, 4 ) 4 ( 2, 3 ) 5 ( 2, 4 ) 6 ( 3, 4 ) Numarul combinarilor de 4 obiecte luate cate 2 este egal cu 6

125. Program pentru generarea aranjamentelor de n obiecte luate câte p şi a numărului lor // Generare aranjamente de n luate cate p si a numarului // lor anp #include "stdio.h" #include "conio.h" typedef int sir[100]; sir a,x; int p,i,k,n,as,ev,anp; void init(int k) { x[k]=0; } int sol(int k) { return k==p+1; } int succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; return as; } int validare(sir x,int k, int &ev) { ev=1;

Page 167: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

329

for(i=1;i<=k-1;i++) if(x[k]==x[i]) ev=0; return ev; } void afisare(sir x, int k) { int i; printf(" %d ( ",anp); for(i=1;i<=k-2;i++) printf("%d, ",a[x[i]]); printf("%d ) \n",a[x[i]]); } void back(int k) { if(sol(k)) { anp++; afisare(x,k); } else { init(k); while(succesor(x,k,as)) if(validare(x,k,ev)) back(k+1); } } int main() { printf(" Generarea aranjamentelor de n luate cate p \n"); anp=0; printf(" Numarul de obiecte n = "); scanf("%d",&n); printf(" Tastati obiectele \n"); for(i=1;i<=n;i++) { printf(" a [ %d ] = ",i); scanf("%d",&a[i]); } printf(" Aranjamente luate cate p = "); scanf("%d",&p);

Metode de programare

330

if(p<=n) { back(1); printf(" Numarul de aranjamente de %d luate cate %d este egal cu %d\n",n,p,anp); } else printf(" Eroare %d > %d \n ",p,n); getch(); }

Pentru n=4 şi p=2 se obtin rezultatele: Generarea aranjamentelor de n luate cate p Numarul de obiecte n = 4 Tastati obiectele a [ 1 ] = 1 a [ 2 ] = 2 a [ 3 ] = 3 a [ 4 ] = 4 Aranjamente luate cate p = 2 1 ( 1, 2 ) 2 ( 1, 3 ) 3 ( 1, 4 ) 4 ( 2, 1 ) 5 ( 2, 3 ) 6 ( 2, 4 ) 7 ( 3, 1 ) 8 ( 3, 2 ) 9 ( 3, 4 ) 10 ( 4, 1 ) 11 ( 4, 2 ) 12 ( 4, 3 ) Numarul de aranjamente de 4 luate cate 2 este egal cu 12

126. Program pentru generarea produsului cartezian a m mulţimi // Produsula cartezian a m multimi finite #include "stdio.h" #include "conio.h" typedef int sir[100]; sir x,n;

Page 168: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

331

int a[50][50],k,as,ev,i,j,m,nel; void init(int k) { x[k]=0; } int sol(int k) { return k==m+1; } int succesor(sir x,int k,int &as) { if(x[k]<n[k]) { as=1; x[k]=x[k]+1; } else as=0; return as; } int validare(int &ev) { ev=1; return ev; } void afisare(sir x, int k) { printf(" %d (",nel); for(i=1;i<=k-1;i++) printf(" %d ",a[i][x[i]]); printf(")\n"); } void back(int k) { if(sol(k)) { nel++; afisare(x,k); } else { init(k); while(succesor(x,k,as))

Metode de programare

332

if(validare(ev)) back(k+1); } } int main() { int i; nel=0; printf(" Produs cartezian \n"); printf(" Numarul de multimi m = "); scanf("%d",&m); printf(" Tastati elementele multimilor \n"); for(i=1;i<=m;i++) { printf(" Numarul de elemente ale multimii %d n[%d] = ",i,i); scanf("%d",&n[i]); printf(" Tastati elementele multimii %d \n",i); for(j=1;j<=n[i];j++) { printf(" a%d[%d] =",i,j); scanf("%d",&a[i][j]); } } printf(" Produsul cartezian a celor %d multimi are elementele :\n",m); back(1); printf(" Numarul total de elemete este egal cu %d \n",nel); getch(); }

Ca exemplu de control am considerat cazul a trei mulţimi cu câte două, trei şi respectiv patru elemente. După rulare se obţin rezultatele: Produs cartezian Numarul de multimi m = 3 Tastati elementele multimilor Numarul de elemente ale multimii 1 n[1] = 2 Tastati elementele multimii 1 a1[1] =11

Page 169: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

333

a1[2] =22 Numarul de elemente ale multimii 2 n[2] = 3 Tastati elementele multimii 2 a2[1] =1 a2[2] =2 a2[3] =3 Numarul de elemente ale multimii 3 n[3] = 4 Tastati elementele multimii 3 a3[1] =6 a3[2] =7 a3[3] =8 a3[4] =9 Produsul cartezian a celor 3 multimi are elementele : 1 ( 11 1 6 ) 2 ( 11 1 7 ) 3 ( 11 1 8 ) 4 ( 11 1 9 ) 5 ( 11 2 6 ) 6 ( 11 2 7 ) 7 ( 11 2 8 ) 8 ( 11 2 9 ) 9 ( 11 3 6 ) 10 ( 11 3 7 ) 11 ( 11 3 8 ) 12 ( 11 3 9 ) 13 ( 22 1 6 ) 14 ( 22 1 7 ) 15 ( 22 1 8 ) 16 ( 22 1 9 ) 17 ( 22 2 6 ) 18 ( 22 2 7 ) 19 ( 22 2 8 ) 20 ( 22 2 9 ) 21 ( 22 3 6 ) 22 ( 22 3 7 ) 23 ( 22 3 8 ) 24 ( 22 3 9 ) Numarul total de elemete este egal cu 24

127. Program pentru rezolvarea problemei celor n dame // Problema celor n dame si numarul solutiilor rezultat

Metode de programare

334

#include "stdio.h" #include "conio.h" // Problema celor n dame #include "math.h" #include "stdlib.h" typedef int sir[100]; sir x; int i,n,k,as,ev,nsol; void init(int k) { x[k]=0; } int sol(int k) { return k==n+1; } int succesor(sir x,int k,int &as) { if(x[k]<n) { as=1; x[k]=x[k]+1; } else as=0; return as; } int validare(sir x,int k, int &ev) { ev=1; for(i=1;i<=k-1;i++) if(x[k]==x[i] || (k-i==abs(x[k]-x[i]))) ev=0; return ev; } void afisare(sir x, int k) { for(i=1;i<=k-1;i++) printf(" Dama %d este pe linia %d si coloana %d \n",i,i,x[i]); } void back(int k)

Page 170: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

335

{ if(sol(k)) { printf("Solutia %d\n",nsol); nsol++; afisare(x,k); } else { init(k); while(succesor(x,k,as)) if(validare(x,k,ev)) back(k+1); } } int main() { nsol=1; printf(" Problema celor n dame \n"); printf(" Numarul de dame n = "); scanf("%d",&n); back(1); printf(" Numarul total de solutii rezultat este %d \n",nsol-1); getch(); }

Soluţiile pentru cazul a patru dame sunt: Problema celor n dame Numarul de dame n = 4 Solutia 1 Dama 1 este pe linia 1 si coloana 2 Dama 2 este pe linia 2 si coloana 4 Dama 3 este pe linia 3 si coloana 1 Dama 4 este pe linia 4 si coloana 3 Solutia 2 Dama 1 este pe linia 1 si coloana 3 Dama 2 este pe linia 2 si coloana 1 Dama 3 este pe linia 3 si coloana 4 Dama 4 este pe linia 4 si coloana 2 Numarul total de solutii rezultat este 2

Metode de programare

336

12.8. Metoda Greedy Algoritmii de tip greedy se caracterizează prin

luarea unor decizii rapide care duc la găsirea unei soluţii a problemei. Nu întotdeauna asemenea decizii rapide duc la o soluţie optimă, dar vom vedea că există anumite tipuri de probleme unde se pot obţine soluţii optime sau foarte apropiate de optim. În traducere din limba engleză cuvântul “greedy” înseamnă lacom. Algoritmii de tip greedy vor să construiască într-un mod cât mai rapid soluţia problemei.

Algoritmii de tip Greedy se aplică problemelor ale căror date de intrare sunt organizate sub forma unei mulţimi A şi pentru care se cere determinarea unei submulţimi B A⊂ care să îndeplinească anumite condiţii astfel încât submulţimea B să fie acceptată ca soluţie posibilă.

În general pot să existe mai multe submulţimi B A⊂ care să reprezinte soluţii posibile ale problemei. Dintre toate aceste submulţimi B se pot selecta, conform unui anumit criteriu, anumite submulţimi *B care reprezintă soluţii optime ale problemei. Scopul este de a găsi, dacă este posibil, una din submulţimile *B . Dacă acest lucru nu este posibil, atunci scopul este găsirea unei mulţimi B care să fie cât mai aproape de submulţimile *B , conform criteriului de optimalitate impus.

Construirea submulţimii B se face printr-un şir de decizii. Iniţial se porneşte cu mulţimea vidă, B = Φ . Fiecare decizie constă în alegerea unui element din mulţimea A , analiza lui şi eventual introducerea lui în

Page 171: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

337

submulţimea B . În funcţie de modul în care se iau aceste decizii, submulţimea B se va apropia mai mult sau mai puţin de soluţia optimă *B . În cazul ideal vom avea

*B B= . Algoritmii de tip greedy nu urmăresc să determine

toate soluţiile posibile şi să aleagă dintre ele, conform criteriului de optimalitate impus, soluţiile optime. După cum spune şi numele, algoritmii de tip greedy sunt caracterizaţi prin lăcomie şi nu au răbdarea să investigheze toate variantele posibile de alegere a soluţiei. Ei încep construirea unei soluţii pornind de la mulţimea vidă. La fiecare pas se ia câte o decizie şi se extinde soluţia cu câte un element. La fiecare pas se analizează câte un element din mulţimea A şi se decide dacă să fie sau nu inclus în submulţimea B care se construieşte. Astfel se progresează de la mulţimea vidă Φ cu un şir de submulţimi intermediare 1 2, , , , ,kB B BΦ K K cu proprietatea 1 2 kB B B AΦ ⊂ ⊂ ⊂ ⊂ ⊂L L , până când se obţine o soluţie finală B .

Nu există un standard de implementare a metodei greedy, care să fie general valabil pentru toate problemele care pot fi rezolvate cu această metodă. Implementarea metodei diferă de la o problemă la alta. Există două variante de principiu pentru implementarea algoritmilor de tip greedy. Prima variantă foloseşte funcţiile: sel, test, adaug şi afis. Funcţia sel este are rolul de a selecta următorul element x din mulţimea A care să fie prelucrat. Funcţia test verifică dacă elementul x poate fi adăugat soluţiei intermediare iB astfel încât

Metode de programare

338

noua soluţie 1iB + care s-ar obţine să fie o soluţie validă. Funcţia adaug adaugă elementul testat x mulţimii 1iB + . Funcţia afis listează soluţia optimă, elementele mulţimii

*B . Prezentăm în continuare pseudocodul pentru această variantă greedy. Se consideră că numărul de elemente al mulţimii A este egal cu n.

( , )

0; ;

( )( ( , ))

( , )( )

greedy A BBpentru i i n irepeta

x sel Aif test B xadaug B x

afis B

= Φ= < + +

=

Dificultatea în această variantă constă în scrierea funcţiei sel. Dacă funcţia sel este bine concepută, atunci putem fi siguri că soluţia B găsită este o soluţie optimă. Dacă funcţia sel nu este foarte bine concepută, atunci soluţia B va fi doar o soluţie posibilă şi nu va fi optimă. Ea se poate apropia însă mai mult sau mai puţin de soluţia optimă *B , în funcţie de criteriul de selecţie implementat.

În a doua variantă de implementare a algoritmului greedy se face mai întâi o prelucrare a mulţimii A de funcţia prel. Practic se face o sortare a elementelor mulţimii A , conform unui anumit criteriu. După sortare, elementele vor fi prelucrate direct în ordinea rezultată. Dacă prelucrarea mulţimii A este bine făcută, atunci se

Page 172: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

339

va ajunge în mod sigur la o soluţie optimă. Altfel se va obţine doar o soluţie posibilă, mai mult sau mai puţin apropiată de optim. Prezentăm în continuare descrirea în limbajul pseudocod pentru această a doua variantă a algoritmului greedy.

( )

( , )

0; ;

[ ]( ( , ))

( , )( )

greedy A BBprel Apentru i i n irepeta

x A iif test B xadaug B x

afis B

= Φ

= < + +

=

Funcţiile test, adaug şi afis joacă acelaşi rol ca în prima variantă de implementare a metodei greedy.

Prezentăm în continuare exemple de probleme rezolvate cu metoda greedy. 128. Fie A o mulţime cu n elemente numere reale. Să se determine mulţimea B cu număr maxim de elemente a căror sumă să fie maximă.

Este evident că pentru ca numărul elementelor mulţimii B să fie maxim şi suma lor să fie maximă, trebuie ca B să conţină numai elemente pozitive şi nule. Funcţia alege selectează elemetele mulţimii A. Funcţia posibil returnează o valaore nenulă (1) dacă a fost selectat un element pozitiv sau nul din mulţimea A, sau valoarea zero în cazul selecţiei unei valori negative. Funcţia adauga

Metode de programare

340

construieşte mulţimea B cu elementele selectate din mulţimea A care satisfac codiţia de nenegativitate. Funcţia afiseaza tipareşte elementele mulţimii B, numărul şi suma lor. Programul este un exemplu a primei metode de implementare a algoritmului greedy.

12.9. Exemple 129. Program pentru determinarea sumei maxime. // Problema sumei maxime #include "stdio.h" #include "conio.h" int n,m,i; float sel(float a[]) { return a[i]; } int test(float b[],float x) { if(x>=0) return 1; else return 0; } void adaug(float b[],float x) { b[m]=x; m++; } void afis(float b[]) { float s; if(m) { printf(" Multimea B are %d elemente \n",m); for(i=0,s=0;i<m;s+=b[i],i++) printf(" B [ %d ]= %f \n",i,b[i]); s+=b[i];

Page 173: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

341

printf(" Suma elementelor multimii B este %f \n",s); } else printf(" Multimea B este vida \n"); } void greedy(float a[],float b[]) { float x; for(i=0;i<n;i++) { x=sel(a); if(test(b,x)) adaug(b,x); } afis(b); } int main() { float a[50],b[50]; printf(" Numarul de elemente ale multimii n = "); scanf("%d",&n); printf(" Elementele multimii A : \n"); for(i=0;i<n;i++) { printf(" A [ %d ] = ",i); scanf("%f",&a[i]); } greedy(a,b); getch(); }

Un exemplu de rulare este: Numarul de elemente ale multimii n = 5 Elementele multimii A : A [ 0 ] = 2. A [ 1 ] = 3. A [ 2 ] = -9. A [ 3 ] = -5. A [ 4 ] = 7. Multimea B are 3 elemente B [ 0 ]= 2.000000

Metode de programare

342

B [ 1 ]= 3.000000 B [ 2 ]= 7.000000 Suma elementelor multimii B este 12.000000

Pentru cazul în care mulţimea A are numai elemente negative, B este vidă. Numarul de elemente ale multimii n = 3 Elementele multimii A : A [ 0 ] = -9. A [ 1 ] = -1. A [ 2 ] = -2. Multimea B este vida

130. Program pentru determinarea multiplilor de k dintr-o mulţime de numere naturale cu metoda greedy. // Multipli de k dintr-o multime de numere naturale #include "stdio.h" #include "conio.h" int n,m,i; float sel(int a[]) { return a[i]; } int test(int b[],int x, int k) { if(x%k==0) return 1; else return 0; } void adaug(int b[],int x) { b[m]=x; m++; } void afis(int b[],int k) { if(m)

Page 174: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

343

{ printf(" Multimea A are %d multipli de %d \n",m,k); for(i=0;i<m;i++) printf(" B [ %d ]= %d \n",i,b[i]); } else printf(" Submultimea multiplilor de %d este vida \n",k); } void greedy(int a[],int b[], int k) { int x; for(i=0;i<n;i++) { x=sel(a); if(test(b,x,k)) adaug(b,x); } afis(b,k); } int main() { int a[50],b[50],k; printf(" Numarul de elemente ale multimii n = "); scanf("%d",&n); printf(" Elementele multimii A : \n"); for(i=0;i<n;i++) { printf(" A [ %d ] = ",i); scanf("%d",&a[i]); } printf(" Divizorul comun k = "); scanf("%d",&k); greedy(a,b,k); getch(); }

Pentru n=6 şi k=5 se obţine: Numarul de elemente ale multimii n = 6 Elementele multimii A : A [ 0 ] = 2

Metode de programare

344

A [ 1 ] = 0 A [ 2 ] = 1 A [ 3 ] = 5 A [ 4 ] = 4 A [ 5 ] = 15 Divizorul comun k = 5 Multimea A are 3 multipli de 5 B [ 0 ]= 0 B [ 1 ]= 5 B [ 2 ]= 15

Pentru o mulţime fără multipli de k se obţine: Numarul de elemente ale multimii n = 6 Elementele multimii A : A [ 0 ] = 2 A [ 1 ] = 3 A [ 2 ] = 4 A [ 3 ] = 1 A [ 4 ] = 12 A [ 5 ] = 22 Divizorul comun k = 5 Submultimea multiplilor de 5 este vida

131. Problema continuă a rucsacului. Presupunem că avem la dispoziţie un rucsac în care se poate transporta o greutate maximă fixată G. Cu acest rucsac urmează a fi transportate n obiecte de greutăţi cunoscute. Pentru fiecare obiect se cunoaşte beneficiul transportării lui la destinaţie. Problema constă în determinarea obiectelor care vor fi transportate la destinaţie astfel încât beneficiul total al transportului să fie maxim. Dacă un obiect nu poate fi divizat, problema se numeşte continuă. Dacă obiectele pot fi divizate problema se numeste discretă.

Rezolvarea problemei continue a rucsacului se poate face cu un algoritm de tip greedy. Se calculează pentru fiecare obiect raportul dintre beneficiu şi greutatea lui. Se sortează descrescător vectorul acestor rapoarte. Cât timp nu a fost completată greutatea totală a

Page 175: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

345

rucsacului şi mai există obiecte în afara lui se selectează obiectul cu raportul maxim dintre beneficiu şi greutate. Dacă obiectul nu încape în întregime în rucsac se determină fracţiunea din obiect care se poate transporta. Programul afisează ordinea introducerii obiectelor în rucsac, informaţiile referitoare la obiectele introduse si beneficiul total al transportului. Programul este un exemplu a celei de a doua metode de implementare a algoritmului greedy. // Problema continua a rucsacului #include "stdio.h" #include "conio.h" int n,m,i,j; float G,grcurent,dif,benef,f; struct ob { int nr; float g; float benef; float raport; } obiect[50]; struct r { int nr; float g; float benef; float raport; float f; } rucsac[50]; void prel(void) { int auxx; float aux; for(i=0;i<n;i++) for(j=i+1;j<n;j++) if(obiect[i].raport<=obiect[j].raport) { aux=obiect[j].raport; obiect[j].raport=obiect[i].raport;

Metode de programare

346

obiect[i].raport=aux; aux=obiect[j].benef; obiect[j].benef=obiect[i].benef; obiect[i].benef=aux; aux=obiect[j].g; obiect[j].g=obiect[i].g; obiect[i].g=aux; auxx=obiect[j].nr; obiect[j].nr=obiect[i].nr; obiect[i].nr=auxx; } } int test() { dif=G-grcurent; if(dif) return 1; else return 0; } void adaug() { if(obiect[i].g<=dif) { rucsac[m].nr=obiect[i].nr; rucsac[m].g=obiect[i].g; rucsac[m].benef=obiect[i].benef; rucsac[m].raport=obiect[i].raport; grcurent+=rucsac[m].g; benef+=rucsac[m].benef; rucsac[m].f=1.; m++; } else { rucsac[m].nr=obiect[i].nr; rucsac[m].g=dif; rucsac[m].benef=(obiect[i].benef/ obiect[i].g)*dif; rucsac[m].raport=rucsac[m].benef/ rucsac[m].g; benef+=rucsac[m].benef; rucsac[m].f=rucsac[m].g/obiect[i].g; m++;

Page 176: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

347

} } void afis(void) { if(m) { printf(" Rucsacul contine %d obiecte \n",m); for(i=0;i<m;i++) { printf(" Obiectul %d \n",rucsac[i].nr); printf("\t\t Greutate %f \n",rucsac[i].g); printf("\t\t Fractiunea de greutate %f \n",rucsac[i].f); printf("\t\t Beneficiu %f \n",rucsac[i].benef); printf("\t\t Raport beneficiu/greutate %f \n",rucsac[i].raport); } printf(" Beneficiul total este %f \n",benef); } else printf(" Rucsacul este gol \n"); } void greedy(void) { prel(); for(i=0;i<n;i++) { if(test()) adaug(); } afis(); } int main() { printf(" Numarul de obiecte n = "); scanf("%d",&n); printf(" Greutatea si beneficiul: \n"); for(i=0;i<n;i++) { printf(" Greutatea obiectului %d = ",i);

Metode de programare

348

scanf("%f",&obiect[i].g); printf(" Beneficiul adus de obiectul %d = ",i); scanf("%f",&obiect[i].benef); obiect[i].raport=obiect[i].benef/ obiect[i].g; obiect[i].nr=i; } printf(" Greutatea maxima admisa : "); scanf("%f",&G); greedy(); getch(); }

Pentru exemplul de mai jos suma tuturor greutăţilor obiectelor este mai mică decât capacitatea rucsacului. Numarul de obiecte n = 3 Greutatea si beneficiul: Greutatea obiectului 0 = 4 Beneficiul adus de obiectul 0 = 8 Greutatea obiectului 1 = 2 Beneficiul adus de obiectul 1 = 6 Greutatea obiectului 2 = 5 Beneficiul adus de obiectul 2 = 20 Greutatea maxima admisa : 100 Rucsacul contine 3 obiecte Obiectul 2 Greutate 5.000000 Fractiunea de greutate 1.000000 Beneficiu 20.000000 Raport beneficiu/greutate 4.000000 Obiectul 1 Greutate 2.000000 Fractiunea de greutate 1.000000 Beneficiu 6.000000 Raport beneficiu/greutate 3.000000 Obiectul 0

Page 177: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

349

Greutate 4.000000 Fractiunea de greutate 1.000000 Beneficiu 8.000000 Raport beneficiu/greutate 2.000000 Beneficiul total este 34.000000 Beneficiul total este 34.000000

Pentru exemplul de mai jos suma tuturor greutăţilor obiectelor este mai mare decât capacitatea rucsacului. Repartiţia obiectelor în rucsac este: Numarul de obiecte n = 3 Greutatea si beneficiul: Greutatea obiectului 0 = 4 Beneficiul adus de obiectul 0 = 8 Greutatea obiectului 1 = 2 Beneficiul adus de obiectul 1 = 6 Greutatea obiectului 2 = 5 Beneficiul adus de obiectul 2 = 20 Greutatea maxima admisa : 8 Rucsacul contine 3 obiecte Obiectul 2 Greutate 5.000000 Fractiunea de greutate 1.000000 Beneficiu 20.000000 Raport beneficiu/greutate 4.000000 Obiectul 1 Greutate 2.000000 Fractiunea de greutate 1.000000 Beneficiu 6.000000 Raport beneficiu/greutate 3.000000 Obiectul 0 Greutate 1.000000 Fractiunea de greutate 0.250000

Metode de programare

350

Beneficiu 2.000000 Raport beneficiu/greutate 2.000000 Beneficiul total este 28.000000

12.10. Metoda programării dinamice Metoda programării dinamice se utilizează la

rezolvarea unor probleme de optimizare care se referă la un proces. Procesul parcurge stările 0 1, , , ns s sK , 0s este starea iniţială, ns este starea finală. La fiecare trecere din starea is în starea 1is + se ia decizia 1id + , 0,1, , 1i n= −K pentru a se realiza acest lucru. La fiecare pas i , decizia

1id + poate fi aleasă din mai multe decizii posibile, cea care se ia trebuie să fie optimă. În general deciziile id care conduc la soluţia problemei, trebuie să fie optime satisfăcând principiul optimalităţii al lui R.Bellmann. Acest principiu spune că, dacă stărilor 0 1, , , ns s sK le corespund deciziile 1 2, , , nd d dK optime, atunci, oricare ar fi i , la şirul de stări 0 1, , , is s sK le corespund aceleaşi decizii optime

1 2, , , id d dK iar la şirul de stări 1, , ,i i ns s s+ K corespund aceleaşi decizii optime 1 2, , ,i i nd d d+ + K .

Dacă principiul optimalităţii este satisfăcut, metoda programării dinamice presupune scrierea unei relaţii de recurenţă pentru decizia de la pasul i . În general, relaţiile de recurenţă sunt de două tipuri:

( )1 2 1, , ,i id f d d d −= K cunoscută sub numele de relaţia retrospectivă sau metoda înapoi, în care funcţia f determină decizia de la pasul 1i − ţinând cont de deciziile

Page 178: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

351

luate anterior; ( )1 2, , ,i i i nd f d d d+ += K cunoscută sub

numele de relaţia prospectivă sau metoda înainte, în care funcţia f determină decizia de la pasul 1i − ţinând cont de viitorul procesului.

Nu există criterii pe baza cărora să se poată decide aplicarea metodei programării dinamice unei probleme date. putem formula două proprietăţi care sugerează o soluţie prin programare dinamică. Există două proprietăţi ale problemei de rezolvat care recomandă aplicarea metodei programării dinamice. Problema de rezolvat se poate descompune în subprobleme şi soluţia optimă a problemei derivă din soluţiile optime ale subproblemelor sale. Această proprietate poate să conducă fie la metoda metoda Divide et Impera, fie la metoda Greedy. A doua proprietate a problemei de rezolvat constă în posibilitatea suprapunerii subproblemelor, ceea ce elimină ideea metodei Divide et Impera datorită creşterii timpului de execuţie al programului, prin execuţia repetată a codului asociat unor subprobleme. Prin metoda programării dinamice fiecare subproblemă se rezolvă o singură dată.

Rezolvarea unei probleme cu metoda programării dinamice presupune parcurgerea următoarelor etape: - identificarea subproblemele problemei date; - definirea unei structuri de date, care să memoreze soluţiile subproblemelor; - definirea unei relaţii de recurenţă care să caracterizeze substructura optimală a problemei; - implementarea relaţiei de recurenţă.

Metode de programare

352

12.11. Exemple Vom exemplifica aplicarea metodei programării

dinamice unor probleme clasice. 132. Problema înmulţirii optime a matricilor.

Fie n matrici 0 1 1 2 1

, , ,n np p p p p pA A A

−× × ×K . Deoarece

înmulţirea matricilor este asociativă, produsul 0 1 1 2 1n np p p p p pA A A

−× × ×⋅ L poate fi calculat în mai multe moduri.

Se pune problema determinării acelei asocieri a factorilor pentru care produsul celor n matrici se poate efectua cu un număr minim de înmulţiri elementare. Este cunoscut faptul că numărul de înmuţiri necesare efectării produsului a două matrice p q q rM M× ×⋅ este egal cu p q r⋅ ⋅ . De exemplu, pentru matricele 2 2 2 3 3 4, ,A A A× × × , produsul

2 2 2 3 3 4A A A× × ×⋅ ⋅ se poate calcula folosind asocierea ( )2 2 2 3 3 4A A A× × ×⋅ ⋅ , care necesită 2 2 3 2 3 4 36⋅ ⋅ + ⋅ ⋅ = înmulţiri

elementare. Folosind asocierea ( )2 2 2 3 3 4A A A× × ×⋅ ⋅ sunt necesare 2 3 4 2 2 4 40⋅ ⋅ + ⋅ ⋅ = înmulţiri elementare. Calculul produsului

0 1 1 2 1n np p p p p pA A A−× × ×⋅ L , se reduce la

calculul unui produs de forma ( ) ( )0 1 1 2 1 1 1 2 1k k k k k k n np p p p p p p p p p p pA A A A A A

− + + + −× × × × × ×⋅ ⋅ ⋅L L , ceea ce

este adevărat şi pentru produsele din cele două paranteze. Astfel, rezolvarea problemei se reduce la rezolvarea subproblemelor de forma

1 1i i j jp p p pA A+ +× ×L cu

1 i j n≤ ≤ ≤ . Subproblemele nu sunt independente deoarece produsele

1 1i i j jp p p pA A+ +× ×L şi

1 2 1 2i i j jp p p pA A+ + + +× ×L au

în comun produsul 1 2 1i i j jp p p pA A

+ + +× ×L . Vom defini matricea soluţie sol , cu elementele [ ][ ],1sol i j i j n≤ ≤ ≤ în care

Page 179: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

353

[ ][ ]sol i j este numărul minim de înmulţiri elementare necesare calculului produsului

1 1i i j jp p p pA A− −× ×L . Evident

[1][ ]sol n reprezintă numărul minim de înmulţiri elementare necesare calculului produsului

0 1 1 2 1, , ,

n np p p p p pA A A−× × ×K . Pentru ca asocierea factorilor

produsului 0 1 1 2 1n np p p p p pA A A

−× × ×⋅ L să fie optimă este necesar

ca asocierea factorilor produselor ( ) ( )0 1 1 2 1 1 1 2 1k k k k k k n np p p p p p p p p p p pA A A A A A

− + + + −× × × × × ×⋅ ⋅ ⋅L L

0 1 1 2 1k kp p p p p pA A A−× × ×⋅ L şi

1 1 2 1k k k k n np p p p p pA A A+ + + −× × ×⋅ L să fie

optimă. Pentru a determina numărul minim de înmulţiri elementare pentru calculul produsului

1 1i i j jp p p pA A− −× ×L se

fixează poziţia parantezei k în toate modurile, 1i k j≤ ≤ − şi se alege varianta care conduce la numărul minim de înmulţiri. Pentru k fixat, numărul de înmulţiri este egal cu numărul de înmulţiri necesare efectuării produsului

1 1i i k kp p p pA A− −× ×L , egal cu [ ][ ]sol i k , adunat cu numărul de

înmulţiri necesare efectuării produsului 1 1k k j jp p p pA A

+ −× ×L , egal cu [ 1][ ]sol k j+ , la care trebuie adunat numărul de înmulţiri necesare efectuării produsului celor două matrice rezultate, egal cu 1i k jp p p− ⋅ ⋅ . Rezultă că elementele matricei sol trebuie să satisfacă relaţia de recurenţă:

{ }[ ][ ] min [ ][ ] [ 1][ ] [ 1]* [ ]* [ ]i k j

sol i j sol i k sol k j p i p k p j≤ <

= + + + −

şi ( )[ ][ ] 0 1,2, ,sol i i i n= ∀ = K .

În program sunt definite funcţiile prodopt care rezolvă relaţia de recurenţă şi funcţia afis care afişează asocierea optimă a factorilor produsului

Metode de programare

354

1 2 2 3 1, , ,

n np p p p p pA A A+× × ×K . În funcţia main se tipăreşte

numărul minim de înmulţiri elementare. Programul primeşte la intrare numărul de matrici şi pentru fiecare matrice numărul de linii şi coloane. Dacă condiţia de înlănţuire nu este satisfăcută se semnalează eroare. Programul afişează asocierea optimă a factorilor produsului şi numărul minim de înmulţiri elementare. // Inmultirea optima a unui sir de matrice #include "stdio.h" #include "conio.h" #include "limits.h" long int sol[100][100]; int n,p[100],lc; void prodopt() { int nm, i, j, k, kminim; long int min; for (nm=2; nm<=n; nm++) for (i=1; i<=n-nm+1; i++) { j=i+nm-1; min=INT_MAX; for (k=i; k<j; k++) if (min>sol[i][k]+sol[k+1][j]+p[i-1]*p[k]*p[j]) { min=sol[i][k]+sol[k+1][j]+p[i-1]*p[k]*p[j]; kminim=k; } sol[i][j]=min; sol[j][i]=kminim; } } void afis(int i, int j) { if (i==sol[j][i]) printf("A%d * ",i); else {

Page 180: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

355

printf("("); afis(i,sol[j][i]); printf(") * "); } getch(); if (j==sol[j][i]+1) printf("A%d",j); else { printf("("); afis(sol[j][i]+1,j); printf(")"); } } int main() { int m,sw; int lin[100],col[100]; printf(" Numar de matrici : "); scanf("%d",&n); for(m=1;m<=n;m++) { sw=1; while(sw) { printf(" Matricea %d \n\n",m); printf(" Numrul de linii ale matricei %d : ",m); scanf("%d",&lin[m]); if(col[m-1]!=lin[m] && m>1) printf(" Conditia de inlantuire nu este satisfacuta \n"); else { sw=0; printf(" Numrul de coloane ale matricei %d : ",m); scanf("%d",&col[m]); }}} for(m=1,p[0]=lin[1];m<=n;p[m]=col[m],m++); prodopt(); printf("\n\n Asocierea optima a factorilor produsului este: \n\n");

Metode de programare

356

afis(1,n); printf("\n\n Numarul minim de inmultiri este egal cu %d \n",sol[1][n]); getch(); }

Un prim exemplu de rulare este pentru exemplul tratat mai sus: determinarea asocierii optime a factorilor produsului 2 2 2 3 3 4A A A× × ×⋅ ⋅ . Pentru asocierea

( )2 2 2 3 3 4A A A× × ×⋅ ⋅ am obţinut numărul minim de înmulţiri

elementare egal cu 36. Numar de matrici : 3 Matricea 1 Numrul de linii ale matricei 1 : 2 Numrul de coloane ale matricei 1 : 2 Matricea 2 Numrul de linii ale matricei 2 : 2 Numrul de coloane ale matricei 2 : 3 Matricea 3 Numrul de linii ale matricei 3 : 5 Conditia de inlantuire nu este satisfacuta Matricea 3 Numrul de linii ale matricei 3 : 3 Numrul de coloane ale matricei 3 : 4 Asocierea optima a factorilor produsului este: (A1 * A2) * A3 Numarul minim de inmultiri este egal cu 36

Următorul exemplu de rulare este pentru determinarea asocierii optime a factorilor produsului a 5 matrici 3 4 4 5 5 2 2 3 3 5A A A A A× × × × ×⋅ ⋅ ⋅ ⋅ .

Page 181: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

357

Numar de matrici : 5 Matricea 1 Numrul de linii ale matricei 1 : 3 Numrul de coloane ale matricei 1 : 4 Matricea 2 Numrul de linii ale matricei 2 : 1 Conditia de inlantuire nu este satisfacuta Matricea 2 Numrul de linii ale matricei 2 : 4 Numrul de coloane ale matricei 2 : 5 Matricea 3 Numrul de linii ale matricei 3 : 2 Conditia de inlantuire nu este satisfacuta Matricea 3 Numrul de linii ale matricei 3 : 5 Numrul de coloane ale matricei 3 : 2 Matricea 4 Numrul de linii ale matricei 4 : 2 Numrul de coloane ale matricei 4 : 3 Matricea 5 Numrul de linii ale matricei 5 : 3 Numrul de coloane ale matricei 5 : 5 Asocierea optima a factorilor produsului este: (A1 * (A2 * A3)) * (A4 * A5) Numarul minim de inmultiri este egal cu 124

133. Problema determinării celui mai lung subşir comun a două şiruri.

Metode de programare

358

Un subşir al unui şir dat se obţine din şirul dat prin eliminarea unor termeni sau a nici unui termen. Fiind dat şirul ( )1 2, , , nX x x x= K , şirul ( )1 2, , , kZ z z z= K este

subşir al şirului X dacă există un şir strict crescător de indici din X , ( )1 2, , , ki i iK , astfel încât, pentru orice

1,2, ,j k= K , are loc egalitatea ji jx z= . Considerăm

şirurile de numere reale ( )1i i nX x

≤ ≤= şi ( )1j j m

Y y≤ ≤

= .

Problema constă în determinarea celui mai lung subşir comun al celor două şiruri. De exemplu pentru şirurile

( )1.1, 2.3,2.5,8., 7.,3.2X = − − şi

( )9.8, 5.6,1.1, 2.3,2.5, 8., 7.,3.2Y = − − − − există subşirurile

comune ( )1.1, 2.3,2.5− , ( )2.3,2.5, 7.,3.2− − ,

( )1.1, 2.3,2.5, 7.,3.2− − , ( )7.,3.2− . Cel mai lung subşir comun este ( )1.1, 2.3,2.5, 7.,3.2− − şi are lungimea 5. Fie

( )1 2, , ,k kX x x x= K subşirul primilor k termeni al lui X ,

numit prefixul de ordin k al lui X , ( )1 2, , ,p pY y y y= K prefixul de ordinul p al şirului Y . Determinarea subşirului comun de lungime maximă al prefixelor kX şi

pY este o subproblemă. Fie ( ),k pl X Y lungimea maximă a subşirurilor comune prefixelor kX şi pY . Problema iniţială se reduce la determinarea valorii ( ),n ml X Y şi a subşirului

comun corespunzător. Evident are loc egalitatea:

( )( )

( ) ( ){ }1 1

1 1

,,

max , , ,

k p k p

k p

k p k p k p

l X Y daca X Yl X Y

l X Y l X Y daca X Y

− −

− −

== ≠

.

Page 182: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

359

Definim matricea ( ) ( )1 1n ml + × + , în care [ ][ ]l k p este

lungimea celui mai lung subşir comun al prefixelor kX şi

pY . Elementele matricei l verifică relaţiile:

( )[ ][0] 0 1,2, ,l k k n= ∀ = K , ( )[0][ ] 0 1,2, ,l p p m= ∀ = K ,

{ }1 [ 1][ 1] [ ] [ ]

[ ][ ]max [ ][ 1], [ 1][ ] [ ] [ ]

l k p daca x k y pl k p

l k p l k p daca x k y p+ − − =

= − − ≠.

Programul primeşte la intrare dimensiunile şi termenii celor două şiruri. Se afişează, dacă există, lungimea şi termenii subşirului comun maximal al celor două şiruri. Funcţia sir implementează relaţiile de recurenţă. // Cel mai lung subsir comun si lungimea lui #include "stdio.h" #include "conio.h" float x[50],y[50],l[50][50],subsir[50]; int k,p; void sir(int n,int m) { for(k=1;k<=n;l[k][0]=0,k++); for(p=1;p<=m;l[0][p]=0,p++); for (k=1; k<=n; k++) for (p=1; p<=m; p++) if (x[k]==y[p]) l[k][p]=1+l[k-1][p-1]; else l[k][p]=(l[k-1][p]>l[k][p-1]) ? l[k-1][p] : l[k][p-1]; } int main() { int n,m,i; printf(" Numarul de termeni ai sirului X : "); scanf("%d",&n); printf(" Termenii sirului X \n"); for(i=1;i<=n;i++) {

Metode de programare

360

printf(" x [ %d ] = ",i); scanf("%f",&x[i]); } printf(" Numarul de termeni ai sirului Y : "); scanf("%d",&m); printf(" Termenii sirului Y \n"); for(i=1;i<=m;i++) { printf(" y [ %d ] = ",i); scanf("%f",&y[i]); } sir(n,m); printf(" Lungimea subsirului comun maximal este %d \n",(int)l[n][m]); if(l[n][m]) { printf(" Cel mai lung subsir comun este: \n"); for (i=0,k=n,p=m;l[k][p];) if (x[k]==y[p]) { subsir[i++]=x[k]; k--; p--; } else if (l[k][p]==l[k-1][p]) k--; else p--; for (k=0;k<=i-1; k++) printf(" %f ",subsir[k]); } else printf(" Nu exista subsiruri comune \n"); getch(); }

Pentru şirurile ( )1.1, 2.3,2.5,8., 7.,3.2X = − − şi

( )9.8, 5.6,1.1, 2.3,2.5, 8., 7.,3.2Y = − − − − programul

furnizează: Numarul de termeni ai sirului X : 6

Page 183: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

361

Termenii sirului X x [ 1 ] = 1.1 x [ 2 ] = -2.3 x [ 3 ] = 2.5 x [ 4 ] = 8. x [ 5 ] = -7. x [ 6 ] = 3.2 Numarul de termeni ai sirului Y : 8 Termenii sirului Y y [ 1 ] = 9.8 y [ 2 ] = -5.6 y [ 3 ] = 1.1 y [ 4 ] = -2.3 y [ 5 ] = 2.5 y [ 6 ] = -8. y [ 7 ] = -7. y [ 8 ] = 3.2 Lungimea subsirului comun maximal este 5 Cel mai lung subsir comun este: 3.200000 -7.000000 2.500000 -2.300000 1.100000

Pentru două şiruri disjuncte, care nu au subşiruri comune, programul furnizează rezultatele: Numarul de termeni ai sirului X : 3 Termenii sirului X x [ 1 ] = 1. x [ 2 ] = 2. x [ 3 ] = 3. Numarul de termeni ai sirului Y : 4 Termenii sirului Y y [ 1 ] = 5. y [ 2 ] = 6. y [ 3 ] = 7. y [ 4 ] = 8. Lungimea subsirului comun maximal este 0 Nu exista subsiruri comune

Metode de programare

362

134. Rezolvăm problema 94 pentru cazul şirurilor de caractere. Cu mici modificări care se impun, referitoare la lucrul cu caractere, programul este: // Cel mai lung subsir comun si lungimea lui #include "stdio.h" #include "conio.h" int l[50][50]; char x[50],y[50],subsir[50]; int k,p; void sir(int n,int m) { for(k=1;k<=n;l[k][0]=0,k++); for(p=1;p<=m;l[0][p]=0,p++); for (k=1; k<=n; k++) for (p=1; p<=m; p++) if (x[k]==y[p]) l[k][p]=1+l[k-1][p-1]; else l[k][p]=(l[k-1][p]>l[k][p-1]) ? l[k-1][p] : l[k][p-1]; } int main() { int n,m,i; printf(" Numarul de termeni ai sirului X : "); scanf("%d",&n); printf(" Termenii sirului X \n"); for(i=1;i<=n;i++) { printf(" x [ %d ] = ",i); x[i]=getche(); printf("\n"); } printf(" Numarul de termeni ai sirului Y : "); scanf("%d",&m); printf(" Termenii sirului Y \n"); for(i=1;i<=m;i++) { printf(" y [ %d ] = ",i); y[i]=getche(); printf("\n"); }

Page 184: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

363

sir(n,m); printf(" Lungimea subsirului comun maximal este %d \n",(int)l[n][m]); if(l[n][m]) { printf(" Cel mai lung subsir comun este: \n"); for (i=0,k=n,p=m;l[k][p];) if (x[k]==y[p]) { subsir[i++]=x[k]; k--; p--; } else if (l[k][p]==l[k-1][p]) k--; else p--; for (k=0;k<=i-1; k++) printf(" %c ",subsir[k]); } else printf(" Nu exista subsiruri comune \n"); getch(); }

Rezultatele pentru cazul în care ( ), , ,X a b c d= şi

( ), , , ,Y w a s c d= sunt:

Numarul de termeni ai sirului X : 4 Termenii sirului X x [ 1 ] = a x [ 2 ] = b x [ 3 ] = c x [ 4 ] = d Numarul de termeni ai sirului Y : 5 Termenii sirului Y y [ 1 ] = w y [ 2 ] = a y [ 3 ] = s y [ 4 ] = c y [ 5 ] = d

Metode de programare

364

Lungimea subsirului comun maximal este 3 Cel mai lung subsir comun este: d c a

Page 185: Bazele_programarii_calculatoarelor

365

111333... ÎÎÎNNNTTTRRREEEBBBĂĂĂRRRIII DDDEEE AAAUUUTTTOOOEEEVVVAAALLLUUUAAARRREEE

13.1. Unităţi lexicale 1. Care comentariu nu este corect? a. // un comentariu b. /* un comentariu */ c. // un comentariu // d. /* un comentariu e. //* un comentariu 2. Care element nu este un tip de dată primar în limbajul

C? a. int b. float c. real d. char e. double 3. Care element nu este un cuvânt cheie al limbajului C? a. while b. for c. if d. main e. short

Întrebări de autoevaluare

366

4. Care element nu poate fi identificator într-un program C?

a. flota b. Float c. _float d. float e. float_

13.2. Expresii. Operanzi. Operatori 5. Indicaţi valoarea lui x in urma instrucţiunilor: short int a=10, x; x = sizeof(25.-a);

a. 15 (adică 25 -10) b. 25 (adică valoarea cea mai mare) c. 4 sau 8 (depinde de implementarea tipului double) d. 2 (adică sizeof(a), a fiind singura variabila din expresie) e. eroare (operatorul sizeof nu se aplica unei expresii)

6. Care este valoarea lui a după secvenţa următoare? int a = 4; a==-5+2;

a. secvenţa conţine o eroare b. a are valoarea 4 c. a are valoarea -3 d. a are valoarea 7 e. a are valoarea -7 7. Indicaţi valoarea lui x in urma instrucţiunilor: x=0; z=1; x=x==(y=!z);

Page 186: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

367

a. 0 b. eroare deoarece operatorul de inegalitate (!=) este scris invers c. 1 d. alta valoare e. rezultatul depinde de valoarea lui y 8. Indicaţi ce afişează următoarele instrucţiuni: int a=3, b=5; printf (a=b ? "egale" : "diferite");

a. diferite b. egale c. instrucţiunile conţin erori de sintaxa d. true e. false

9. Indicaţi valoarea lui x in urma instrucţiunilor: int x=0, y=1, z=0; x=x||!y&&z;

a. expresie eronata neadmisă în limbaj b. 0 c. 1 d. 2 e. nici una din variante

10. Indicaţi valoarea lui x in urma instrucţiunilor: int x=2, y=1, z; x=x&&y||z;

a. depinde de valoarea iniţială a lui z b. 1 c. 0 d. false

Întrebări de autoevaluare

368

e. alta valoare 11. Care este valoarea expresiei 6 & 8? a. 4 b. 5 c. 1 d. 3 e. 0 12. Indicaţi valorile lui t, x si y in urma instrucţiunilor: int t, x=2, y=3; t=++x<y++?++x:y++;

a. 1, 2, 2 b. 2, 2, 2 c. 1, 2, 3 d. 2, 2, 3 e. 4, 3, 5 13. Indicaţi ce afişează următoarele instrucţiuni: int a=3, b=7%4; printf(a==b ? "egale" : "diferite");

a. instrucţiunile conţin erori de sintaxa b. true c. egale d. diferite e. false

13.3. Instrucţiuni 14. Ce afişează secvenţa de cod următoare? int i, j; for(i=1,j=1;i<4; j=++i) {

Page 187: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

369

int k=1; printf("%d%d%d", i, j, k); k++; }

a. 111222333 b. 111122133 c. 111221331 d. 111211311 e. 111111111 15. Ce afişează secvenţa de cod următoare? int i, j; for(i=1,j=1;i<4; i++) { int k=1; printf("%d%d%d", i, j, k); j++;k++; }

a. 111221331 b. 111222333 c. 111111111 d. 111211311 e. 111122133

16. Ce afişează secvenţa de cod următoare? int i=3; while(--i); printf("%d",i);

a. 2 1 0 b. 3 2 1 c. 0 3 2 2 1

Întrebări de autoevaluare

370

17. Ce afişează secvenţa de cod următoare? int i; for(i=19;i>0;i%7) printf("%d", i);

a. 191919191919... (19 la infinit) b. 195 c. 197 d. 19 e. altceva

13.4. Funcţii standard de intrare/ieşire 18. În ce bibliotecă se găsesc funcţiile standard de

intrare/ieşire? a. conio.h b. stdlib.h c. math.h d. stdio.h e. string.h 19. Ce afişează următoarea secvenţă de program? printf("%03c %#x %o %.0s", '9', 9, 9, "9");

a. 009 0x9 9 b. 009 0x9 11 c. 009 0x9 11 9 d. 009 0x9 9 9 e. 9 9 9 9 20. Ce afişează următoarea secvenţă de program dacă

datele introduse de la tastatură sunt Decembrie 2006:

int a; char x[10];

Page 188: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

371

scanf("%s%2d", x, &a); printf("%d, %.3s", a, x);

a. 20, Dec b. 2006, Dec c. 2006, Decembrie d. 2006 Decembrie e. nimic

13.5. Tipuri de date compuse 21. Care afirmaţie este corecta referitor la iniţializarea: char mesaj[]={'G', 'r', 'e', 's', 'i', 't', 0};

a. iniţializarea este corecta b. iniţializarea nu este permisa in cazul vectorilor de caractere c. iniţializarea este eronata (lipseşte dimensiunea vectorului) d. iniţializarea este eronata (0 nu este scris intre apostrofi) e. iniţializarea este eronata (trebuiau scrise ghilimele in loc de apostrofi) 22. Câte elemente are vectorul declarat în secvenţa

următoare: double v[30] = {1, 5, 18};

a. 30 de elemente b. 3 elemente c. 4 elemente d. 0 elemente

Întrebări de autoevaluare

372

e. secvenţa nu este corectă 23. Care instrucţiunea afişează numele coloanei a 3-a din

linia a 6-lea? struct { struct { char nume[20]; int latime; } coloane[5]; } linii[10];

a. printf("Nume: %s", linii[5].coloane[2].nume); b. printf("Nume: %s", linii[6].coloane[3].nume); c. printf("Nume: %s", linii[5].coloane[3].nume); d. printf("Nume: %s", linii[6].coloane[2].nume); e. printf("Nume linii[6].coloane[3].nume");

13.6. Funcţii 24. Care este valoarea expresiei f(21)? int f(int n) { if (n<3) return n; else return f(n/2); }

a. 1 b. 2 c. 3 d. 0 e. 5 25. Care este valoarea expresiei f(5)?: int f(int n)

Page 189: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

373

{ if (n<2) return 1; else return n * f(n-1); }

a. 120 b. 5 c. 1 d. 6 e. 12345 26. Care este valoarea expresiei f(2,5)? int f(int n, int m) { if (m<1) return 1; else return n * f(n, m-1); }

a. 2 b. 5 c. 32 d. 1 e. 0 27. Care este valoarea expresiei f(5)? int f(int n) { if (n<2) return 1; else return n / f(n-1); }

a. 1 b. 5 c. 0 d. 1.2 e. 15

Întrebări de autoevaluare

374

28. Care este valoarea expresiei f(5,0)? int f(int n, int m) { if (n<0) return f(n+1, m+n); else if (n>0) return f(n-1, m+n); else return m; }

a.15 b. 5 c. 0 d. -5 e. 12345 29. Care este valoarea expresiei f(6)? int f(int n) { if (n<2) return 1; else return n / f(n-1); }

a. 1 b. 5 c. 0 d. 1.2 e. 6

13.7. Pointeri şi gestiunea dinamică a memoriei

30. În ce bibliotecă se găseşte funcţia realloc? a. conio.h b. realloc.h c. malloc.h d. stdio.h

Page 190: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

375

e. string.h 31. Care instrucţiune afişează numele coloanei a 3-a din

linia indicată prin pointerul linie? struct { struct { char nume[20]; int latime; } coloane[5]; } *linie;

a. printf("Nume: %s", linie->coloane[2].nume); b. printf("Nume: %s", linie.coloane[3].nume); c. printf("Nume: %s", linie->coloane->nume); d. printf("Nume: %s", linie.coloane->nume); e. printf("Nume linie->coloane->nume"); 32. Care sunt valorile din vectorul v după secvenţa de

program: int v[] = {3, 4, 5}, *p; p = v+1; *p = 6; *p++ = 2;

a. 3 6 2 b. 3 2 5 c. 6 5 5 d. 3 7 5 e. 3 7 2

13.8. Funcţii pentru şiruri de caractere 33. Precizaţi valoarea expresiei strlen(test) dacă variabila test este declarata astfel: char test[]="";

Întrebări de autoevaluare

376

a. 1, deoarece se numără caracterele şi terminatorul de sir b. 0, deoarece se numără caracterele fără terminatorul de sir c. -1, deoarece nu e precizata dimensiunea şirului de caractere d. 2 sau 4, deoarece variabila test este un pointer e. alta valoare decât cele de la celelalte răspunsuri 34. Ce afişează secvenţa de program: char s[] = "out.txt"; printf("%s", strrchr(s,'t'));

a. out.txt b. txt c. out. d. t e. fişierul este gol 35. Precizaţi valoarea expresiei strlen(test) dacă variabila test este declarata astfel: char test[100]="Examen";

a. 7, deoarece se numără caracterele şi terminatorul de sir b. 6, deoarece se numără caracterele fără terminatorul de sir c. 100, deoarece aceasta este dimensiunea vectorului d. 2 sau 4, deoarece variabila test este un pointer e. alta valoare decât cele de la celelalte răspunsuri

36. Care afirmaţie este corectă referitor la secvenţa de program:

Page 191: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

377

char s1[100]="daca", s2[50]="inveti", s3[]="raspunzi"; strcat(s2, s3); strcat(s1, s2); puts(s1);

a. afişează daca înveţi răspunzi b. afişează răspunzi daca înveţi c. este greşită (prin concatenare se suprascriu zone de memorie nealocate) d. este greşită (vectorii de caractere nu pot fi folosiţi ca şiruri de caractere) e. afişează daca răspunzi înveţi 37. Indicaţi valoarea returnată in urma apelului: strcmp("ghicitoare", "ghici");

a. o valoare pozitiva b. o valoare negativa c. zero d. apel eronat deoarece compara doua constante e. apel eronat deoarece compara doua adrese

13.9. Funcţii matematice 38. Ce funcţie nu face parte din biblioteca math.h? a. sin b. fabs c. strlen d. pow e. sqrt

39. Ce constantă predefinită are valoarea numărului π ? a. PI

Întrebări de autoevaluare

378

b. M_PI c. pi d. m_pi e. Pi

13.10. Funcţii pentru gestiunea fişierelor 40. Ce conţine fişierul out.txt după execuţia secvenţei de

cod: char s[] = "out.txt"; FILE *fout = fopen(s, "wt"); fprintf(fout, "", strlen(s)); fclose(fout);

a. 7 b. 5 c. 123 d. 0 e. fişierul este gol 41. Ce conţine fişierul out.txt după execuţia secvenţei de cod: char s[] = "out.txt"; FILE *fout = fopen(s, "wt"); fprintf(fout, "%d", strlen(s)); fclose(fout);

a. 7 b. 5 c. 123 d. 0 e. fişierul este gol 42. Ce conţine fişierul out.txt după execuţia secvenţei de

cod:

Page 192: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

379

char s[] = "out.txt"; FILE *fout = fopen(s, "wt"); fprintf(fout, s); fclose(fout);

a. out.txt b. t.txt c. txt.tuo d. t e. fişierul este gol 43. In ce biblioteca este declarata fprintf? a. file.h b. stdio.h c. strlib.h d. math.h e. string.h

44. Care instrucţiune închide fişierul asociat variabilei fis? a. fclose(fis); b. close(fis) c. fclose fis; d. close fis; e. *fis = fclose;

Tabel 13.1 Răspunsuri la întrebări

Î R Î R Î R Î R Î R 1 d 2 c 3 d 4 d 5 c 6 b 7 c 8 b 9 b 10 b 11 e 12 e 13 c 14 c 15 a 16 c 17 a 18 d 19 b 20 a 21 a 22 a 23 a 24 b 25 a 26 c 27 a 28 a 29 e 30 c

Întrebări de autoevaluare

380

31 a 32 b 33 b 34 d 35 b 36 a 37 a 38 c 39 b 40 e 41 a 42 a 43 b 44 a

Page 193: Bazele_programarii_calculatoarelor

381

111444... AAANNNEEEXXXAAA 111 ––– MMMEEEDDDIIIUUULLL DDDEEE PPPRRROOOGGGRRRAAAMMMAAARRREEE DDDEEEVVV---CCC++++++

Dev-C++, realizat de Bloodshed Software, este un mediu integrat de dezvoltare pentru limbajul C/C++ pentru Windows, gratuit şi complet. Un mediu integrat de dezvoltare este un pachet de programe care permit editare, compilarea, executarea şi verificarea programelor direct din programul principal.

Dev-C++ foloseşte varianta Mingw a compilatorului GCC (GNU Compiler Collection), recunoscut pentru calităţile sale. Printre calităţile mediului Dev-C++ se numără:

• funcţii avansate de editare, căutare şi înlocuire, tipărire, CVS (Concurrent Versioning System), listă „To Do”, etc;

• interfaţă în diverse limbi (printre care şi limba română);

• colorarea şi completarea codului; • depanator de programe integrat; • crearea rapidă a programelor Windows (cu sau

fără interfaţă grafică) şi a bibliotecilor (statice şi dinamice);

Anexa 1 – Mediul de programare Dev-C++

382

• manager de instrumente (pentru integrarea altor unelte de dezvoltare în mediul Dev-C++);

• manager de proiecte (pentru gestionarea uşoară a proiectelor medii şi mari);

• manager pe pachete (pentru instalarea uşoara a unor module suplimentare).

14.1. Instalare Programul de instalare poate fi descărcat gratuit

de pe situl Bloodshed (la adresa http://www.bloodshed.net) sau de pe SourceForge (la adresa http://sourceforge.net/projects/dev-cpp). Vă recomandăm să descărcaţi programul de instalare care include compilatorul Mingw, instalarea fiind mai simplă.

După descărcare porniţi programul de instalare şi urmaţi paşii indicaţi de acesta. Noi vom discuta etapele necesare pentru Dev-C++ versiunea 4.9.9.2. La pornirea programului de instalare, acesta vă cere să dezinstalaţi eventuale versiuni mai vechi ale sale, după care vă cere să selectaţi limba în care se va desfăşura procesul de instalare. Selectaţi limba română.

Următoarea fereastră vă afişează contractul de

licenţă GNU, care vă dă dreptul să folosiţi şi să distribuiţi

Page 194: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

383

gratis programul. Apăsaţi butonul „De acord”. Urmează etapa de selectare a componentelor. Setările implicite sunt corecte, aşa că puteţi apăsa pe butonul „Înainte >”.

În continuare, trebuie să selectaţi folderul unde va fi instalat programul. În majoritatea cazurilor, valoarea implicită este bună, deci apăsaţi butonul „Instalare”. Următoarea fereastră afişează operaţiile efectuate în timpul instalării. Dacă apar erori, porniţi din nou instalarea şi selectaţi un alt folder la pasul anterior. Dacă nu apar erori, programul de instalare vă întreabă dacă doriţi ca programul să fie accesibil tuturor utilizatorilor calculatorului. Răspunsul depinde de dumneavoastră.

Anexa 1 – Mediul de programare Dev-C++

384

Ultima fereastră vă informează că instalarea s-a încheiat cu succes şi vă oferă posibilitatea de a porni programul.

14.2. Configurare

14.2.1. Prima pornire La prima pornire, programul trebuie configurat

pentru a satisface preferinţele dumneavoastră. Vom folosi limba engleză, deoarece opţiunile mediului Dev-C++ seamănă cu opţiunile din alte medii de programare. Valorile pentru restul opţiunilor depind de dumneavoastră. Apăsaţi pe „Next”.

Următoarea fereastră vă oferă posibilitatea să

activaţi funcţia de completare a codului. Această funcţie poate fi utilă deoarece Dev-C++ afişează prototipul

Page 195: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

385

funcţiilor atunci când consideră necesar, dar consumă memorie suplimentară şi necesită timp de încărcare suplimentar. Vă recomandăm să nu o activaţi.

Ultima fereastră vă anunţă că Dev-C++ a fost

configurat corect şi este gata de lucru. Apăsaţi pe butonul „Ok”.

14.2.2. Editorul În mod implicit, la pornire Dev-C++ afişează

fereastra „Tip of the day” cu sfaturi utile pentru utilizatorii începători. Dacă observaţi că v-aţi însuşit aceste sfaturile, puteţi bifa opţiunea „Don’t display tips at startup”, înainte de a închide fereastra.

Pentru a configura editorul, selectaţi din meniul Tools opţiunea „Editor options”. În secţiunea „General”, efectuaţi următoarele verificări/modificări:

Anexa 1 – Mediul de programare Dev-C++

386

• bifaţi „Auto indent”, astfel programele vor fi mai uşor de urmărit, deoarece instrucţiunile vor fi deplasate automat la stânga, în funcţie de context;

• bifaţi „Use Tab Character”, astfel apăsarea tastei Tab introduce în fişier un caracter tab, în loc de spaţii; setaţi valoarea Tab size la 4;

• debifaţi „Smart tabs”, altfel programele vor fi greu de urmărit, datorită indentării inegale a liniilor;

• debifaţi „Keep Trailing Spaces”, astfel fişierele nu vor conţine spaţii inutile;

• bifaţi „Enhanced Home Key”, astfel prima apăsare a tastei Home, deplasează cursorul la primul caracter afişabil din rând, iar a doua apăsare la începutul rândului. În secţiunea „Display”, puteţi modifica tipul şi

mărimea fontului în funcţie de preferinţele dumneavoastră. Indiferent de mărime, vă recomandăm folosirea unui font proporţional precum Courier New, Lucida Console sau Terminal. De asemenea, bifaţi opţiunea „Line Numbers”.

14.2.3. Tastele rapide Pentru a configura tastele rapide, selectaţi din

meniul „Tools” opţiunea „Configure Shortcuts”. Selectaţi comanda căreia vreţi să-i asociaţi o combinaţie de taste şi apăsaţi tastele respective. De exemplu, pentru comanda „Edit::Date/Time” setaţi combinaţia de taste Ctrl+T. Astfel, când veţi edita un program, apăsând Ctrl+T se va introduce automat data şi ora curentă. Pentru a anula o combinaţie de taste apăsaţi tasta ESC.

Page 196: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

387

14.2.4. Alte instrumente Dev-C++ conţine un manager de instrumente

care permite integrarea altor programe în mediul Dev-C++. Acesta este accesibil din meniul „Tools” opţiunea „Configure Tools”. De exemplu, pentru a căuta pe Google informaţii despre cuvânt din program pe care este plasat cursorul, executaţi următorii paşi:

• apăsaţi pe butonul Add; • introduceţi setările din imaginea următoare. • apăsaţi pe butonul Ok, apoi pe butonul Close

În meniul Tools, apare opţiunea „Cauta pe Google”.

Anexa 1 – Mediul de programare Dev-C++

388

14.3. Utilizare

14.3.1. Scrierea unui program C Realizarea unui program începe cu transcrierea

algoritmului acelui program în limbajul de programare dorit (limbajul C în cazul nostru). În mediul Dev-C++, această activitate începe cu deschiderea unui fişier sursă: din meniul „File”, selectaţi opţiunea „New”, apoi opţiunea „Source File”.

În zona de editare se deschide un fişier nou, denumit Untitled1. În acest fişier trebuie scrise instrucţiunile C care formează programul nostru. Pentru exemplificare, scrieţi următorul program: #include <stdio.h> #include <conio.h> int main()

Page 197: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

389

{ printf("Un mic exemplu"); getch(); }

i Funcţia getch, definită în biblioteca conio.h, suspendă execuţia programului până când utilizatorul apasă o tastă. Dacă această instrucţiune lipseşte, programul se termină imediat după execuţia ultimei instrucţiuni, iar fereastra cu rezultatele dispare.

i Instrucţiunea getch() poate fi înlocuită cu instrucţiunea system("pause"). În acest caz, biblioteca conio.h trebuie înlocuită cu biblioteca stdlib.h.

14.3.2. Compilarea codului sursă La fiecare modificare a programului, acesta

trebuie compilat. Compilarea se realizează cu ajutorul opţiunii „Compile” din meniul „Execute”. Pentru programele noi, înainte de prima compilare, Dev-C++ cere utilizatorului să salveze fişierul pe disc.

În faza de compilare, programul este verificat din punct de vedere sintactic. Dacă sunt detectate erori, atunci acestea sunt afişate în partea inferioară a ferestrei. În caz, contrar pe ecran este afişată o fereastră cu statistici despre program. Apăsaţi butonul „Close”.

14.3.3. Rularea programului Programul poate fi rulat cu ajutorul opţiunii „Run”

din meniul „Execute”. La pornire programul afişează pe ecran o fereastră (dacă fereastra apare şi dispare

Anexa 1 – Mediul de programare Dev-C++

390

imediat, verificaţi existenţa instrucţiunii getch() sau system("pause") la sfârşit). Mediul Dev-C++ nu permite pornirea simultană a două programe. Dacă opţiunile din meniul „Execute” sunt dezactivate, închideţi programul care rulează deja.

i Compilarea şi rularea programului se pot realiza într-un singur pas folosind opţiunea „Compile & Run” din meniul „Execute”.

14.3.4. Depanarea Chiar dacă un program nu conţine erori de

sintaxă (deci a fost compilat cu succes), el poate să conţină erori de programare. Acestea se manifestă prin furnizarea de rezultate incorecte pentru toate sau doar anumite cazuri de test. În această situaţie funcţia de depanare (debugging) a mediului Dev-C++ facilitează detectarea şi eliminarea erorilor.

Pentru depanarea unui program se folosesc opţiunile din meniul „Debug” după etapa de compilare. Programatorul poate selecta opţiunea „Run to cursor” pentru a executa programul până ajunge la linia unde este cursorul editorului. Odată ajuns pe această linie, execuţia programului se suspendă. În acest moment, opţiunea „Next step” execută următoarea instrucţiune, opţiunea „Step into” execută prima instrucţiune din funcţia care urmează, iar opţiunea „Continue” reia execuţia programului.

În timpul depanării, opţiunea „Add Watch” permite monitorizarea şi modificarea valorilor variabilelor. Astfel

Page 198: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

391

programatorul poate să vadă unde apar nepotriviri între program şi algoritm.

Opţiunea „Toogle breakpoint” permite adăugarea şi anularea punctelor de oprire, adică linii din program în care execuţia programului se suspendă, controlul fiind redat din nou programatorului.

În orice moment, depanarea poate fi oprită folosind opţiunea „Stop debugging”.

Page 199: Bazele_programarii_calculatoarelor

393

111555... AAANNNEEEXXXAAA 222 ––– MMMEEEDDDIIIUUULLL DDDEEE PPPRRROOOGGGRRRAAAMMMAAARRREEE RRRAAAPPPTTTOOORRR

Raptor este un acronim pentru Rapid Algorithmic Prototyping Tool for Ordered Reasoning, adică instrument de prototipizare algoritmică rapidă pentru o gândire ordonată.

Raptor este un mediu de programare pe bază de scheme logice, proiectat special pentru a ajuta studenţii să vizualizeze algoritmi lor şi să-i testeze înainte de a îi implementa într-un limbaj de programare textual. Programele Raptor sunt create vizual şi se execută vizual prin parcurgerea stărilor schemei logice. Sintaxa necesară pentru scrierea unui program în Raptor este redusă la minimum.

Raptor a fost dezvoltat în cadrul Departamentului de Ştiinţă a Calculatoarelor de la Academia Forţelor Aeriene a Statelor Unite. El poate fi descărcat gratuit de pe situl web al Academiei Forţelor Aeriene: www.usafa.af.mil/df/dfcs/bios/mcc_html/raptor.cfm sau de pe pagina web personală a principalului autor, Dr. Martin Carlisle: www.martincarlisle.com.

Anexa 2 – Mediul de programare Raptor

394

Instalarea mediului Raptor este un proces simplu datorită utilitarului de instalare şi nu necesită setări suplimentare. Pentru sistemele de operate Windows care nu au instalată platforma .Net, utilitarul de instalare va descărca automat şi va instala această platformă.

După pornirea aplicaţiei, aceasta va deschide în mod automat două ferestre: fereastra principală pentru programare şi fereastra consolă pentru monitorizarea ieşirilor programului. Fereastra principală conţine în parte din stânga cele şase blocuri de programare accesibile în Raptor (majoritatea fiind descrise în primul capitol al cărţii).

Zona principală a ferestrei conţine schema logică

corespunzătoare programului folosit. Butoanele din bara de instrumente din partea superioară a ferestrei permit efectuarea diferitelor operaţii de editare (salvare şi încărcare programe; copiere în/din clipboard; operaţii de

Page 200: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

395

anulare şi refacere a ultimelor operaţii) şi operaţii de programare (pornirea continuă sau pas cu pas, oprirea şi stoparea programului).

În timpul rulării schemei logice, starea activă care se execută va fi marcată printr-un contur cu linie verde îngroşată. Aceasta permite monitorizarea uşoară a execuţiei algoritmului. În plus faţă de acestă opţiune, Raptor aferă multe alte instrumente grafice care uşurează înţelegerea algoritmului executat şi găsirea eventualelor erori:

• viteza de execuţie a algoritmului poate fi controlată cu ajutorul reglabilului disponibil pe bara de instrumente în partea dreaptă;

• butonul Step to next shape permite execuţia pas cu pas a schemei logice şi permite astfel un studiu eficient al efectelor acestuia;

• valorilor variabilelor sunt afişate permanent în timpul execuţiei în partea din stânga jos a ferestrei principale, iar variabilele ale căror valori au fost modificate de algoritm în ultimul pas executat sunt afişate cu culoarea roşie pentru a ieşi în evidenţă;

• din meniul contextual al fiecărui bloc (afişat prin clic dreapta pe blocul respectiv) se pot adăuga comentarii care să ofere explicaţii suplimentare referitoare la acel bloc;

• din meniul contextual al fiecărui bloc se pot adăuga puncte de oprire ale execuţiei (breakpoints) astfel încăt execuţia schemei logice să fie oprită momentan atunci când se ajunge la acestea. Nu în ultimul rând, fereasta consolă este un ajutor

nepreţuit pentru a urmări mesajele tipărite de algoritm

Anexa 2 – Mediul de programare Raptor

396

prin intermediul blocurilor de ieşire (blocuri cu tipul output).

Toate aceste funcţii utile pentru programare sunt

suplimentate de o serie de instrumente şi opţiuni utile pentru prezentarea schemelor logice realizate, cum sunt instrumentul creion de marcare din meniul Ink sau opţiunile de compactare a blocurilor de decizie şi repetiţie din meniul View.

Page 201: Bazele_programarii_calculatoarelor

397

111666... AAANNNEEEXXXAAA 333 ––– SSSEEETTTUUULLL DDDEEE CCCAAARRRAAACCCTTTEEERRREEE AAASSSCCCIIIIII

i Caracterele din setul ASCII cu codurile între 0 şi 31, şi codul 127 (în baza 10) nu au o reprezentare grafică pe calculator. Ele sun denumire caractere de control, fiind folosite iniţial pentru a controla comunicaţia între calculator şi periferice, şi modul de afişare al informaţiei.

Tabel 16.1 Tabela codurilor ASCII

Cod în baza 10 8 16

Caracter (semnificaţie)

0 000 0x00 NUL (Nul character) 1 001 0x01 SOH (Start of Header) 2 002 0x02 STX (Start of Text) 3 003 0x03 ETX (End of Text) 4 004 0x04 EOT (End of Transmission) 5 005 0x05 ENQ (Enquiry) 6 006 0x06 ACK (Acknowledgment) 7 007 0x07 BEL (Bell) 8 010 0x08 BS (Backspace) 9 011 0x09 HT (Horizontal Tab) 10 012 0x0A LF (Line Feed) 11 013 0x0B VT (Vertical Tab)

Anexa 3 – Setul de caractere ASCII

398

Cod în baza 10 8 16

Caracter (semnificaţie)

12 014 0x0C FF (Form Feed) 13 015 0x0D CR (Carriage Return) 14 016 0x0E SO (Shift Out) 15 017 0x0F SI (Shift In) 16 020 0x10 DLE (Data Link Escape) 17 021 0x11 DC1 (XON) (Device Control 1) 18 022 0x12 DC2 (Device Control 2) 19 023 0x13 DC3 (XOFF) (Device Control 3) 20 024 0x14 DC4 (Device Control 4) 21 025 0x15 NAK (Negative Acknowledgement) 22 026 0x16 SYN (Synchronous Idle) 23 027 0x17 ETB (End of Trans. Block) 24 030 0x18 CAN (Cancel) 25 031 0x19 EM (End of Medium) 26 032 0x1A SUB (Substitute) 27 033 0x1B ESC (Escape) 28 034 0x1C FS (File Separator) 29 035 0x1D GS (Group Separator) 30 036 0x1E RS (Request to Send) (Record

Separator) 31 037 0x1F US (Unit Separator) 32 040 0x20 SP (spaţiu) 33 041 0x21 ! 34 042 0x22 " 35 043 0x23 # (diez) 36 044 0x24 $ (dollar) 37 045 0x25 % (procent)

Page 202: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

399

Cod în baza 10 8 16

Caracter (semnificaţie)

38 046 0x26 & (ampersand) 39 047 0x27 ' (apostof) 40 050 0x28 ( 41 051 0x29 ) 42 052 0x2A * (asterisk) 43 053 0x2B + 44 054 0x2C , 45 055 0x2D - 46 056 0x2E . 47 057 0x2F / (slash) 48 060 0x30 0 49 061 0x31 1 50 062 0x32 2 51 063 0x33 3 52 064 0x34 4 53 065 0x35 5 54 066 0x36 6 55 067 0x37 7 56 070 0x38 8 57 071 0x39 9 58 072 0x3A : 59 073 0x3B ; 60 074 0x3C < 61 075 0x3D = 62 076 0x3E > 63 077 0x3F ? 64 0100 0x40 @ (simbol AT)

Anexa 3 – Setul de caractere ASCII

400

Cod în baza 10 8 16

Caracter (semnificaţie)

65 0101 0x41 A 66 0102 0x42 B 67 0103 0x43 C 68 0104 0x44 D 69 0105 0x45 E 70 0106 0x46 F 71 0107 0x47 G 72 0110 0x48 H 73 0111 0x49 I 74 0112 0x4A J 75 0113 0x4B K 76 0114 0x4C L 77 0115 0x4D M 78 0116 0x4E N 79 0117 0x4F O 80 0120 0x50 P 81 0121 0x51 Q 82 0122 0x52 R 83 0123 0x53 S 84 0124 0x54 T 85 0125 0x55 U 86 0126 0x56 V 87 0127 0x57 W 88 0130 0x58 X 89 0131 0x59 Y 90 0132 0x5A Z 91 0133 0x5B [

Page 203: Bazele_programarii_calculatoarelor

A. Băutu, P. Vasiliu – Bazele programării calculatoarelor

401

Cod în baza 10 8 16

Caracter (semnificaţie)

92 0134 0x5C \ (backslash) 93 0135 0x5D ] 94 0136 0x5E ^ (circumflex) 95 0137 0x5F _ (underscore) 96 0140 0x60 ` (apostrof invers) 97 0141 0x61 a 98 0142 0x62 b 99 0143 0x63 c 100 0144 0x64 d 101 0145 0x65 e 102 0146 0x66 f 103 0147 0x67 g 104 0150 0x68 h 105 0151 0x69 i 106 0152 0x6A j 107 0153 0x6B k 108 0154 0x6C l 109 0155 0x6D m 110 0156 0x6E n 111 0157 0x6F o 112 0160 0x70 p 113 0161 0x71 q 114 0162 0x72 r 115 0163 0x73 s 116 0164 0x74 t 117 0165 0x75 u 118 0166 0x76 v

Anexa 3 – Setul de caractere ASCII

402

Cod în baza 10 8 16

Caracter (semnificaţie)

119 0167 0x77 w 120 0170 0x78 x 121 0171 0x79 y 122 0172 0x7A z 123 0173 0x7B { 124 0174 0x7C | (bară verticală) 125 0175 0x7D } 126 0176 0x7E ~ (tildă) 127 0177 0x7F DEL (delete)

Page 204: Bazele_programarii_calculatoarelor

403

BBBIIIBBBLLLIIIOOOGGGRRRAAAFFFIIIEEE

[1] Cormen T.H., Leiserson C.E., Rivest R.R.. – Introducere în algoritmi, Editura Computer Libris Agora, Cluj Napoca, 2000

[2] Cristea V. – Limbajul C Standard, Editura Teora, Bucureşti, 1992

[3] Gheorghe M., Popoviciu I., Chiru C., Vasiliu P., Lupei T. – Limbajul C. Programare prin exemple, Editura Academia Navală „Mircea cel Bătrân”, Constanţa, 2001

[4] Vasiliu P., Băutu A. – Programarea calculatoarelor în limbajul C, Editura Europolis, Constanţa, 2006.

[5] Kernigham B., Ritchie D. – The C programming language, Practice Hall, 1975

[6] Knuth D.E. – Tratat de programarea calculatoarelor, vol.I-II, Editura Tehnică, Bucureşti,1974

[7] Livovschi L., Georgescu H. – Sinteza şi analiza algoritmilor, Editura Ştiinţifică şi Enciclopedică, Bucureşti, 1992

[8] Negrescu L. – Limbajul Turbo C, Editura Libris, Cluj-Napoca, 1992

404

[9] Negrescu L. – Limbajele C şi C++ pentru începători, Editura Albastră, Cluj-Napoca, 1998

[10] Schildt H. – C++ manual complet, Editura Teora, 1997

[11] Vlada M. – Informatică, Editura Ars Docendi, Bucureşti, 1999