limbaje de programare

70
www.cartiaz.ro – Carti si articole online gratuite de la A la Z LIMBAJE DE PROGRAMARE CAPITOLUL 1. ELEMENTE DE LOGICĂ MATEMATICĂ ŞI ALGEBRĂ BOOLEANĂ 1.1. Calculul propoziţiilor 1.1.1. Noţiunea de propoziţie DEFINIŢIA 1.1. Se numeşte propoziţie un enunţ (un ansamblu de cuvinte cărora li s-a dat un sens) despre care se ştie că este sau adevărat sau fals, însă nu simultan şi una şi alta. Exemple de propoziţii: 1) În orice triunghi suma unghiurilor sale este egală cu 180 o ; 2) 3+2=5; 3) 2>5; 4) Balena este un mamifer. 5) Planeta Saturn este satelit al Pamântului. Propoziţiile 1), 2) şi 4) sunt adevărate dar 3) şi 5) sunt false. O clasă foarte largă de propoziţii adevărate o constituie teoremele din matematică. Contraexemple (enunţuri care nu sunt considerate propoziţii): 1) x+2=5; (enunţurile cu variabile sunt predicate) 2) Deschide uşa !; (enunţ imperativ) 3) Numărul x divide numărul y; (predicat) 4) Atomul de aur este galben. (enunţ absurd) 1.1.2. Valoare de adevăr Notăm cu P 2 mulţimea propoziţiilor definite după definiţia 1.1. DEFINIŢIA 1.2. Funcţia v : P 2 {0,1} se numeşte funcţia valoare de adevăr. Fiecărei propoziţii din mulţimea propoziţiilor P 2 i se ataşează valoarea 1 dacă propoziţia este adevărată, şi valoarea 0 dacă este falsă. De obicei se vor nota propoziţiile prin litere mici: p,q,r ... şi cu v(p), v(q), 1

Upload: radu

Post on 13-Jun-2015

7.296 views

Category:

Documents


9 download

DESCRIPTION

LIMBAJE DE PROGRAMARE

TRANSCRIPT

Page 1: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

LIMBAJE DE PROGRAMARE

CAPITOLUL 1.

ELEMENTE DE LOGICĂ MATEMATICĂ ŞI ALGEBRĂ BOOLEANĂ

1.1. Calculul propoziţiilor

1.1.1. Noţiunea de propoziţie

DEFINIŢIA 1.1. Se numeşte propoziţie un enunţ (un ansamblu de cuvinte cărora li s-a dat un sens) despre care se ştie că este sau adevărat sau fals, însă nu simultan şi una şi alta.

Exemple de propoziţii:1) În orice triunghi suma unghiurilor sale este egală cu 180o;2) 3+2=5;3) 2>5;4) Balena este un mamifer.5) Planeta Saturn este satelit al Pamântului.

Propoziţiile 1), 2) şi 4) sunt adevărate dar 3) şi 5) sunt false.

O clasă foarte largă de propoziţii adevărate o constituie teoremele din matematică.

Contraexemple (enunţuri care nu sunt considerate propoziţii):1) x+2=5; (enunţurile cu variabile sunt predicate)2) Deschide uşa !; (enunţ imperativ)3) Numărul x divide numărul y; (predicat)4) Atomul de aur este galben. (enunţ absurd)

1.1.2. Valoare de adevăr

Notăm cu P2 mulţimea propoziţiilor definite după definiţia 1.1.

DEFINIŢIA 1.2. Funcţia v : P2 → {0,1} se numeşte funcţia valoare de adevăr. Fiecărei propoziţii din mulţimea propoziţiilor P2 i se ataşează valoarea 1 dacă propoziţia este adevărată, şi valoarea 0 dacă este falsă.

De obicei se vor nota propoziţiile prin litere mici: p,q,r ... şi cu v(p), v(q), 1

Page 2: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

v(r)... valorile lor de adevăr.

1.1.3. Operatori (operaţii cu propoziţii)

Cu ajutorul operatorilor definiţi în continuare se pot construi propoziţii compuse.

Negarea propoziţiilor. Negarea propoziţiei este propoziţia "non p", care se notează !p şi care este adevărată când p este falsă şi falsă când p este adevărată.

Valoarea de adevăr a propoziţiei !p este dată în tabela următoare:

Conjucţia propoziţiilor. Conjucţia propoziţiilor p,q este propoziţia care se citeşte "p şi q", notată cu pΛq, care este adevărată atunci şi numai atunci când fiecare din propoziţiile p, q este adevărată.

Valoarea de adevăr a propoziţiei pΛq este dată în tabela următoare:

Disjuncţia propoziţiilor. Disjuncţia propoziţiilor p, q este propoziţia care se citeşte "p sau q" (notată pVq) şi care este adevărată, atunci şi numai atunci când este adevărată cel puţin una dintre propoziţiile p, q.

v(p) v(!p)1 00 1

v(p) v(q) v(p∧q) 0 0 0 0 1 0 1 0 0 1 1 1

2

Page 3: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Valoarea de adevăr a propoziţiei pVq este dată în tabela următoare:

Implicaţia propoziţiilor. Să considerăm propoziţia compusă (p)Vq a cărei valoare de adevăr rezultă din tabela urmatoare

Propoziţia (!p)Vq se notează p → q şi se numeşte implicaţia propoziţiilor p,q (în acestă ordine); p este ipoteza iar q este concluzia.

Observăm că (!p)Vq este falsă atunci şi numai atunci când p este adevărată şi q falsă, în celelalte cazuri fiind adevărată.

Echivalenţa propoziţiilor. Cu propoziţiile p, q putem forma propoziţia compusă (p→q) Λ (q→p) care se notează p ↔ şi se citeşte "p dacă şi numai dacă q". Din tabela următoare se vede că propoziţia p ↔ q este adevărată atunci şi numai atunci când p şi q sunt în acelaşi timp adevărate sau false.

Formule echivalente în calculul propoziţional

v(p) v(q) v(p∨q) 0 0 0 0 1 1 1 0 1 1 1 1

v(p) v(q) v(!p) v((!p)∨ q) 0 0 1 1 0 1 1 1 1 0 0 0 1 1 0 1

v(p) v(q) v(p→q) v(q→p) v(p↔q) 0 0 1 1 1 0 1 1 0 0 1 0 0 1 0 1 1 1 1 1

3

Page 4: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Cu operaţiile anterioare se pot construi diverse expresii numite formule ale calculului propoziţional (notate cu a,b...).

O formulă a(p,q,r, ...) care are valoarea o propoziţie adevărată indiferent cum sunt propoziţiile p, q, r, ... se numeşte formulă identic adevărată sau tautologie.

Proprietăţi:

1) pV(qΛr) ↔ (pVq)Λ (pVq) distributivitaţi2) pΛ (qVr) ↔ (pΛq)V(pΛr)3) p ↔ !(!p) negarea negaţiei 4) ! (pVq) ↔ !p Λ !q legile lui De Morgan5) ! (pΛq) ↔ !p V !q6) p →q ↔ !p → !q principiul reducerii la absurd7) pVq ↔ qVp comutativităţi8) pΛq ↔ qΛp

1.2. Elemente de algebră booleană

Elementele pe care le vom prezenta în cele ce urmează sunt strict necesare pentru a putea explica, în capitolele următoare, principiile funcţionării calculatoarelor. Ne vom mărgini doar la prezentarea elementelelor de strictă necesitate, fără a intra în detalii referitoare la calculul propoziţiilor, formele normale etc.

1.2.1. Mulţimea B2, operaţii

Unul dintre capitolele importante ale logicii matematice este calculul propoziţiilor, având ca principale operaţii negarea propoziţiilor, conjuncţia propoziţiilor şi disjuncţia propoziţiilor. Valoarea de adevăr a unei propoziţii este notată cu "1", iar valoarea de fals cu "0", simbolurile "1" şi "0" fiind aici simboluri fără înţeles numeric.

Mulţimea formată din elementele 0 şi 1, cu operaţiile + şi ⋅ definite în clasa de resturi modulo 2, formează un inel.

După cum se va vedea, mulţimea formată din elementele 0 şi 1 are extrem de multe aplicaţii în informatică.

4

Page 5: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Fie mulţimea B2 = {0,1}. Peste această mulţime se definesc mai multe operaţii. DEFINIŢIA 1.3. Operaţia binară notată ∨, definită prin tabelul 1.1. se numeşte disjuncţie

∨ 0 1

01

0 11 1

Tabelul 1.1. Definirea operaţiei ∨

DEFINIŢIA 1.4. Operaţia binară notată ∧ sau ⋅, definită prin tabelul 3.2. se numeşte conjuncţie.

∧ 0 1

01

0 00 1

Tabelul 1.2. Definirea operaţiei ∧

DEFINIŢIA 1.5. Operaţia unară notată ! definită prin tabelul 3.3. se numeşte negaţie.

x ! x

01

10

Tabelul 1.3. Operaţia !

În ordinea priorităţilor, aceste operaţii se succed astfel: !, ⋅, ∨ (negaţia, conjuncţia, disjuncţia).

În continuare vor fi prezentate principalele proprietăţi ale acestor operaţii. Ele pot fi demonstrate uşor, folosindu-se tabelele de adevăr.

TEOREMA 3.1. Următoarele 10 grupuri de proprietăţi sunt adevărate:

5

Page 6: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

1. a ⋅(b ⋅ c) = (a ⋅ b) ⋅ c a∨(b∨c) = (a∨b)∨c (Asociativităţi) 2. a ⋅(b∨c) = a ⋅ b∨a ⋅ c a∨b⋅c = (a∨b)⋅(a∨c) (Distributivităţi) 3. a ⋅ a = a a∨a = a (Idempotenţe) 4. a ⋅ b = b ⋅ a a∨b = b∨a (Comutativităţi) 5. a ⋅ (a∨b) = a a∨a ⋅ b = a (Absorbţii) 6. ! ! a = a (Dubla negaţie) 7. ! (a ⋅ b) = !a ∨ !b ! (a∨b) = !a ⋅ !b (Legile lui De Morgan) 8. a ⋅ 0 = 0 a∨0 = a 9. a ⋅ 1 = a a∨1 = 110. a ⋅ !a = 0 a∨!a = 1

DEFINIŢIA 1.6. Structura algebrică (B2, ∨, ⋅, !) formează o algebră booleană

DEFINIŢIA 1.7. Operaţia binară notată ⊕, definită prin tabelul 1.4. se numeşte suma modulo 2 sau sau-exclusiv.

⊕ 0 1

01

0 11 0

Tabelul 1.4. Definirea operaţiei ⊕

TEOREMA 1.2. Structura algebrică (B2, ⊕, ⋅) formează un inel comutativ cu element unitate. Acest inel poartă numele de inel boolean.1

1.2.2. Funcţii booleene şi reprezentări ale lor

Vom nota prin B2n produsul cartezian al mulţimii B2 cu ea însăşi de n ori. Deci

B2n = B2×B2×...×B2.

DEFINIŢIA 1.8. O funcţie f : B2n --- > B2 se numeşte funcţie booleană.

Un n-uplu x = (x1, x2, ..., xn) ∈ B2n va fi numit punct din B2

n. Prin f(x) =

6

Page 7: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

f(x1,x2,...,xn) vom înţelege valoarea funcţiei în punctul x.Este uşor de văzut că în B2

n sunt 2n puncte. Deci, pentru a reprezenta o funcţie booleană este necesară construcţia tabelului cu valorile funcţiei în fiecare dintre aceste puncte. Un astfel de tabel va avea 2n linii şi n+1 coloane, ca în tabelul 1.5.

x1 x2 ... xn-1 xn f(x)

0 0 ... 0 0 y0

0 0 ... 0 1 y1

0 0 ... 1 0 y2

0 0 ... 1 1 y3

... ... ... ... ... ...

1 1 ... 1 0 y2n-2

1 1 ... 1 1 y2n-1

Tabelul 1.5. Definirea unei funcţii Booleene de n variabile

Prin yi ∈ B2, i ∈ {0, 1, ... , 2n-1 } am notat valorile funcţiei f în toate punctele din B2

n.

De exemplu, se poate defini astfel funcţia booleană de trei variabile din tabelul 1.6.

a B c f(a,b,c)

0000111

0011001

0101010

0011001

7

Page 8: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

1 1 1 0

Tabelul 1.6. O funcţie Booleană de 3 variabile

Pe lângă reprezentarea prin tabel, funcţiile booleene pot fi reprezentate şi cu ajutorul expresiilor booleene. În aceste expresii sunt folosiţi operatorii: !, ⋅ şi ∨. Operanzii sunt formaţi din variabilele funcţiei şi eventual din simbolurile 0 şi 1 având rolul de constante. Pentru schimbarea ordinii de efectuare a operaţiilor se folosesc parantezele ( şi ).

În continuare vom da trei reguli după care se poate construi o expresie ce defineşte o funcţie booleană dată prin tabelul ei de valori.

R1. Pentru fiecare linie din tabel pentru care yi=1 se va construi un termen conjunctiv în conformitate cu regula R2. Termenii conjunctivi se vor lega între ei prin operatori de disjuncţie.

R2. La un termen conjunctiv participă cele n variabile, în conformitate cu regula R3. Acestea variabile sunt legate între ele prin operatori de conjuncţie.

R3. Dacă valoarea unei variabile în linia i este 1 atunci în termenul conjunctiv apare variabila; dacă valoarea ei este 0, atunci în termenul conjunctiv apare negaţia variabilei respective.

Urmând aceste reguli, funcţia de trei variabile dată ca exemplu în tabelul 1.6 va fi definită astfel:

f : B23 → B2 f(a,b,c) = !a ⋅ b ⋅!c ∨ !a ⋅ b ⋅ c ∨ a ⋅ b ⋅!c

Cei trei termeni conjunctivi corespund liniilor y2, y3 şi y6. Valorile celor trei variabile în aceste linii sunt respectiv 010, 011 şi 110. Aplicând cele trei reguli, se obţine formula de mai sus.

După cum este cunoscut din calculul propoziţiilor, o funcţie booleană

8

Page 9: LIMBAJE DE PROGRAMARE

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

poate avea mai multe reprezentări prin expresii. Acelaşi calcul al propoziţiilor defineşte diverse forme normale şi descrie metode de simplificare a expresiilor care reprezintă funcţii booleene. Obţinerea unei expresii prin regulile R1-R3 de mai sus conduce la forma normală disjunctivă perfectă.

9

Page 10: LIMBAJE DE PROGRAMARE

CAPITOLUL 2.SISTEME ŞI BAZE DE NUMERAŢIE

Numim sistem de numeraţie totalitatea regulilor folosite pentru scrierea numerelor, cu ajutorul unor simboluri numite cifre. În decursul istoriei s-au impus mai multe sisteme de numeraţie.

2.1. Tipuri de sisteme de numeraţie

Cel mai primitiv mod de reprezentare a numerelor este răbojul. Pe un băţ numit răboj, omul primitiv cresta câte o linie pentru fiecare obiect sau animal care-i aparţinea (blănuri, oi etc.). Tot pe un astfel de răboj se marcau datoriile unui gospodar faţă de altul (de unde şi cunoscuta expresie "am scris pe răboj").

S-a constatat că acest mod de scriere a numerelor este deosebit de incomod. Pentru numere mari trebuie folosite multe semne, eventual trebuie folosite semne diferite pentru numere mai deosebite (10, 50, 100 etc.). Aceste semne se combină în diferite moduri. După felul de grupare şi ordonare a semnelor se deosebesc două sisteme de numeraţie: sistemul aditiv şi sistemul poziţional.

2.1.1. Sistemul de numeraţie aditiv (roman)

Sistemul de numeraţie roman foloseşte pentru scrierea numerelor cifrele:

I, V, X, L, C, D, M

care corespund valorilor: unu, cinci, zece, cincizeci, o sută, cinci sute, respectiv o mie.

Trei reguli importante guvernează acest sistem de numeraţie:

Regula 1. Mai multe cifre de aceeaşi valoare, scrise consecutiv, reprezintă suma acestor cifre.

De exemplu: XXX = 30; II = 2; MM = 2000.

Regula 2. O pereche de cifre de valori diferite, în care cifra de valoare mai mare se află în faţa cifrei de valoare mai mică reprezintă,

9

Page 11: LIMBAJE DE PROGRAMARE

de asemenea, suma acestor cifre.

De exemplu: XI = 10+1 = 11; VI = 5+1 = 6.

Regula 3. O pereche de cifre de valori diferite, în care cifra de valoare mai mică se află în faţa cifrei de valoare mai mare reprezintă diferenţa acestor cifre.

De exemplu: IX = 10-1 = 9; IC = 100-1 = 99.

Sistemul de numeraţie roman prezintă dezavantajul unei scrieri greoaie şi a lipsei unei reprezentări unice a numerelor. Astfel avem:

4 9 = IL = LIC

Alte neajunsuri ale acestui mod de scriere: numerele mari sunt în general lungi şi adesea de necuprins cu privirea, iar operaţiile aritmetice cu aceste semne sunt foarte anevoioase.

2.1.2. Sistemul de numeraţie poziţional (arab)

Acest sistem a fost introdus de către indieni şi chinezi şi a fost preluat de către europeni datorită arabilor. El este un sistem poziţional, în care aportul unei cifre în stabilirea valorii numărului depinde atât de valoarea ei cât şi de poziţia pe care o ocupă în scrierea numărului. Această caracteristică asigură superioritatea sistemului de numeraţie arab faţă de sistemul de numeraţie roman.

În scrierea arabă, fiecare grup de 10 elemente numărate (unităţi) formează o grupă de rang superior (zeci), 10 grupe de zeci formează o grupă de rang superior grupei zeci (sute) ş.a.m.d., conform celor cunoscute de elevi încă din clasa I primară. Datorită faptului că numărul zece este pragul de trecere la un rang superior, se spune că este folosită baza de numeraţie 10.

Pentru scrierea în baza 10 sunt folosite cifrele:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

În secţiunile următoare vom dezvolta acest sistem de numeraţie folosind nu numai baza 10, ci şi alte baze de numeraţie.

10

Page 12: LIMBAJE DE PROGRAMARE

2.2. Sisteme poziţionale de numeraţie în diverse baze

2.2.1. Reprezentarea numerelor naturale într-o bază b

Fie b ∈ N, b > 1 un număr numit bază de numeraţie. Întreaga teorie aritmetică a reprezentării numerelor în baza b este fundamentată pe teorema care urmează.

TEOREMA 2.1. Pentru orice număr natural nenul x, există în mod unic un număr natural n numit rang şi n+1 numere naturale c0, c1, ... cn numite cifre în baza b, care satisfac relaţiile:

1) x = cnbn + cn-1bn-1 +...+ c1b + c0,2) ci ∈ { 0, 1, ... , b-1 }, ∀ i ∈ { 0, 1, ..., n }3) cn ≠ 0.

Demonstraţie: Deoarece x ≥ 1, iar b > 1, rezultă că există, în mod unic, un număr natural n pentru care este verificată dubla inegalitate (axioma lui Arhimede):

bn ≤ x < bn+1

Acest număr n este cel din enunţul teoremei. În continuare, vom aplica inducţia completă după n.

Pentru n = 0, avem chiar c0 = x şi alegerea este unică.Presupunem teorema adevărată pentru n şi să demonstrăm că rămâne

valabilă şi pentru n+1. Scriind teorema împărţirii cu rest, unde deîmpărţitul este x şi împărţitorul este b, rezultă:

x = y⋅b + r, cu 0 ≤ r < b

Vom pune acum c0 = r, care este determinat în mod unic. Dacă bn+1 ≤ x < bn+2, rezultă că bn ≤ y < bn+1. Pentru y este valabilă ipoteza de inducţie, deci pentru el există n+1 cifre, pe care le vom nota c1, c2, ..., cn+1 şi care sunt de asemenea unic determinate. Rezultă că avem:

x = cn+1bn+1 + cnbn +...+ c1b + c0

11

Page 13: LIMBAJE DE PROGRAMARE

şi că sunt verificate celelalte două relaţii din teoremă. Cu aceasta, teorema este complet demonstrată.

Să ataşăm acum fiecărui număr din mulţimea { 0, 1, ..., b-1 } câte un simbol (caracter, semn etc.) în aşa fel, încât la numere diferite să ataşăm simboluri diferite. Convenim, de asemenea, că atunci când ne referim la o cifră ci, să-i scriem de fapt simbolul Ci asociat numărului ci. Cu aceste convenţii (şi cu ipoteza că simbolurile "(" şi ")" nu reprezintă cifre, avem reprezentarea numărului x în baza b:

(CmCm-1...C1C0)b

În tot ceea ce urmează, vom identifica cifrele ci din teoremă cu simbolurile ataşate lor Ci (şi le vom scrie tot ci).

Observaţie: Din definiţia dată reprezentării unui număr într-un sistem de numeraţie poziţional cu baza b, rezultă că o cifră indică o valoare de b ori mai mare decât aceeaşi cifră situată într-o poziţie de rang mai mic cu o unitate. Din acest motiv sistemele de numeraţie poziţionale mai sunt numite sisteme ponderate.

În sistemul de numeraţie cu baza 10, numit sistem zecimal, sunt folosite simbolurile de cifre:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Acest sistem este utilizat pe scară largă în prelucrarea manuală a datelor, precum şi în comunicarea cu un sistem de calcul.

Sistemul de numeraţie cu baza doi, numit sistem binar, foloseşte numai cifrele 0 şi 1. El stă la baza construcţiei sistemelor de calcul.

Sistemul de numeraţie cu baza 16, numit sistem hexazecimal foloseşte cifrele:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F

ultimele şase simboluri, de fapt primele şase litere ale alfabetului, scrise fie cu majuscule fie cu minuscule, desemnează numerele 10, 11, 12, 13, 14 şi 15 ca cifre în baza 16.

Din raţiuni de scriere comodă, şi pentru evitarea unor confuzii, simbolurile de cifre dintr-o anumită bază sunt preluate în bazele de numeraţie superioare. Astfel, în orice bază de numeraţie cu b ≤ 16 se folosesc simbolurile indicate mai sus, fiecare simbol indicând acelaşi număr, indiferent de bază. De exemplu, în baza 7 se folosesc simbolurile 0 ÷ 6, iar

12

Page 14: LIMBAJE DE PROGRAMARE

în baza 13 se folosesc simbolurile 0 ÷ 9, A ÷ C; simbolul B este cifră doar începând cu baza 12 şi reprezintă cifra care are valoarea 11.

Este momentul să precizăm că sunt folosite şi alte reprezentări ale cifrelor decât cea prin simboluri grafice. Această afirmaţie este, aparent, neobişnuită, deoarece majoritatea covârşitoare a populaţiei nu cunoaşte alt sistem de numeraţie decât cel zecimal! Dar, spre exemplu, atunci când se foloseşte calculatorul pentru lucrul cu baze de numeraţie foarte mari, fiecare cifră este reprezentată prin conţinutul unui cuvânt din memoria calculatorului.

2.2.2. Reprezentarea numerelor reale într-o bază b

Definiţia dată reprezentării numerelor naturale poate fi extinsă şi la numerele reale. Fie x > 0 un număr real.

DEFINIŢIA 2.1. Reprezentarea unui număr real x > 0 în baza b este (şi se scrie):

x = (cncn-1...c1c0,c-1c-2c-3...)b

dacă sunt îndeplinite condiţiile:

1) x = cnbn+cn-1bn-1+...+c1b+c0+c-1b-1+c-2b-2+c-3b-3...2) ci ∈ {0,1,...,b-1} , ∀ i ∈ {n,...,1,0,-1,-2,-3,...} 3) cn ≠ 04) ∀ k≤ 0 ∃ j < k astfel, încât cj < b-1

Comparând definiţia dată reprezentării numerelor reale cu cea dată reprezentării numerelor întregi, se observă că la numerele reale avem în plus condiţia 4). Această condiţie cere să nu existe un rang la partea fracţionară după care toate cifrele reprezentării să fie egale cu b-1. Ea este necesară pentru a asigura unicitatea reprezentării numerelor. Drept contraexemplu, considerăm egalităţile:

1/2 = 0,5 = 0,499999…numerele fiind scrise în baza 10.

Are loc următoarea:

TEOREMA 2.2. Fiind dată o bază de numeraţie b, pentru orice număr real x > 0 există o reprezentare unică în baza b conform definiţiei 4.1. Numărul:

13

Page 15: LIMBAJE DE PROGRAMARE

(cncn-1...c1c0)b

reprezintă, în baza b, partea iintreagă a numărului x, iar numărul

(0,c-1c-2c-3...)b

reprezintă, în baza b, partea fracţionară a numărului x.

DEFINITIA 2.2 Reprezentarea unui număr real x în baza b (extinderea definiţiei 4.1 la toate numerele reale).

a) Dacă x > 0, atunci numărul se reprezintă conform def. 2.1.b) Dacă x = 0, atunci x se reprezintă 0.c) Dacă x < 0, atunci se pune mai întâi semnul -, după care urmează

reprezentarea numărului -x, conform definiţiei 2.1.

2.2.3. Conversii între baze de numeraţie

Existenţa şi utilizarea mai multor baze de numeraţie ridică problema conversiei dintr-o bază în alta. Pentru fixarea ideilor, să presupunem că numărul x este reprezentat într-o bază (baza veche) p şi se doreşte conversia lui într-o bază (baza nouă) q. Cele mai cunoscute metode de conversie sunt:

a) Metoda împărţirii la bază a numerelor întregi, cu calcule în baza p (baza veche);

b) Metoda înmulţirii cu baza a părţilor fracţionare, cu calcule în bază p (baza veche);

c) Metoda substituţiei, cu calcule în baza q (baza nouă).

2.2.3.1. Conversia întregilor prin împărţiri succesive

Esenţa metodei este descrisă în demonstraţia teoremei 4.1 şi constă în faptul că cifra unităţilor unui număr natural x scris într-o anumită bază q coincide cu restul împărţirii lui x la q. Această metodă se aplică cu toate calculele efectuate în baza veche pentru a obţine reprezentarea lui x în noua bază q. Algoritmul de conversie este descris în figura 4.1., folosind un limbaj de tip pseudocod. În esenţă, se fac împărţiri succesive ale numărului x la q. Se

14

Page 16: LIMBAJE DE PROGRAMARE

reţine de fiecare dată restul, iar câtul va deveni noul deîmpărţit. Resturile, luate în ordinea inversă a apariţiei, vor fi cifrele în noua bază q.

DATE x ( din N*, scris în baza p), q (noua bază) ; n := -1; d := x; CAT TIMP d > 0 EXECUTA n := n + 1; cn := d mod q; Cn := simbolul asociat cifrei cn în baza q; d := d div q; SF CAT TIMP ; REZULTATE Cn Cn-1 ... C1 C0

Figura 2.1 Conversia întregilor prin împărţiri succesive

Exemple: Să considerăm numărul (985437)10 pe care vrem să-l reprezentăm pe rând în bazele 6 şi 16. Organizarea calculelor depinde de fantezia celui care face conversia. Noi vă propunem organizarea sub formă de tabel. Aşa cum am mai spus, toate calculele se fac în baza p, în cazul de faţă baza 10. Conversia în baza 6 este prezentată în tabelul 2.1.

Citind ultima coloană din tabelul 2.1 de jos în sus, obţinem rezultatul conversiei:

(985437)10 = (33042113)6

nDeîmpărţit(baza 10)

Impărţitor (q)

(baza 10)

Cât(baza 10)

Rest(cn)

(baza 10)

Simbolcifră (Cn)(baza 6)

01234567

985437164239273734562760126213

66666666

1642392737345627601262130

31124033

31124033

Tabelul 2.1 Conversia unui număr în baza 6

15

Page 17: LIMBAJE DE PROGRAMARE

Acum conversia în baza 16 este dată în tabelul 2.2.

nDeîmpărţit(baza 10)

Impărţitor (q)

(baza 10)

Cât

(baza 10)

Rest(cn)

(baza 10)

Simbolcifră (Cn)(baza 16)

01234

98543761589384924015

1616161616

615893849240150

1359015

D590F

Tabelul 2.2 Conversia unui număr în baza 16

Citind ultima coloană de jos în sus, obţinem rezultatul conversiei: (985437)10 = (F095D)16

În fine, aplicăm aceeaşi metodă pentru a trece din baza 6 în baza 16, cu calculele făcute în baza 6. Tabelul 2.3 redă aceste calcule, toate numerele din tabel fiind în baza 6.

nDeîmpărţit(baza 6)

Impărţitor (q)

(baza 6)

Cât

(baza 6)

Rest(cn)

(baza 6)

Simbolcifră (Cn)(baza 16)

01234

33042113115304525453104023

2424242424

1153045254531040230

21513023

D590F

Tabelul 2.3 Conversia din baza 6 în baza 16Citind acum ultima coloană de jos în sus, obţinem rezultatul conversiei: (33042113)6 = (F095D)16

16

Page 18: LIMBAJE DE PROGRAMARE

2.2.3.2. Conversia părţilor fracţionare prin înmulţiri succesive

Ne interesează acum să găsim cifrele zecimale la trecerea dintr-o bază veche p într-o bază nouă q.

Să notăm cu y partea fracţionară. După obţinerea cifrei c -1 = [ q ⋅ y ] în baza q, se aplică din nou metoda înlocuind pe y cu { q ⋅ y } ş.a.m.d. Acest algoritm este descris în figura 2.2.

DATE y ( din (0,1), scris în baza p), q (noua bază) ; n := 0 ; f := y; CAT_TIMP (f > 0) sau (nu s-au obţinut suficiente cifre) sau (nu s-a obţinut perioadă: f nu coincide cu unul anterior) EXECUTA n := n - 1; cn := [ f ⋅ q ]; Cn := simbolul asociat cifrei cn în baza q; f := { f ⋅ q } SF_CATTIMP REZULTATE Cn Cn-1 ... C1 C0

Figura 2.2 Conversia părţii fracţionare prin înmulţiri succesive

Prezentăm în continuare câteva exemple. Pentru simplificarea calculelor, presupunem că baza p este 10. Mai întâi, vom trece numărul (0,43359375)10 în baza 8 (deci vom înmulţi mereu părţile fracţionare cu 8).

0 4 3 3 5 9 3 7 5 .8 3 4 6 8 7 5 3 7 5 6 0

Rezultă că (0,43359375)10 = (0,336)8 şi s-au obţinut un număr finit de cifre la partea fracţionară.

În continuare vom converti tot în baza 8 numărul 0,51437, dar vom reţine numai trei cifre după virgulă:

0 5 1 4 3 7 ⋅ 817

Page 19: LIMBAJE DE PROGRAMARE

4 1 1 4 9 6 0 9 1 9 6 8 7 3 5 7 4 4 - - - - - Rezultă că (0,51437)10 = (0,407...)8.

Să convertim acum, tot în baza 8, numărul (0,45)10.

0 4 5 ⋅ 8 3 6 0 4 8 6 4 3 2

1 6 4 8 6 4 3 2 1 6 - - -

Rezultă că (0,45)10 = (0,346314631...)8. Se observă că se obţine o fracţie periodică mixtă, deci (0,45)10 = (0,3(4631))8.

Să trecem acum numărul (0,85)10 în baza 16.

0 8 5 ⋅ 16 13 6 0 9 6 9 6 - - - -

Rezultă că (0,85)10 = (0,D(9))16, unde D este simbolul cifrei cu valoarea (13)10.

Observaţii:

1. Procesul de conversie se încheie fie când dat partea fracţionară devine zero, fie când se consideră că s-au extras suficiente cifre, fie când se

18

Page 20: LIMBAJE DE PROGRAMARE

observă apariţia perioadei. Dacă conversia se face cu ajutorul calculatorului, depistarea perioadei este dificilă, motiv pentru care se foloseşte doar prima sau a doua condiţie de oprire.

2. Calculele se fac întotdeauna cu un număr finit de cifre după virgulă, indiferent dacă conversia se face manual sau cu ajutorul calculatorului. Din această cauză, teoretic întotdeauna apare perioada. Într-adevăr, dacă se reţin k cifre după virgulă, după maximum bk încercări variabila f din algoritmul 4.2 se va repeta.

3. Din cauza observaţiilor de mai sus, o conversie dublă conduce la erori de trunchiere. Spre exemplu, să trecem numărul (0,6)10 în baza 8 şi să reţinem patru cifre. Avem că (0,6)10 = (0,4631...)8. Acum să aplicăm acelaşi algoritm pentru trecerea numărului (0,4631)8 în baza 10, cu calculele în baza 8.

0 4 6 3 1 ⋅ (12)8

5 7 7 7 2 11 7 7 0 4 ; (11)8 = (9)10

11 6 6 5 0 10 4 2 2 ; (10)8 = (8)10

5 2 6 4 3 4 1 0 - - - - - - -

Rezultă că (0,4631)8 = (0,599853...)10. Aceasta înseamnă că în loc de (0,6)10, în urma dublei conversii, se obţine (0,599853...)10.

2.3. Relaţii între bazele 2, 8 şi 16

2.3.1. Cifre binare, octale, zecimale, hexazecimale

În informatică, bazele 2, 8 şi 16 au o deosebită importanţă. După cum se va vedea imediat, conversiile între aceste baze se pot realiza deosebit de simplu. Ca terminologie, vom spune că lucrăm în sistemul binar dacă baza este 2, sistemul octal dacă baza este 8 şi sistemul hexazecimal dacă baza este 16. În acest context, este firesc să folosim termenul de sistem zecimal dacă baza este 10.

Tabelul 2.4. conţine corespondenţa între cele patru reprezentări ale numerelor de la 0 la 15. Pentru o manevrare uşoară între aceste patru baze de numeraţie, recomandăm memorarea acestui tabel.

19

Page 21: LIMBAJE DE PROGRAMARE

Zecimal

Binar Octal Hexa

0123456789101112131415

0000000100100011010001010110011110001001101010111100110111101111

012345671011121314151617

0123456789ABCDEF

Tabelul 2.4. Corespondenţa între cifrele bazelor 10, 2, 8 şi 16.

Orice grup de 3 cifre binare determină, în mod unic, o cifră octală; reciproc, o cifră octală se reprezintă în binar printr-un grup de 3 cifre binare, completând zerourile necesare la stânga. Un astfel de grup de 3 cifre binare îl numim triadă.

Analog, orice grup de 4 cifre binare determină, în mod unic, o cifră hexazecimală; reciproc, o cifră hexazecimală se reprezintă în binar printr-un grup de 4 cifre binare, completând cu zerourile necesare la stânga. Un astfel de grup de 4 cifre binare îl vom numi tetradă.

2.3.2. Conversii binar - octal

Să considerăm numărul x = (cncn-1...c1c0,c-1c-2c-3...c-k)2. Fără a restrânge generalitatea, presupunem că n+1 este multiplu de 3, iar k este de asemenea un multiplu de 3 (deci atât partea întreagă a numărului, cât şi partea fracţionară a lui au câte un număr de cifre multiplu de 3). Dacă nu este aşa, se adaugă, după necesităţi, zerouri înaintea lui cn, şi/sau după c-k. Aceste zerouri nu modifică valoarea numărului.

Să scriem acum valoarea numărului x, grupând în triade cifrele din

20

Page 22: LIMBAJE DE PROGRAMARE

stânga şi din dreapta virgulei zecimale şi să evidenţiem astfel câteva triade:

X = cn⋅2n+cn-1⋅2n-1+cn-2⋅2n-2 + cn-3⋅2n-3+cn-4⋅2n-4+cn-5⋅2n-5 + ... +c5⋅25+c4⋅24+c3⋅23+c2⋅22+c1⋅2+ c0 + c-1⋅2-1+c-2⋅2-2+c-3⋅2-3 + ... + c-k+2⋅2-k+2+c-k+1⋅2-

k+1+c-k⋅2-k = = (cn⋅22+cn-1⋅2+cn-2)⋅2n-2 + (cn-3⋅22+cn-4⋅2+cn-5)⋅2n-5 + ... +(c5⋅22+c4⋅2+c3)⋅23 + c2⋅22+c1⋅2+c + (c-1⋅22+c-2⋅2+c-3)⋅2-3 + ... + (c-k+2⋅22+c-k+1⋅2+c-

k)⋅2-k

Să notăm j=(n-2)/3 şi s= k/3.

Se observă că numerele:

n-2, n-5, ..., 3, 0, -3, ..., -k

sunt multiplii lui 3 dintre -s⋅3 şi j ⋅3.

Fiecare paranteză de forma:

(cl+2⋅22+cl+1⋅2+cl), l ∈ {n-2, n-5, ..., 3, 0, -3, ..., -k}

este un număr de trei cifre în baza 2, deci un număr între 0 şi 7. Altfel spus, o astfel de paranteză este o cifră în baza 8. Vom nota cu ri , cu i = l / 3 această cifră.

Fiecare dintre puterile lui 2 care apar pe lângă cifrele ri cu aceşti exponenţi poate fi transformată astfel:

2l = 23⋅(l/3) = 8l/3 = 8i, unde

l ∈ {n-2, n-5, ..., 3, 0, -3, ..., -k}, respectiv

i ∈ {j, j-1, ..., 1, 0, -1, ..., -s}

Cu aceste notaţii, avem:

x = rj⋅8j + rj-1⋅8j-1 + ... + r1⋅8 + r0 + r-1⋅8-1 + ... + r-s⋅8-s

deci x = (rj rj-1 ... r1 r0 ,r-1 r-2 r-3 ... r-s)8.

Raţionând invers, dacă avem un număr scris în octal, reprezentarea lui binară se obţine păstrând virgula zecimală, iar spre stânga şi spre dreapta ei se înlocuieşte fiecare cifră octală cu triada corespunzătoare ei.21

Page 23: LIMBAJE DE PROGRAMARE

Sintetizând cele demonstrate mai sus, avem următoarele două reguli practice de trecere între bazele 2 şi 8:

- Pentru trecerea din baza 2 în baza 8, se grupează cifrele reprezentării binare în triade, pornind de la virgulă spre stânga şi spre dreapta. Dacă cel mai din stânga grup al părţii întregi, respectiv cel mai din dreapta grup al părţii fracţionare, nu are exact trei cifre, se completează cu zerouri la stânga pentru partea întreagă, respectiv la dreapta pentru partea fracţionară. Se înlocuieşte fiecare triadă cu cifra octală corespunzătoare.

- Pentru trecerea din baza 8 în baza 2, pornind de la virgulă, spre stânga şi spre dreapta, se înlocuieşte fiecare cifră octală cu triada binară corespunzătoare ei (fiecara cifră octală se va înlocui cu exact trei cifre binare!).

Să considerăm câteva exemple:

(100 101 110 , 111 011 001)2 = (456,731)8

(1001101011.0110100100101011)2 = (001 001 101 011.011 010 010 010 101 100)2 =(1153.322254)8

(11100111000001101)2 = (011 100 111 000 001 101)2 = (347015)8

(0,001000000111011011)2 = (000,001 000 000 111 011 011)2 = (0,100733)8

2.3.3. Conversii binar - hexazecimal

Să considerăm numărul x = (cncn-1...c1c0,c-1c-2c-3...c-k)2. Fără a restrânge generalitatea, presupunem că n+1 este multiplu de 4, iar k este de asemenea un multiplu de 4 (deci atât partea întreagă a numărului, cât şi partea fracţionară a lui au câte un număr de cifre multiplu de 4). Dacă nu este aşa, se adaugă după necesităţi zerouri înaintea lui cn, şi/sau după c-k. Aceste zerouri nu modifică valoarea numărului.

Analog conversiilor binar - octal, aici se scrie valoarea numărului x, grupând în tetrade spre stânga şi spre dreapta virgulei zecimale. Întreaga demonstraţie decurge analog celei prezentate în secţiunea precedentă, motiv pentru care nu o mai reluăm. Dăm numai două reguli practice de trecere între bazele 2 şi 16:

- Pentru trecerea din baza 2 în baza 16, se grupează cifrele reprezentării binare în tetrade, pornind de la virgulă spre stânga şi spre dreapta. Dacă cel mai din stânga grup al părţii întregi, respectiv cel mai din dreapta grup al părţii fracţionare, nu are exact patru cifre, se completează cu zerouri la stânga

22

Page 24: LIMBAJE DE PROGRAMARE

pentru partea întreagă, respectiv la dreapta pentru partea fracţionară. Se înlocuieşte fiecare tetradă cu cifra hexazecimală corespunzătoare.

- Pentru trecerea din baza 16 în baza 2, pornind de la virgulă, spre stânga şi spre dreapta, se înlocuieşte fiecare cifră hexazecimală cu tetrada binară corespunzătoare ei (fiecara cifră hexazecimală se va înlocui cu exact patru cifre binare!).

Să considerăm câteva exemple:

(110100101010,00111101)2 = (D2A,3D)16

( 1001101011.0110100100101011)2 = (0010 0110 1011,0110 1001 0010 1011)2 =

=(26B,692B)16

( 11100111000001101)2 = (0001 1100 1110 0000 1101)2 = (1CE0D)16

( 0,001000000111011011)2 = (0000,0010 0000 0111 0110 1100)2

=(0,2076C)16

2.3.4 Conversii octal - hexazecimal

Este evident că cel mai simplu mod de a face conversii între aceste două baze de numeraţie este acela al folosirii bazei 2 ca intermediar. Pentru a nu opera cu şiruri nesfârşite de cifre binare, facem următoarele recomandări:

- Pentru trecerea din baza 8 în baza 16, se grupează la stânga şi dreapta virgulei, câte 4 cifre octale. Acestea vor fi transformate mai întâi în 12 cifre binare, care apoi vor fi transformate în 3 cifre hexazecimale.

- Pentru trecerea din baza 16 în baza 8, se procedează analog, adică se grupează la stânga şi dreapta virgulei, câte 3 cifre hexazecimale. Acestea vor fi transformate mai întâi în 12 cifre binare, care apoi vor fi transformate în 4 cifre octale.

De exemplu, (27354357,3576)8 = (5DD8EF,77E)16. Grupările intermediare în baza 2 se pot organiza astfel:

( 2735 4357, 3576 )8 = (010 111 011 101 100 011 101 111 , 011 101 111 110)2 =

(0101 1101 1101 1000 1110 1111 , 0111 0111 1110 )2 = ( 5DD 8EF , 77E )16

23

Page 25: LIMBAJE DE PROGRAMARE

CAPITOLUL 3.CODIFICAREA INFORMAŢIEI

3.1. Noţiunea de cod; exemple

Codificarea a apărut din necesitatea de a se face schimb de mesaje care să poată fi înţelese numai de către persoanele care cunosc cheia codificării. Aceste schimburi de mesaje sunt necesare atunci când se manipulează informaţii secrete. În astfel de comunicaţii este esenţial mecanismul de codificare, care trebuie să fie suficient de complex încât să împiedice descifrarea lui de către persoane neautorizate sau rău intenţionate.

Ca parte integrantă a prelucrării informaţiilor cu ajutorul calculatorului, codificarea urmăreşte transpunerea informaţiei din forma ei primară într-o formă accesibilă calculatorului. Mecanismul codificării trebuie să fie simplu, astfel încât să poată fi automatizat în mod eficient.

3.1.1. Definirea codului

Trecând peste particularităţi şi diferenţe de exprimare, se poate crea un model matematic cuprinzător şi general al procesului de codificare. Să notăm prin:

S = { s1, s2, ..., sp }

mulţimea simbolurilor primare de informaţie. După caz, S poate fi mulţimea caracterelor ce se pot tipări, o mulţime de cuvinte dintr-un anumit limbaj sau limbă, o submulţime finită de numere etc. Să notăm prin:

A = { a1, a2, ..., aq }

un alfabet al codificării, în care elementele ai le vom numi litere. Cu ajutorul literelor alfabetului A, elementele mulţimii S vor fi reprezentate într-o nouă formă, forma codificată. Vom nota prin An mulţimea tuturor cuvintelor de lungime n formate cu litere din A. Deci

An = { w w = ai1ai2...ain / aij∈A, j = 1,n }

Prin A+ vom nota mulţimea tuturor cuvintelor ce se pot forma cu litere din A. Deci

24

Page 26: LIMBAJE DE PROGRAMARE

A+ = A ∪ A2 ∪ ... ∪ An ∪ ...

DEFINIŢIA 3.1. O aplicaţie injectivă C : S → A+ se numeşte codificare a simbolurilor din S, sau mai simplu, cod.

DEFINIŢIA 3.2. Un cod pentru care toate cuvintele de cod au aceeaşi lungime n se numeşte uniform, iar n se numeşte lungimea codului. În acest caz avem că

C : S → An

Codurile care nu sunt uniforme se numesc coduri neuniforme.

3.1.2. Exemple simple de coduri

Să stabilim o corespondenţă între cifrele zecimale şi reprezentarea lor binară. În acest caz avem de-a face cu un cod uniform, în care:

S = { 0, 1, ..., 9 }, A = B2 = { 0 , 1 }, n = 4

Codificarea prin funcţia de codificare C este cea cunoscută, adică:

C(0) = 0000, C(1) = 0001, C(2) = 0010, ..., C(9) = 1001

Este binecunoscut alfabetul Morse. El este un cod pentru care S este mulţimea literelor mici, a cifrelor zecimale şi a unor semne speciale. Mulţimea A este formată din "." (punct); "-" (linie), care are ca lungime echivalentul a trei puncte; "pauza" care poate avea trei lungimi: lungime de un punct între două semne din A, de trei puncte între două semne din S, de cinci puncte între două cuvinte din S. Tabelul 3.1 defineşte funcţia C pentru alfabetul Morse.

Mesajele în cod Morse sunt afişate de regulă pe o bandă îngustă şi continuă de hârtie. Până la apariţia mijloacelor moderne de comunicaţie, acest cod s-a folosit pe scară largă în telegrafie şi în transportul feroviar. Codul Morse a fost astfel conceput încât caracterele mai frecvente să aibă codificarea mai scurtă. Din punctul de vedere al modelului matematic, codul Morse este cod neuniform, deoarece cuvintele de codificat au lungimi cuprinse între 1 şi 6 litere din A.

a .- n -. 1 .----

ă .-.- o --- 2 ..---

25

Page 27: LIMBAJE DE PROGRAMARE

b -... ö ---. 3 ...--

c -.-. p .--. 4 ....-

d -.. q --.- 5 .....

e . r .-. 6 -....

f ..-. s ... 7 --...

g --. ş ---- 8 ---..

h .... t - 9 ----.

i .. u ..- 0 -----

j .--- ü ..-- . ......

k -.- v ...- , .-.-.-

l .-.. w .-- ; -.-....

m -- x -..- : ---...

y -.-- ? ..--..

z --.. ! --..--

Tabelul 3.1. Codificarea în alfabetul Morse

3.1.3. Problema decodificării

Deoarece funcţia de codificare C este injectivă, rezultă că funcţia:

C : S → C(S) ⊆ A+

este bijectivă. În acest caz problema decodificării se pune astfel: fiind dat un cuvânt w ∈ A+ să se determine si ∈ S astfel încât C(si) = w sau să se răspundă că nu există un astfel de si. Cu alte cuvinte, trebuie evaluată în w funcţia:

C-1 : C(S) → S

Pentru codurile uniforme, problema decodificării este simplă. În schimb, ea se complică la codurile neuniforme. O posibilă rezolvare a decodificării la codurile neuniforme constă în introducerea unui simbol consacrat ca element despărţitor între două secvenţe C(w1) şi C(w2) consecutive. Codul Morse foloseşte în acest scop "pauza" de diverse lungimi. Utilizarea unui astfel de

26

Page 28: LIMBAJE DE PROGRAMARE

simbol determină creşterea numărului de litere cu care se codifică o succesiune de simboluri din S.

Există coduri neuniforme pentru care decodificarea se poate face fără folosirea simbolurilor despărţitoare. Clasa acestor coduri este dată de definiţia care urmează.

DEFINIŢIA 3.3. Un cod C poartă numele de cod unic decodificabil dacă pentru orice două simboluri si şi sj din S, nici una dintre secvenţele de cod C(si) şi C(sj) nu este prefix pentru cealaltă.

De exemplu, dacă unui simbol s1 îi atribuim codul a1a1a2, atunci nu mai putem avea, pentru alte caractere, secvenţele de cod a1, a1a1,a1a1a2a4 etc. . În tabelul 3.2 este redată o codificare unic decodificabilă pentru mulţimile

S = { 0, 1, ..., 9 } şi A = { 1, 2, 3 }

0 1 2 3 4 5 6 7 8 9

11 12 212 222 31 321 322 211 3321 3312

Tabelul 3.2. Un cod unic decodificabil

3.2. Codificarea datelor în calculator

3.2.1. Codificarea caracterelor

Fie S = { _, a, b, . . ., z, A, B, . . ., Z, 0, 1, . . ., 9, +, ., ;, :, $, . . .} mulţimea caracterelor tipăribile, existente la fiecare imprimantă, tastatură etc.

Pentru codificarea acestor caractere în vederea prelucrării lor automate, standardele internaţionale impun o serie de restricţii. După cum se va vedea, aceste restricţii sunt benefice pentru prelucrarea automată a caracterelor.

Să considerăm mulţimile: l mulţimea literelor mici ale alfabetului latin, L mulţimea literelor mari (în aceste mulţimi nu intră caracterele româneşti ă, â, î, ş, ţ), c mulţimea cifrelor zecimale, s mulţimea caracterelor speciale (spaţiul îl vom nota cu _), iar f mulţimea caracterelor funcţionale, cele care nu apar la tipărire (afişare), ci doar dirijează tipărirea (afişarea). Aşadar:

27

Page 29: LIMBAJE DE PROGRAMARE

l = { a, b, ..., z }, L = { A, B, ..., Z },c = { 0, 1, ..., 9 }, s = { _, +, ;, :, $, ... },f = {CR, LF, TAB, FF, BEL, BS, . . . }

În continuare, vom nota S = l ∪ L ∪ c ∪ s ∪ f. Înainte de a descrie condiţiile codificării, să prezentăm rolul câtorva dintre caracterele funţionale, simbolizate mai sus prin grupuri de litere mari. CR provoacă deplasarea dispozitivului de afişare (tipărire) la început de rând (Carriage Return). LF provoacă deplasarea dispozitivului cu un rând mai jos (Line Feed), păstrându-se poziţia în cadrul rândului. De obicei, se foloseşte succesiunea de caractere CR LF pentru a separa două linii de afişat, efectul lor cumulat fiind trecerea la începutul rândului următor. TAB este caracterul de tabulare, deci avansul dispozitivului la poziţia următorului stop de tabulare (de obicei peste 5-8 caractere). FF (Form Feed) provoacă trecerea la pagina (ecranul) următoare (următor). BEL provoacă emiterea unui semnal sonor, iar BS provoacă deplasarea dispozitivului de afişare (tipărire) cu o poziţie spre stânga, în vederea ştergerii (supraimprimării) ultimului caracter.

Funcţia de codificare se defineşte astfel:

C: S → [0,m] ,

unde m este 127 sau 255. Dacă considerăm reprezentarea binară, avem de-a face cu un cod uniform pe 7 biţi, respectiv pe 8 biţi. Având în vedere faptul că octetul este unitatea de adresare a memoriei, codul pe 7 biţi se extinde la 8 biţi punând 0 la bitul cel mai semnificativ. Deci codificarea unui caracter este un număr care încape pe un octet.

0 00 NUL1 01 SOH2 02 STX3 03 ETX4 04 EOT5 05 ENQ6 06 ACK7 07 BEL8 08 BS9 09 TAB10 0A LF11 0B VT

32 20 33 21 ! 34 22 " 35 23 # 36 24 $ 37 25 % 38 26 & 39 27 ' 40 28 ( 41 29 ) 42 2A * 43 2B +

64 40 @65 41 A66 42 B67 43 C68 44 D69 45 E70 46 F71 47 G72 48 H73 49 I74 4A J75 4B K

96 60 ` 97 61 a 98 62 b 99 63 c100 64 d101 65 e102 66 f103 67 g104 68 h105 69 I106 6A j107 6B k

28

Page 30: LIMBAJE DE PROGRAMARE

12 0C FF13 0D CR14 0E SO15 0F SI16 10 DLE17 11 DC118 12 DC219 13 DC320 14 DC421 15 NAK22 16 SYN23 17 ETB24 18 CAN25 19 EM26 1A SUB27 1B ESC28 1C FS29 1D GS30 1E RS31 1F US

44 2C , 45 2D - 46 2E . 47 2F / 48 30 0 49 31 1 50 32 2 51 33 3 52 34 4 53 35 5 54 36 6 55 37 7 56 38 8 57 39 9 58 3A : 59 3B ; 60 3C < 61 3D = 62 3E > 63 3F ?

76 4C L77 4D M78 4E N79 4F O80 50 P81 51 Q82 52 R83 53 S84 54 T85 55 U86 56 V87 57 W88 58 X89 59 Y90 5A Z91 5B [92 5C \93 5D ]94 5E ^95 5F _

108 6C l109 6D m110 6E n111 6F o112 70 p113 71 q114 72 r115 73 s116 74 t117 75 u118 76 v119 77 w120 78 x121 79 y122 7A z123 7B {124 7C |125 7D }126 7E ~127 7F _

Tabelul 3.3 Codul ASCII

Standardele de codificare a caracterelor impun următoarele 4 condiţii de definire a funcţiei C. 1. ∀ x ∈ f ==> C(x) < C('_') 2. ∀ x ∈ S \ f ==> C(x) ≥ C('_') 3. C('a') < C('b') < ... < C('z') C('A') < C('B') < ... < C('Z') C('1') = C('0')+1 ... C('9') = C('8')+1

4. Intervalele: [C('a') , C('z')], [C('A') , C('Z')], [C('0') , C('9')] şi mulţimea C(s) au intersecţiile vide două câte două.

128 80 Ç129 81 ü

160 A0 á161 A1 í

192 C0 193 C1

224 E0 α225 E1 ß

29

Page 31: LIMBAJE DE PROGRAMARE

130 82 é131 83 â132 84 ä133 85 à134 86 å135 87 ç136 88 ê137 89 ë138 8A è139 8B ï140 8C î141 8D ì142 8E Ä143 8F Å144 90 É145 91 æ146 92 Æ147 93 ô148 94 ö149 95 ò150 96 û151 97 ù152 98 _153 99 Ö154 9A Ü155 9B ¢156 9C £157 9D ¥158 9E _159 9F ƒ

162 A2 ó163 A3 ú164 A4 ñ165 A5 Ñ166 A6 ª167 A7 º168 A8 ¿169 A9 _170 AA ¬171 AB ½172 AC ¼173 AD ¡174 AE «175 AF »176 B0 177 B1 178 B2 179 B3 180 B4 181 B5 182 B6 183 B7 184 B8 185 B9 186 BA 187 BB 188 BC 189 BD 190 BE 191 BF

194 C2 195 C3 196 C4 197 C5 198 C6 199 C7 200 C8 201 C9 202 CA 203 CB 204 CC 205 CD 206 CE 207 CF 208 D0 209 D1 210 D2 211 D3 212 D4 213 D5 214 D6 215 D7 216 D8 217 D9 218 DA 219 DB 220 DC 221 DD 222 DE 223 DF

226 E2 Γ227 E3 π228 E4 Σ229 E5 σ230 E6 μ231 E7 τ232 E8 Φ233 E9 Θ234 EA Ω235 EB δ236 EC ∞237 ED φ238 EE ε239 EF ∩240 F0 ≡241 F1 ±242 F2 ≥243 F3 ≤244 F4 ⌠245 F5 ⌡246 F6 ÷247 F7 ≈

248 F8 ° 249 F9 ⋅ 250 FA ⋅

251 FB √252 FC _253 FD ²254 FE

255 FF Tabelul 3.4 Extinderea codului ASCII

Un prim sistem de codificare stabilit a fost EBCDIC (Extended Binary Decimal Interchange Code), care foloseşte pentru codificare numerele întregi din intervalul [0,255]. Calculatoarele medii-mari de tip IBM-360, IBM-370, precum şi FELIX-C folosesc codul EBCDIC.

În prezent, cel mai folosit sistem de codificare este ASCII (American Standard Code for Information Interchange), care foloseşte pentru codificare numerele întregi din intervalul [0, 127].

30

Page 32: LIMBAJE DE PROGRAMARE

Faţă de condiţiile 1-4 de mai sus, acest standard de codificare mai verifică condiţia:

5. C('z') = C('a') + 25, C('Z') = c('A') + 25

Tabelul 3.3 conţine standardul ASCII. Fiecare coloană a tabelului conţine mai întâi numărul de cod în zecimal, apoi acelaşi număr în hexazecimal, urmate de simbolul din alfabet care se codifică. Corespunzător coloanei caracterelor funcţionale, se precizează şi simbolul pe care-l codifică maşinile IBM-PC, precum şi prescurtarea internaţională corespunzătoare caracterului funcţional codificat.

Calculatoarele IBM-PC folosesc codificarea ASCII extinsă, folosind întregii din intervalul [0,255]. Dintre aceste numere, cele din intervalul [0,127] păstrează codificarea ASCII standard, iar celelalte coduri desemnează o serie de caractere noi. Tabelul 3.4 prezintă extinderea codificărilor pe intervalul [128,255].

31

Page 33: LIMBAJE DE PROGRAMARE

CAPITOLUL 4.Algoritmică şi programareRezolvarea unei probleme cu ajutorul calculatorului

presupune parcurgerea următoarelor faze:1) Precizarea completă a problemei de rezolvat (specificarea);2) Proiectarea algoritmilor de rezolvare a problemei

(proiectarea);3) Programarea propriu-zisă a algoritmilor într-un limbaj de

programare (codificarea);4) Testarea şi verificarea (testarea);5) Elaborarea documentaţiei de realizare şi a domentaţiei de

exploatare (documentaţia);6) Exploatarea şi întreţinerea programului (exploatarea).

Toate aceste faze constituie ciclul de viaţă al programului sau al produsului program. Prin produs program se înţelege tot ceea ce se produce prin activităţile de mai sus. Nu detaliem mai mult fiecare din fazele anterioare, aceasta ar depăşi cadrul acestei cărţi. Ne vom axa mai mult pe fazele 1), 2) şi 3) pentru problemele didactice care le vom rezolva.

4.1. AlgoritmNoţiunea de algoritm nu se poate defini foarte uşor. O definiţie matematică, riguroasă

este greu de dat chiar şi cu ajutorul altor noţiuni. Vom încerca în continuare să descriem ce se înţelege prin algoritm.

Prin algoritm vom înţelege un set finit de reguli – propoziţii scrise într-un limbaj de descriere a algoritmilor - care procesează, calculează, rezolvă o problemă.

Mai în detaliu, pentru fiecare problemă P există date presupuse cunoscute (date iniţiale pentru algoritmul corespunzător, A) şi rezultate care se cer a fi găsite (date finale). Evident, s-ar putea ca problema să nu aibă sens pentru orice date iniţiale. Vom spune că datele pentru care problema P are sens fac parte din domeniul D al algoritmului A. Rezultatele obţinute fac parte dintr-un domeniu R, astfel că executând algoritmul A cu datele de intrare x∈D vom obţine rezultatele r∈R. Vom spune că A(x)=r şi astfel algoritmul A defineşte o funcţie:

A : D RAlgoritmii au următoarele caracteristici: generalitate,

finitudine şi generalitate.Prin generalitate se înţelege faptul că un algoritm este

aplicabil pentru orice date iniţiale x. Deci un algoritm A nu rezolvă problema P numai cu anumite date de intrare particulare, ci o rezolvă în general, oricare ar fi aceste date din D. Astfel algoritmul lui Euclid de determinarea a

32

Page 34: LIMBAJE DE PROGRAMARE

celui mai mare divizor comun al două numere naturale a şi b rezolvă problema pentru orice alegere a lui a şi b şi nu numai pentru, să zicem, a > b.

Prin finitudine se înţelege că regulile algoritmului sunt în număr finit şi numărul transformărilor ce trebuie aplicate unei informaţii admisibile x∈D pentru a obţine rezultatul final corespunzător este finit. Deci rezulatele se obţin după un număr finit de transformări.

Prin unicitate se înţelege că toate transformările prin care trece informaţia iniţială pentru a obţine rezultatul r∈R sunt bine determinate de regulile algorimului. Aceasta înseamnă că fiecare pas din execuţia algoritmului dă rezultate bine determinate şi precizează în mod unic pasul următor. Astfel spus, ori de câte ori am executa algoritmul, pornind de la aceeaşi informaţie admisibilă x∈D, transformările prin care se trece şi rezultatele obţinute sunt aceleaşi.

În descrierea algoritmilor se folosesc mai multe limbaje de descriere, dintre care cele mai des folosite sunt:

- schemele logice;- limbajul pseudocod.Ne vom referi în continuare la limbajul pseudocod.

4.2. Limbajul pseudocodLimbajul pseudocod este un limbaj inventat în scopul

proiectării algoritmilor şi este format din propoziţii asemănătoare propoziţiilor limbii române, care corespund structurilor de calcul şi structurilor de control folosite în construirea algoritmilor. Limbajul pseudocod are două feluri de propoziţii:

- propoziţii standard;- propoziţii nestandard.

Deoarece realizarea unui algoritm pentru rezolvarea unei probleme nu este întotdeauna o problemă uşoară se vor obţine forme intermediare a algoritmului. În acest proces de rafinare se vor folosi pentru elementele nefinisate la un anumit nivel propoziţiile nestandard. Acestea sunt texte care descriu părţi ale algoritmului incomplet elaborate asupra cărora se va reveni la un alt nivel de detaliere. Începutul propoziţiilor nestandard se marchează printr-un semn special (# sau @ de exemplu) iar sfârşitul prin punct. Propoziţiile standard au sintaxă şi semantică bine precizate şi reflectă în algoritm structurile de calcul şi de control ale procesului de rezolvare a problemei.Pe langă aceste propoziţii se pot folosi şi comentarii scrise între acolade şi de obicei scrise pe linia propoziţiilor standard şi nestandard.

33

Page 35: LIMBAJE DE PROGRAMARE

Pentru marcarea începutului descrierii unui algoritm se foloseşte propoziţia standard:

ALGORITMUL numele_algoritmului ESTE:Sfârşitul unui algoritm se marchează prin propoziţia standard:

SFÂRŞIT ALGORITM.Cele două propoziţii standard nu au decât semnificaţia delimitării descrierii unui algoritm de restul contextului în care apare.

Pentru citirea datelor iniţiale, care se presupun că sunt cunoscute în problemă se folosesc propoziţiile standard:

DATE listă;sau

CITEŞTE listă;unde listă conţine toate numele variabilelor a căror valoare iniţială este cunoscută.Afişarea (tipărirea) sau precizarea rezultatelor obţinute se face cu ajutorul propoziţiilor standard:

TIPĂREŞTE listă;sau

REZULTATE listă;unde listă conţine toate variabilele ce doresc să fie afişate sau care s-au obţinut în urma procesului de calcul descris de algoritm.

Calculele se descriu prin atribuiri cu ajutorul propoziţiei standard:

[FIE] variabilă := expresie;unde expresie este de obicei o expresie de calcul (sau o variabilă) iar variabilă este o variabilă căruia i se atribuie rezultatul obţinut după efectuarea calculelor indicate în expresie.

Structura de control de alternativă este de 3 tipuri:- alternativa simplă;- alternativa completă;- alternativa generalizată.

Alternativa simplă se descrie prin propoziţia standard:

DACĂ condiţie ATUNCI S SFÂRŞITDACĂ;

34

Page 36: LIMBAJE DE PROGRAMARE

unde condiţie este de obicei o expresie relaţională sau logică iar S este un grup de propoziţii standard (poate fi şi doar o propoziţie standard).Execuţie:

1) Se evaluează condiţia;2) Dacă condiţia este adevărată atunci se execută S şi se trece

la propoziţia următoare; dacă condiţia nu este îndeplinită (falsă) atunci se trece automat la propoziţia următoare.

Alternativa completă se descrie prin propoziţia standard:

DACĂ condiţie ATUNCI S1 ALTFEL S2

SFÂRŞITDACĂExecuţie:

1) Se evaluează condiţia;2) Dacă condiţia este adevărată atunci se execută S1 şi se

trece la propoziţia următoare; dacă condiţia nu este adevărată (falsă) atunci se execută S2 şi se trece la propoziţia următoare.Alternativa generalizată se descrie prin propoziţia standard:

SELECTEAZĂ i DINTREV1 : S1;V2 : S2;. . . Vn : Sn

SFÂRŞITSELECTEAZĂ

Execuţie:1) Dacă valoarea lui i este egală cu Vi atunci se execută secvenţa Si.2) Se dă controlul propoziţiei de după SFÂRŞITSELECTEAZĂ.Structura de control repetitivă are trei forme:

- repetiţia anterior testată (pretestată);- repetiţia posterior testată (posttestată);- repetiţia cu un număr determinat (şi aprioric cunoscut)

de execuţii.

Repetiţia pretestată se descrie prin propoziţia standard:

CÂTTIMP condiţie EXECUTĂS;

SFÂRŞITCÂTTIMP;Execuţie:

1) Se evaluează condiţia;2) Dacă condiţia este adevărată atunci se execută S şi se reia

35

Page 37: LIMBAJE DE PROGRAMARE

evaluarea condiţiei; dacă condiţia nu este adevărată atunci se trece la propoziţia imediat următoare.

În S trebuie modificată într-un fel oarecare condiţia, altfel ciclul ar deveni infinit dacă la prima evaluare condiţia ar fi adevărată.

Repetiţia posttestată se descrie prin propoziţia standard:

REPETĂS;

PÂNĂCÂND condiţie;SFÂRŞITREPETĂ;

Execuţie:

1) Se execută S;2) Dacă condiţia este falsă se reia execuţia S; dacă condiţia

este adevărată atunci se trece la propoziţia imediat următoare.

În S trebuie modificată într-un fel oarecare condiţia, altfel ciclul ar deveni infinit dacă la prima evaluare condiţia ar fi falsă.

Structura posttestată REPETĂ … de mai sus este echivalentă cu următoarea secvenţă:

S;CÂTTIMP not condiţie EXECUTĂ

S;SFÂRŞITCÂTTIMP;

Repetiţia predefinită, cu număr determinat de execuţii se descrie prin propoziţia standard:

PENTRU contor:=liminiţială;limfinală[;pas] EXECUTĂS;

SFÂRŞITPENTRUAceastă structură este echivalentă cu secvenţa următoare:

contor:=liminiţială;REPETĂ

S;contor:=contor+pas;

PÂNĂCÂND (contor > limfinală şi pas > 0) sau (contor < limfinală şi pas < 0)

SFÂRŞITREPETĂ;36

Page 38: LIMBAJE DE PROGRAMARE

Execuţie:1) Se iniţializează contorul cu liminiţială;2) Se execută S; şi se incrementează contorul cu pas (dacă

pasul este 1 nu este obligatoriu să fie pus în evidenţă, e implicit);

3) Se testează dacă contorul nu depăşeşte limita finală şi se reia punctul 2); dacă contorul depăşeşte limita finală atunci se trece controlul la propoziţia următoare.

Exemple:1) Să se determine dacă un număr natural, n este palindrom sau

nu. Precizăm că un număr este palindrom dacă valoarea sa este egală cu valoarea oglinditului său (citit de la dreapta spre stânga).

Algoritmul Palindrom Este: { Problema palindromului }Date n; x := n; { x variabilă internă de lucru

egală la început cu n}m := 0; { în m se va construi oglinditul

lui n}CatTimp x > 0 Executa

c := x modulo 10; { în c se izolează ultima cifră a lui x, pas cu pas}

m := m*10 + c; { se adaugă c la m}x := x div 10; { se imparte x la 10 şi se reţine

câtul }Sfarsit CatTimp ;Daca m = n atunci Tipăreşte “numarul este palindrom”

altfel Tipăreşte “numarul este palindrom”

SfarsitDaca;Sfarsit Algoritm

2) Să se determine cel mai lung platou dintr-un vector de numere întregi, ordonat crescător.

Specificaţia problemei este:

Intrare: n, a[i], i∈{0,1,...,n-1} Un platou este caracterizat de o pereche de indici (j,k) cu:

37

Page 39: LIMBAJE DE PROGRAMARE

j≤k ∧ (a[j-1]<a[j]=a[k]<a[k+1]) iar lungimea lp=k+1-j.

Ieşire: Cel mai lung platou este caracterizat de predicatul: (∃ j:0≤j≤n-p ∧ a[j]=a[j+p-1]) ∧ (∀ k:0≤k≤n-p-1) ═> a[k]≠a[k+p])

prima abordare

Algoritmul platou Este: { Problema platoului } i:=1; { i este indicele de scanare a tabloului a } lp:=1; { lp conţine lungimea platoului maxim } CatTimp i≤n-1 Executa # determină lp { propoziţie nestandard } Sfarsit CatTimp Sfarsit Algoritm

a doua rafinare

Algoritmul platou Este: { Problema platoului } i:=1; { i este indicele de scanare a tabloului a } lp:=1; { lp conţine lungimea platoului maxim } CatTimp i≤n-1 Executa Daca a[i-lp] ═ a[i] Atunci lp:=lp+1; Sfarsit Daca; i:=i+1; Sfarsit CatTimp Sfarsit Algoritm.

4.3. STRUCTURA GENERALĂ A UNUI PROGRAM C4.3.1. ISTORIC, CONCEPŢIE , EVOLUŢIE

38

Page 40: LIMBAJE DE PROGRAMARE

Limbajul C a fost finalizat în 1972 de Dennis M. Ritchie şi Brian W. Kernighan de la firma americană Bell Laboratories. Prima versiune a limbajului se numeşte BCPL apoi următoarele poartă numele de A, B şi C. Cei doi autori au dezvoltat aceste prime versiuni în jurul sistemului de operare UNIX. La vremea respectivă din aproximativ 13000 linii sursă ale UNIX-ului doar 200 de linii sursă nu erau scrise în limbajul C. De acest fapt se leagă detractorii limbajului care spun că limbajul C nu este un limbaj deosebit ci doar un fel de limbaj “oficial” al sistemului de operare UNIX.

În anul 1978 apare manualul The C Programming Language care este de fapt şi prima standardizare a limbajului. Cei doi autori intră astfel în istorie...

După anul 1980 odată cu dezvoltarea hardware apar şi primele PC-uri iar acestea implică şi produse software adecvate. Principalele firme producătoare de sofware - MICROSOFT şi BORLAND - au dezvoltat unelte adecvate pentru programarea şi utilizarea limbajului C. Deocamdată firma BORLAND deţine supremaţia prin versiunile mediului BORLAND C. Cele mai folosite sunt versiunile 2.0, 3.1, 4.0. În ultimii doi ani au apărut aşa numitele medii “visuale”: VISUAL C versiunile 4.5 şi 5.0 care sunt de fapt extensii ale mediului BORLAND C adaptate programării orientate obiect şi interfeţei grafice WINDOWS 95. Mediile de programare BORLANDC pot compila 3 tipuri de programe sursă C:

- fişiere cu extensia .C (fişiere cu programe standard C);

- fişiere cu extensia .CP (fişiere cu programe C+, un C extins);

- fişiere cu extensia .CPP (fişiere cu programe C++).

Menţionăm că limbajul C++ a fost elaborat de Bjarne Stroustrup de la AT&T. El este un superset al limbajului C şi permite principalele concepte ale programării prin abstractizarea datelor şi programării orientate spre obiecte.

Limbajul C este un limbaj hibrid având facilităţi caracteristice limbajelor de asamblare cât şi facilităţi ale limbajelor de înalt nivel.Câteva dintre principalele caracteristici ale limbajului C sunt:a) portabilitate: chiar dacă acest concept nu-i definit

foarte riguros spunem căcă un program este portabil dacă el poate

fi transferat uşor de laun tip de calculator la altul; limbajul C este

un astfel de limbaj;b) flexibilitate: compilatorul face un număr mai redus de controale (face multe

39

Page 41: LIMBAJE DE PROGRAMARE

conversii implicite);b) programare structurată: limbajul are principalele structuri

ale programării structurate: structura secvenţială, structuraiterativă structura de selecţie;d) compactizare: unele instrucţiuni sunt scrise foarte compact; de exemplu i:=i+1

se poate scrie mai scurt ca i++;e) lucrul pe biţi şi calcule cu adrese.

4.3.2. CONCEPTUL DE FUNCŢIEUn program C se compune din una sau mai multe funcţii.

Funcţia este o unitate lexicală de program compilabilă independent. Una dintre funcţii este funcţie principală, numele ei este predefinit şi anume main. Execuţia programului începe cu prima instrucţiune din funcţia principală. Dăm în continuare 2 exemple de structuri de program (fişiere sursă):

1) directive de preprocesare 2) directive de preprocesare

declaraţii de date globale declaraţii de date globale

declaraţie prototip funcţia f1 implementare funcţia f1 . . . . . . declaraţie prototip funcţia fn implementare funcia fn

void main(void) void main(void) { declaraţii { declaraţii instrucţiuni instrucţiuni } }

implementare funcţia f1 . . . implementare funcţia fn

Funcţia principală main este obligatorie pentru orice

40

Page 42: LIMBAJE DE PROGRAMARE

program celelalte elemente fiind optionale. Pentru ca o anumită funcţie să poată fi apelată e necesar ca ea să aibă declarat prototipul dacă implementarea (definiţia) ei se află după funcţia main (exemplul 1). Dacă funcţia principală se află la sfârşitul fişierului atunci nu mai e necesar prototipul funcţiei apelate ci doar implementarea ei (exemplul 2). Comparând structura unui program C cu structura unui program PASCAL se observă nivelul de imbricare diferit. În PASCAL apare o imbricare a procedurilor şi funcţiilor pe când în C nu există o astfel de imbricare (rămâne la nivelul fiecărei funcţii dacă e cazul).

PASCAL C

...

4.3.2.1. Definiţia unei funcţiiDefiniţia unei funcţii în limbajul C se compune din

antet şi corp. O funcţie poate fi apelată dacă este precedată de definiţia sau de prototipul ei. Aceste lucruri care sunt valabile în limbajul C se regăsesc şi în limbajul C++.

4.3.2.2. Antet şi prototipAntetul simplificat al unei funcţii în C are formatul:

tip nume_funcţie (lista_parametrilor_formali)unde: tip - reprezintă tipul valorii returnate de funcţie sau dacă funcţia nu

returnează nici o valoare se pune cuvântul cheie void;nume_funcţie - reprezintă un identificator clasic

format dintr-un mixaj de litere şi cifre, primul caracter fiind

obligatoriu litera; 41

Page 43: LIMBAJE DE PROGRAMARE

printre litere se numără şi liniuţa de subliniere;

lista_parametrilor_formali – nume de variabile separate prin virgule. Exemple:1) double radical (double x) // calculeaza radacina patrata din x si returneaza valoarea gasita2) double radical_n (double x, int n) // calculeaza radacina de ordinul n din x

Prototipul unei funcţii este asemănător antetului dar la sfârşit se pune caracterul “;”

4.3.2.3. Corpul unei funcţiiCorpul unei funcţii C se compune din declaraţii de

variabile locale şi instrucţiuni scrise între acolade conform figurii de mai jos.

{ declaraţii instrucţiuni }

Şi pentru că autorii limbajului C consideră că un limbaj de programare se învaţă mai repede scriind şi executând programe căt mai timpuriu vom da un mic exemplu de funcţie.

int modul (int i) // determina valoarea absoluta a intregului i si retruneaza aceasta valoare{ if (i < 0) return –i; if (i = = 0) return 0; else return i;}

4.4. EXPRESII, OPERANZI, OPERATORI4.4.1. EXPRESII

O expresie în limbajul C este formată fie dintr-un operand fie din mai mulţi operanzi legaţi între ei prin operatori. O expresie are o valoare şi un tip care se determină aplicând operatorii conform priorităţilor şi asociativităţii acestora.

În limbajul C operatorii se asociază de la stânga la dreapta, exceptând operatorii unari şi de atribuire, care se asociază de la dreapta la stânga.. Totodată pot fi folosite parantezele rotunde pentru a impune o anumită ordine în executarea operaţiilor.

4.4.2. OPERANZI42

Page 44: LIMBAJE DE PROGRAMARE

Un operand în limbajul C poate fi una din următoarele elemente:- o constantă;- o constantă simbolică;- numele unei variabile simple;- numele unui tablou;- numele unei structuri;- numele unei funcţii;- referirea la elementul unui tablou (variabilă cu indici);- referirea la elementul unei structuri;- apelul unei funcţii;- o expresie inclusă în paranteze rotunde.

Exemple: 9876 - constantă întreagă; x - variabilă simplă; t[i][3] - variabilă cu indici; 0xabcd - constantă hexazecimală; t - nume de tablou; (expresie) - expresie inclusă în paranteze rotunde. f1 - numele unei funcţii

4.4.3. OPERATORIOperatorii limbajului C pot fi grupaţi în mai multe

clase, dar oricum ei pot fi folosiţi împreună într-o aceeaşi expresie. Operatorii au arităţi diferite: unari, binari, ternari şi totodată o anumită prioritate implicită care e redată în tabelul de mai jos. Operatorii de aceeaşi prioritate se află trecuţi în aceeaşi linie. Liniile tabelulul conţin operatorii limbajului C în ordinea descrescătoare a priorităţilor. Astfel în prima linie se află operatorii de prioritate maximă, iar în ultima linie operatorul virgulă cu prioritatea cea mai mică. Cu excepţia operatorilor “.”, “->”,”&”,”*”, a parantezelor rotunde (folosite la definiţia şi apelul funcţiilor) şi a parantezelor drepte (folosite la variabilele cu indici) ceilalţi operatori vor fi explicaţi în această lecţie.

( ) [ ] . ->- (unar) +(unar) *(unar) &(unar) ! ~ ++ -- (tip) sizeof* / %+ -<< >>< <= >= >

43

Page 45: LIMBAJE DE PROGRAMARE

= = !=&^|&&| |? : (ternar)= op= op poate fi: *(binar) / % +(binar) –(binar) << >> & ^ |,

4.4.3.1. Operatori aritmeticiLista operatorilor aritmetici este redată mai jos:

- (minus unar); + (plus unar); * / % operatori binari multiplicativi; (înmulţire, împărţire, restul împărţirii întregi); + - operatori binari aditivi (adunare şi scădere).

Operatorii de pe aceeaşi linie au aceeaşi prioritate. Cei unari au prioritate mai mare decât cei binari. Operatorii multiplicativi au prioritate mai mare decât cei aditivi.

Exemple:int i,j,k;float x,y;double t[10];

// se dau cateva exemple de expresii folosind operatorii aritmeticii*x+t[5];-y+k;i%j; // daca i=9 si j=4 atunci i%j are valoarea 1i/j; // daca i=9 si j=4 atunci i/j are valoarea 2x*-y; // - este operatorul unar deci avem x*(-y)

4.4.3.2. Operatori relaţionaliLista operatorilor relaţionali este redată astfel:

< (mai mic)<= (mai mic sau egal; cele două caractere ce compun operatorul sunt concatenate)

> (mai mare)>= (mai mare sau egal; cele două caractere ce compun operatorul sunt concatenate)

Toţi operatorii relaţionali au aceeaşi prioritate. Ea este mai mică decât prioritatea operatorilor aditivi. Rezultatul aplicării unui operator relaţional este 1 sau 0, după cum operanzii se află în relaţia definită de operatorul respectiv sau nu.

44

Page 46: LIMBAJE DE PROGRAMARE

Exemple: a= 4 şi b= -5atunci a>0 are valoarea 1;

a<=0 are valoarea 0;a+b>0 are valoarea 0;a>=b are valoarea 1;a<0 are valoarea 0;a+b>=b-a are valoarea 1;a+b>=(b-a)*(b-a) are valoarea 0;

4.4.3.3. Operatori de egalitateLista operatorilor de egalitate este redată mai jos:

= = (egal; două semne “=” concatenate)!= (diferit; semnele sunt concatenate).

Operatorii de egalitate au ambii aceeaşi prioritate şi este imediat mai mică decât a operatorilor relaţionali. Operatorul “= =” testează egalitatea a doi operanzi. Dacă operanzii sunt egali atunci rezultatul operaţiei “= =” este 1, în caz contrar este 0. Operatorul “!=” furnizează rezultatul 1 când cei doi operanzi sunt diferiţi şi 0 când sunt egali.

Exemple:a= 2 şi b=-1

atuncia= =b are valoarea 0;a!=b are valoarea 1;a*b!=a+b are valoarea 1;

4.4.3.4. Operatori logiciLista operatorilor logici este redată mai jos:

! (negaţia logică - operator unar);&& (ŞI logic);|| (SAU logic).

Operatorul “!” are aceeaşi prioritate cu operatorii unari “+” şi “-“. Operatorul “&&” este mai prioritar decât operatorul “||”, dar are o prioritate mai mică decât operatorii de egalitate.

În limbajul C nu există valori logice speciale. Valoarea fals se reprezintă prin zero. Orice valoare diferită de zero reprezintă valoarea adevărat.

Dacă operatorul “!” se aplică la un operand a cărui valoare este zero, atunci rezultatul este 1. Dacă acelaşi operator se aplică la un operand a cărui valoare este diferită de zero, atunci rezultatul este 0.

Dăm în continuare tabelele operatorilor logici binari aplicate valorilor 0 şi 1.

&& 0 1 || 0 1 45

Page 47: LIMBAJE DE PROGRAMARE

sau exclusiv 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0

Chiar dacă pentru “sau exclusiv” nu există operator el se poate realiza prin expresia următoare aplicată operanzilor a şi b: !a&&b||!b&&a sau folosind parantezele rotunde ((!a) &&b)||((!b)&&a).

Operatorii logici se evaluează de la stânga la dreapta. Dacă la evaluarea unei expresii se ajunge într-un punct în care se cunoaşte valoarea întregii expresii, atunci restul expresiei nu se mai evaluează. Dacă a=0 şi b=1 atunci expresia ! a||b are valoarea 1 pentru că !a are deja valoarea 1.

4.4.3.5. Operatori logici pe biţiLista operatorilor logici pe biţi este redată mai jos în

ordinea descrecătoare a priorităţilor:~ (operator unar; complement faţă de 1)>> << (deplasări la dreapta, respectiv la

stânga)& (ŞI pe biţi)^ (SAU-EXCLUSIV pe biţi)| (SAU pe biţi)

Operatorul “~”, fiind unar, are aceeaşi prioritate ca şi ceilalţi operatori unari ai limbajului C. El schimbă fiecare bit 1 al operandului în 0 şi invers.

Operatorul “>>” realizează deplasarea la dreapta care este echivalentă cu o împărţire întreagă cu puteri a lui 2; a >> 3 este echivalentă cu [a/23].

Operatorul “<<” realizează deplasarea la stânga care este echivalentă cu o înmulţire cu puteri a lui 2; a << 3 este echivalentă cu a*8.

Pentru operatorii &, |, ^ dăm în continuare tabelele operaţiilor:

& 0 1 | 0 1 ^ 0 1 0 0 0 0 0 1 0 0 1 1 0 1 1 1 1 1 1 0

Observaţii:1o. Operanzii care nu ocupă un cuvânt (16 biţi) se extind la un

46

Page 48: LIMBAJE DE PROGRAMARE

cuvânt. De exemplu expresia ~0 are ca rezultat un cuvânt cu toţi biţi egali cu 1.2o. Operatorii logici pe biţi se execută bit cu bit spre deosebire de operatorii logici care se evaluează global. De exemplu dacă x=2 i y=1 sunt variabile de tip int atunci:

x&&y are valoarea 1 pentru că ambii operanzi sunt diferiţi de 0. x&y are valoarea 0 conform schemei de mai jos 0000 0000 0000 0010 0000 0000 0000 0001

& 0000 0000 0000 0000

3o. Operatorul & se foloseşte frecvent pentru a anula biţi din configuraţia unui cuvânt, iar operatorul | pentru a seta (pune) biţi într-un anumit mod;4o. Operanzii trebuie să fie întregi (de tipul int sau long).5o. Atenţie la deplasări nu se modifică valoarea operandului; deci trebuie să facem o atribuire; de exemplu a = a << 3 va modifica valoarea lui a pe când a << 3 nu modifică valoarea lui a.

Exemple:1) Fie declaraţia:

int i;atunci expresia i >> 8 & 255 are ca rezultat valoarea celui mai semnificativ octet a lui i; i >> 8 deplasează octetul mai semnificativ al lui i în poziţia mai puţin semnificativă; se face apoi un ŞI logic pe biţi cu masca 255 care păstrează octetul mai puţin semnificativ.

2) Fie expresia: (x >> 6) & ~(~ 0 << 3) Să presupunem că x are valoarea în biţi 1010 1011 1000 1101. Atunci x>>6 are valoarea 1111 1110 1010 1110 Al doilea operand pregăteşte o mască astfel:

~0 1111 1111 1111 1111 ~0<<3 1111 1111 1111 1000 ~(~0<<3) 0000 0000 0000 0110

Rezultatul final este dat de: 0000 0000 0000 0111

1111 1110 1010 11100000 0000 0000 0110

Practic s-a obţinut valoarea biţilor 8,7,6 a lui x (numerotaţi de la dreapta la stânga începând cu 0).

47

Page 49: LIMBAJE DE PROGRAMARE

4.4.3.6. Operatori de atribuireÎn forma cea mai simplă operatorul de atribuire se

notează cu “=” şi se utilizează în construcţii de forma: v=expresie;

(v este fie o variabilă simplă, fie variabilă cu indici sau un element de structură).

Această construcţie se mai numeşte expresie de atribuire. Ea este considerată ca fiind un caz particular de expresie. Tipul ei coincide cu tipul lui v, iar valoarea întregii expresii este chiar valoarea atribuită lui v.

O expresie de forma: v1=(v=expresie);

este şi ea legală şi se efectuează în felul următor :- se evaluează expresia expresie şi valoarea ei se atribuie

lui v;- valoarea lui v se atribuie apoi şi lui v1.

Deoarece operatorii de atribuire se asociază de la dreapta la stânga, expresia de mai sus se poate scrie şi fără paranteze: v1=v=expresie;

În general, putem realiza atribuiri multiple printr-o expresie de forma:

vn =. . . =v1=v=expresie

Dacă expresia din dreapta semnului egal are un tip diferit de cel al variabilei v, atunci întâi valoarea ei se converteşte spre tipul variabilei v şi pe urmă se realizează atribuirea,

Pentru operaţia de atribuire, în afara semnului egal se mai poate folosi şi succesiunea :

op=

unde prin op se înţelege unul din operatorii binari aritmetici sau logici pe biţi, adică unul din următorii:

% / * - + & ^ | << >>

Acest mod de construcţie se foloseşte pentru a compacta un anumit tip de atribuire. Astfel expresia:

v op = expresie;

este identică cu expresia de atribuire:

v = op expresie;Exemple:

int i, j;double x, y;int v[10];

48

Page 50: LIMBAJE DE PROGRAMARE

i=5;j=10;x=y=10.01;i +=1; // echivalenta cu i=i+1 si cu i++x*=3; // echivalenta cu x=x*3j<<=10; // echivalenta cu j=j<<10v[i]*=i // echivalenta cu v[i]=v[i]*ix /= x-y // echivalenta cu x = x/(x-y)

4.4.3.7. Operatori de incrementare şi decrementareAceşti operatori sunt unari şi au aceeaşi prioritate cu ceilalţi operatori unari ai

limbajului C. Operatorul de incrementare se notează prin “++” şi măreşte valoarea operandului cu unu, iar operatorul de decrementare se notează prin “- -“ şi micşorează valoarea operandului cu unu. Operatorii sunt folosiţi prefixat şi postfixat. Astfel operatorii prefixaţi au notaţia:

++operand; - - operand;

Ei se aplică mai întâi şi apoi se foloseşte valoarea lor.Astfel operatorii postfixaţi au notaţia:

operand++; operand - -;

Se foloseşte valoarea operanzilor şi apoi se aplică incrementarea sau decrementarea.Menţionăm că aceşti operatori se pot aplica numai la următorii operanzi:- variabilă simplă;- variabilă cu indici;- referire la elementul unei structuri.

Exemple:int i,j;double x,y;int vector [5];j=i++; // este echivalent cu j=i si i=i+1; y=--x; // este echivalent cu x=x-1 si y=x;i=++vector[j] // este echivalent cu vector[j]=vector[j]+1 si i=vector[j]

4.4.3.8. Operatorul de conversie explicită (expresie cast)

Pentru forţarea tipului unui operand se foloseşte o construcţie de forma:

(tip) operand

Prin aceasta valoarea operandului se converteşte spre tipul indicat în paranteze.Exemplu:

int i,j;double y;i=8; j=5;y=i/j; // y are valoarea 1, pentru ca se face impartirea intreaga i/j

Dacă vom converti operanzii i şi j spre tipul double se va obţine rezultatul corect adică 1.6.

49

Page 51: LIMBAJE DE PROGRAMARE

Deci:int i,j;double y;i=8; j=5;y=(double) i / (double) j; // y are valoarea 1.6,

Construcţia (tip) este un operator unar prin care se explicitează conversia dorită. Are aceeaşi prioritate ca restul operatorilor unari.

4.4.3.9. Operatorul dimensiune (sizeof)

Pentru a determina lungimea în octeţi a unei date se poate folosi construcţia:

sizeof (data)

unde data poate fi:- numele unei variabile simple;- numele unui tablou;- numele unei structuri;- numele unui tip;- referirea la elementul unui tablou sau structură.

Exemple:int i;long l;float f;double d;char c;int itablou[5];double dtablou[5];sizeof (i) // are valoarea 2;sizeof (l) // are valoarea 4;sizeof (f) // are valoarea 4;sizeof (d) // are valoarea 8;sizeof (c) // are valoarea 1;sizeof (itablou[1]) // are valoarea 2;sizeof (dtablou[1]) // are valoarea 8;sizeof (itablou) // are valoarea 10;sizeof (dtablou) // are valoarea 40;

50

Page 52: LIMBAJE DE PROGRAMARE

4.4.3.10. Regula conversiilor implicite

În general o expresie C conţine operanzi de tipuri diferite. Pentru operatorii binari există situaţii când operanzii nu sunt de acelaşi tip şi trebuie executate conversii astfel încât operatorii să se aplice pentru operanzi de acelaşi tip. Aceste conversii le face automat compilatorul. Există o regulă a conversiilor implicite care are următorii paşi:- fiecare operand de tip char se converteşte spre tipul int şi fiecare operand de tipul float

se converteşte spre double;- dacă unul dintre operanzi este de tip double atunci şi celălalt se converteşte spre tipul

double şi rezultatul va avea tipul double;- dacă unul dintre operanzi este de tip long, atunci şi celălalt se converteşte spre tipul long

şi rezultatul va avea tipul long;- dacă unul dintre operanzi este de tip unsigned, atunci şi celălalt se converteşte spre tipul

unsigned şi rezultatul va fi de tipul unsigned;- la acest pas se ajunge numai dacă ambii operanzi sunt de tip int şi deci operaţia se execută

cu operanzii respectivi, iar rezultatul va fi de tip int.Aplicând regula de mai sus pas cu pas (la fiecare operator în momentul efectuării lui), se

ajunge în final la evaluarea întregii expresii şi prin acesta se determină tipul expresiei. Regula conversiilor implicite nu se aplică pentru operatorul de atribuire (valoarea expresiei din partea dreaptă a semnului de atribuire se converteşte spre tipul variabilei din stânga semnului egal).Exemple:

int i, j, k;float a, b;double x, y;unsigned p;long r;char c;

expresii conversii tipul expresiei

i-j/k nu int a/b a spre double

b spre double doublex+y nu doublei+a a spre double

i spre double doublei-3.14 i spre double double i+3 nu inti+x i spre double doublei-c c spre int intx+10 10 spre double doublep-10 10 spre unsigned unsignedr*5 5 spre long long(double)(i/j) se realizează împărţirea întreagă între

i şi j şi rezultatul se converteşte spre double

Dacă rezultatul unei operaţii depăşeşte domeniul de valori ce corespunde tipului rezultatului, valoarea respectivă se trunchiază şi rezultatul este eronat.

48

Page 53: LIMBAJE DE PROGRAMARE

4.4.3.11. Operatori condiţionali

Operatorii condiţionali sunt ? şi : şi se folosesc împreună în construcţii de forma:exp1 ? exp2 : exp3

Evaluarea se face astfel:- se evaluează expresia exp1;- dacă exp1 este diferită de zero, atunci valoarea şi tipul expresiei condiţionale sunt egale cu

valoarea şi tipul expresiei exp2; altfel cu expresia exp3.Exemplu: procesul de determinare a maximului a două numere a şi b este:

dacă a>b atunci max=aaltfel max=b

sfdacă

În limbajul C se poate realiza acest proces cu ajutorul operatorilor condiţionali astfel:max= a>b ? a : b

Dacă a>b atunci expresia condiţională are valoarea şi tipul lui a altfel expresia condiţională are valoarea şi tipul lui b.

4.4.3.12. Operatorul virgulă

Operatorul “,” este folosit pentru gruparea mai multor expresii într-una singură.Cu ajutorul acestui operator (care are prioritatea cea mai mică) se construiesc expresii de forma:

exp1, exp2,. . ., expn

Această expresie are valoarea şi tipul ultimei expresii (deci a lui expn).

Exemplu:k= (i=10, j=j+5; i+j)

Se execută pe rând cele două atribuiri de la stânga la dreapta din parantezele rotunde apoi se face suma i+j şi în final se atribuie această sumă lui k.

4.5. INSTRUCŢIUNI

4.5.1. SCURT ISTORIC AL METODELOR DE PROGRAMAREVom prezenta în continuare câteva metode de programare

dar nu exhaustiv, nefiind aici cadrul adecvat (eventual într-un curs de Software Engineering). O clasificare cronologică a ceea ce vrem să prezentăm ar fi următoarea:a) programarea artizanală;b) programarea procedurală;c) programarea modulară;d) programarea structurată;e) programarea prin abstractizarea datelor;49

Page 54: LIMBAJE DE PROGRAMARE

f) programarea orientată spre obiecte.

4.5.1.1. Programare artizanală

Această metodă de fapt nu este o metodă propriu-zisă ci este prima modalitate de programare odată cu apariţia calculatoarelor. Intuiţia şi experienţa programatorului joacă un rol important. Fiecare programator îşi are propriile reguli de programare. Programele sunt monolitice (un singur corp de instrucţiuni), lungi şi greu de înţeles de alt programator. Însuşi cel ce a elaborat un astfel de program întâmpină dificultăţi de înţelegere a propriului program după un timp oarecare.

4.5.1.2. Programare proceduralăOdată cu apariţia primelor limbaje de înalt nivel se

utilizează programarea procedurală. Necesitatea ca anumite secvenţe de program să fie folosite de mai multe ori duce la organizarea acestora în unităţi distincte de program numite în diverse limbaje subprograme, subrutine, proceduri, etc. De multe ori procedurile trebuie să fie generale deci procesarea să facă abstractizare de valorile datelor. De exemplu o procedură de calcul al radicalului de ordinul 2 trebuie să calculeze acest lucru din orice număr real pozitiv iar pentru cele negative să semnaleze eroare. Procedurile trebuie deci parametrizate cu anumite variabile numite parametri formali. Valorile de la apel ale parametrilor formali se numesc parametri efectivi. Programarea procedurală are la bază deci utilizarea procedurilor, iar acestea realizează o abstractizare prin parametri. La apelare o procedură funcţionează după principiul cutiei negre (black box): se cunosc intrările şi ieşirile rezultate din acestea dar nu şi modul de transformare care nu e important în acest moment.

În majoritatea limbajelor procedurale de programare se consideră 2 categorii de proceduri:- proceduri care definesc o valoare de revenire (denumite şi

funcţii);- proceduri care nu definesc o valoare de revenire.

În limbajele C şi C++ procedurile de ambele categorii se numesc funcţii.

4.5.1.3. Programarea modularăPe măsură ce complexitatea aplicaţiilor a crescut, a

apărut ideea de a descompune problemele în subprobleme mai

50

Page 55: LIMBAJE DE PROGRAMARE

simple care la rândul lor pot fi descompuse în altele mai simple şi aşa mai departe. În felul acesta se ajunge la o descompunere arborescentă a problemei date în subprobleme mai simple. Programarea subproblemelor devine o problemă mai simplă şi fiecare subproblemă are o anumită independenţă faţă de celelalte subprobleme. De asemenea, interfaţa ei cu celelalte subprobleme este limitată şi bine precizată prin procesul de descompunere a problemei iniţiale. De obicei, programarea unei subprobleme, componentă a descompunerii arborescente a problemei iniţiale, conduce la realizarea unui număr relativ mic de proceduri (funcţii). Aceste funcţii pot prelucra în comun anumite date. Unele dintre ele sunt independente de funcţiile realizate pentru alte subprobleme componente ale descompunerii arborescente. Altele realizează chiar interfaţa cu subproblemele învecinate.

Despre funcţiile obţinute în urma programării unei subprobleme se obişnuieşte să se spună că sunt înrudite. De obicei, aceste funcţii, împreună cu datele pe care le prelucrează, se păstrează într-un fişier şi se compilează independent.

O colecţie de funcţii înrudite, împreună cu datele pe care le prelucrează în comun formează un modul. În felul acesta, problema iniţială se realizează printr-un program alcătuit din module.

Programarea modulară are la bază elaborarea programelor pe module.

O parte din datele utilizate în comun de funcţiile modulului, sau chiar toate datele modulului, nu sunt necesare şi în alte module. Aceste date pot fi protejate sau cum se mai spune, ascunse în modul.

Limbajul C şi C++, permite ascunderea datelor în modul folosind date care au clasa de memorie static. Mai mult, pot fi declarate şi funcţiile ca statice şi atunci ele vor fi ascunse în modul (nu pot fi apelate din afara modului). Ascunderea funcţiilor în modul se face pentru acele funcţii care nu se utilizează la realizarea interfeţei modulului cu celelalte module. Ascunderea datelor şi funcţiilor în module permite protejarea datelor şi preîntâmpină utilizarea eronată a funcţiilor.

4.5.1.4. Programarea structuratăDescompunerea unei probleme în subprobleme mai simple se

poate face succesiv în mai multe etape, până când subproblemele sunt direct programabile sub forma unor proceduri sau module. Această descompunere succesivă se mai numeşte rafinare pas cu pas (stepwise refinement).. Evident că se obţine o descompunere arborescentă. Procedurile se pot organiza sau nu în module. În cadrul procedurilor se folosesc anumite structuri de control a execuţiei. Aceasta impune o anumită disciplină a programării. Structurile de control de sunt:51

Page 56: LIMBAJE DE PROGRAMARE

a) secvenţa;b) iteraţia (pretestată, posttestată, cu număr prestabilit de

ciclări);c) alternativa (simplă, completă, generalizată).

Instrucţiunea de bază (primitivă) în cadrul acestor structuri de control este instrucţiunea de atribuire.

Această abordare a programării s-a născut din necesitatea eliminării instrucţiunii de control GO TO care face saltul necondiţionat la o instrucţiune precizată, alta decât instrucţiunea următoare ei. Profesorul Dijsktra de la Universitatea din Eindhoven spunea, prin anul 1965, “calitatea unui programator este invers proporţională cu numărul de instrucţiuni GO TO folosite” şi a impus D-structurile de control:

a) secvenţa;b) iteraţia pretestată;c) alternativa simplă.D-structurile se regăsesc în toate limbajele procedurale.

Corespondenţa ar fi:a) secvenţa este echivalentă cu execuţia instrucţiunilor

în ordinea scrierii lor în programul sursă;b) iteraţia pretestată echivalentă cu WHILE ... DO;c) alternativa simplă echivalentă cu IF ... THEN.

O ilustrare grafică a celor trei D-structuri se dă în continuare.

da S1 C

nu da S2 S C

S nu . Sn

S-a demonstrat ulterior (Bohm şi Jacopini) că orice algoritm se poate descrie doar cu D-structurile dar pentru o mai bună lizibilitate şi înţelegere a programelor sursă s-au adăugat şi iteraţia postestată (REPEAT ... UNTIL), iteraţia cu număr prestabilit de ciclări (FOR ... DO), alternativa completă (IF ... THEN ... ELSE) şi alternativa generalizată (CASE ... OF).În unele limbaje se folosesc şi alte structuri pe lângă cele de mai sus pentru o cât mai fidelă reflectare a algoritmului.

4.5.1.5. Programarea prin abstractizarea datelor52

Page 57: LIMBAJE DE PROGRAMARE

În toate aceste tehnologii anterioare se urmăreşte mai mult organizarea programului şi mai puţin rezolvarea cât mai naturală a problemei. Programarea prin abstractizarea datelor şi programarea orientată spre obiecte propun metodologii în care conceptele deduse din analiza problemei să poată fi reflectate cât mai fidel în program şi să se poată manevra cu instanţieri ale acestor concepte cât mai natural. Se realizează o mai mare fidelitate a programului faţă de problemă. De exemplu dacă într-o problemă în care se procesează numere complexe e nevoie să se lucreze într-o formă cât mai apropiată cu forma matematică se poate introduce tipul COMPLEX (tip care nu există în limbajele de programare) şi apoi se pot declara variabile de acest tip. Mai mult ar trebui să se poată face toate operaţiile matematice asupra datelor de tip COMPLEX. În general un TAD (Tip Abstract de Date) are două componente fundamentale:

- datele membru (reflectă reprezentarea tipului);- funcţiile membru (reflectă comportamentul tipului).4.5.1.6. Programarea orientată spre obiecte

Un neajuns al programării prin abstractizarea datelor este faptul că nu permite exprimarea legăturilor dintre diferite concepte (TAD-uri). Singura legătură dintre concepte care se poate exprima, este aceea că datele membru ale unei clase pot fi obiecte ale unei alte clase. Acest lucru nu este suficient în cazul în care conceptele sunt strâns dependente între ele formând structuri ierarhice. Exprimarea ierarhiilor conduce la atribute suplimentare cum sunt cele de moştenire. Aceste atribute conduc la un nou model de programare pe care îl numim programare orientată obiect.

În vârful unei ierarhii se află fenomenul sau forma de existenţă care are trăsături comune pentru toate celelalte componente ale ierarhiei respective. Pe nivelul următor al ierarhiei se află componentele care pe lângă trăsăturile comune de pe nivelul superior, mai au şi trăsături suplimentare, specifice. O ierarhie, de obicei, are mai multe nivele, iar situarea unui element pe un nivel sau altul al ierarhiei este uneori o problemă deosebit de complexă. Dăm în exemplul următor o ierarhie arborescentă pentru conceptele de paralelogram, dreptunghi, romb şi pătrat. Paralelogram

Dreptunghi Romb

P53

Page 58: LIMBAJE DE PROGRAMARE

ătrat

Dacă paralelogramul se află în vârful ierarhiei atunci pe nivelul imediat inferior se aşează dreptunghiul (paralelogramul cu un unghi drept) dar şi rombul (paralelgramul cu 2 laturi alăturate congruente). Apoi pătratul se poate defini fie ca un dreptunghi cu laturile congruente fie ca un romb cu un unghi drept. Conceptul de pe fiecare nivel se observă că moşteneşte proprietăţile conceptului imediat superior din care este derivat.

La ora actuală, toate ramurile cunoaşterii ştiinţfice sunt pline de ierarhii rezultate în urma clasificării cunoştinţelor acumulate în perioada lungă de observare a fenomenelor şi formelor de existenţă a lumii materiale şi spirituale. Clasificările ierarhice ale cunoştinţelor pot fi întâlnite atât în domeniile care pleacă de la cele mai concrete forme ale lumii materiale, cum sunt botanica, zoologia, biologia, etc cât şi în domenii care studiază concepte dintre cele mai abstracte, cum ar fi matematica sau filozofia.

Aceste ierarhii sunt rezultatul definirii conceptelor după regula includerii “genul proxim şi diferenţa specifică”.

Limbajul C dispune de un set bogat de instrucţiuni care permit scrierea de:

- programe structurate, - programe flexibile, - programe compacte.Totodată limbajul C permite aplicarea metodelor de

programare procedurală, programare modulară şi programare structurată. Pe lângă aceste metodologii limbajul C++ permite şi programarea prin abstractizarea datelor şi programarea orientată spre obiecte.

Vom descrie în continuare instrucţiunile limbajului C. Ca o caracteristică sintactică toate instrucţiunile limbajului se termină prin caracterul “;”, excepţie făcând instrucţiunile care se termină cu acolada închisă.

4.5.2. INSTRUCŢIUNEA VIDĂInstrucţiunea vidă se reduce la caracterul “;”. Ea nu are nici un efect. Adesea este nevoie

de ea la construcţii în care se cere prezenţa unei instrucţiuni, dar nu este necesar să se execute nimic în punctul respectiv.

4.5.3. INSTRUCŢIUNEA EXPRESIEInstrucţiunea expresie se obţine scriind punct şi virgulă

după o expresie, deci:54

Page 59: LIMBAJE DE PROGRAMARE

expresie;

Există cazuri particulare ale instrucţiunii expresie:1) expresia de atribuire, care de altfel este cel mai important

caz particular al instrucţiunii expresie:expresie;

2) apelul unei funcţii:nume_funcţie (par1, par2, . . . parn);

3) incrementările şi decrementările pre şi post fixate:variabilă++; ++variabilă;

variabilă- -; - - variabilă;

Exemplu:

void main (void){ int i; float f; double d; i=10; // instructiune de atribuire i++; // i se mareste cu 1 f=i; // instructiune de atribuire (valoarea lui i se converteste in float) d=++f; // incrementare lui f si atribuirea valorii lui d putchar(‘a’); // instructiune de apel }

4.5.4. INSTRUCŢIUNEA COMPUSĂInstrucţiunea compusă este o succesiune de declaraţii

urmate de instrucţiuni, succesiune inclusă între acolade:{ declaraţii

instrucţiuni}

Pot lipsi declaraţiile sau instrucţiunle dar nu în acelaşi timp. Dacă delaraţiile sunt prezente, atunci ele definesc variabile care sunt valabile numai în instrucţiunea compusă respectivă. Exemplu:. . . { int i=100; // variabila i este definita in aceasta instructiune compusa i++; // i are valabilitate doar intre acolade; dupa acolada inchisa i isi printf (“i=%d\n”,i); // pierde valabilitatea}

Observaţii:1o. După acolada inchisă a unei instrucţiuni compuse nu se pune ”;”.2o. Corpul unei funcţii are aceeaşi structură ca şi instrucţiunea compusă, deci o funcţie are formatul:55

Page 60: LIMBAJE DE PROGRAMARE

antetul funcţieiinstrucţiune compusă

4.5.5. INSTRUCŢIUNEA ifInstrucţiunea if permite să realizăm o ramificare a

execuţiei în funcţie de valoarea unei expresii. Ea are două formate ce permit aplicarea structurii de alternativă simplă şi compusă.Formatul 1:if (expresie) instructiune;Efectul:1) se evaluează expresia din paranteze;2) dacă valoarea expresiei este diferită de zero (deci conform

convenţiei are valoarea adevărat), atunci se execută instructiune, altfel se trece la instrucţiunea următoare.

Formatul 2:if (expresie) instructiune_1;

else instructiune_2; Efectul:1) se evaluează expresia din paranteze;2) dacă valoarea expresiei este diferită de zero (deci conform

convenţiei are valoarea adevărat), atunci se execută instructiune_1, altfel se execută instructiune_2; apoi în ambele cazuri se trece la instrucţiunea următoare.

Observaţii:1o. Se pot folosi instrucţiuni if imbricate, nivelul de imbricare fiind oarecare (deci nu există o limitare a numărului de imbricări).2o. Pentru mai multe imbricări se foloseşte regula asocierii if-lui cu else astfel:

un else se pune în corespondenţă cu primul if care se află înaintea lui în textul sursă şi nu este inclus în instrucţiunea care îl precede pe el şi nici nu îi corespunde deja un else.

Exemple

void main (void){ float x,y,a; x=-5; y=10; if (x<0) // ultimul else se asociaza cu primul if iar

if (y<0) a=1; // penultimul else se asociaza cu cel de-al doilea ifelse a=2;

else a=0;

56

Page 61: LIMBAJE DE PROGRAMARE

}

void main (void){ float x,y,a; x=-5; y=10; if (x<0) // ultimul else se asociaza cu primul if deoarece cel de-al

{ if (y<0) a=1; } // de-al doilea if este inclus in instructiunea compusa care else a=0; // il precede pe if}

void main (void) // citeste trei intregi si scrie minimul dintre ei{int i,j,k,min; scanf (“\ndati i=%d”, &i); scanf (“\ndati j=%d”, &j); scanf (“\ndati k=%d”, &k); if (i>j) min=j; else min=i; if (k<min) min=k; printf (“min(%d,%d,%d)= %d\n”,i,j,k,,min);}

4.5.6. INSTRUCŢIUNEA whileInstrucţiunea while are următorul format:

while (expresie) instructiune;

Cu ajutorul instrucţiunii while se realizează structura repetitivă pretestată (condiţionată anterior).Efectul:1) se evaluează valoarea expresiei din paranteze;2) dacă expresia are valoarea diferită de zero, atunci se execută instructiune şi se reia punctul

1), altfel se trece la instrucţiunea următoare instrucţiunii while.

Deci instructiune se execută repetat atâta timp cât expresia din paranteză este diferită de zero. Se observă că dacă expresia are valoarea zero de la început, atunci instructiune nu se execută niciodată.

Antetul ciclului while este construcţia while (expresie) iar instructiune formează corpul ciclului. În cazul în care este necesar să se execute repetat mai multe instrucţiuni, se utilizează o instrucţiune compusă formată din instrucţiunile respective.

Exemplu:Vom crea un program care citeşte un întreg n şi scrie n!. Algoritmul în pseudocod este următorul:

Citeste nf=1i=2CâtTimp i<=n execută

57

Page 62: LIMBAJE DE PROGRAMARE

f=f*i;i=i+1

SfârşitCâtTimpScrie n,f

Programul în C este:

#include<stdio.h>void main (void){ int n,i; double f; f=1.0; i=2; printf(“\n dati n= “); scanf(“%d”,&n); while (i<=n)

{ f=f*i; i++;}

printf(“\nn=%d, iar n!=%g\n”,n,f);}

Corpul ciclului while se poate scrie mai compact astfel:

while (i<=n) f*=i++;

4.5.7. INSTRUCŢIUNEA for

Instrucţiunea for, ca şi instrucţiunea while, se utilizează pentru a realiza o structură repetitivă pretestată. Formatul instrucţiunii este:

for(exp1; exp2; exp3) instructiune;

Antetul ciclului este definit de for(exp1; exp2; exp3) iar instructiune formează corpul ciclului. Prima expresie exp1 constituie partea de iniţializare a ciclului, iar exp3 este partea de reiniţializare a ciclului. Condiţia de continuare a ciclului este exp2. De obicei exp1 şi exp3

reprezintă atribuiri.Efectul:1) se execută secvenţa de iniţializare definită de expresia exp1;2) se evaluează exp2; dacă exp2 are valoarea zero, atunci se iese din ciclu, adică se trece la

instrucţiunea următoare instrucţiunii for, altfel se execută instrucţiunea din corpul ciclului;3) se execută apoi secvenţa de reiniţializare definită de exp3, apoi se reia secvenţa de la punctul

2).Observaţii:1o. Ca şi în cazul instrucţiunii while, instrucţiunea din corpul ciclului for poate să nu se execute niciodată dacă exp2 are valoarea zero chiar la prima evaluare.

58

Page 63: LIMBAJE DE PROGRAMARE

2o. Expresiile din antetul instrucţiunii for pot fi şi vide; totuşi caracterele “;” vor fi întotdeauna prezente.3o. Comparând instrucţiunile for şi while observăm că instrucţiunea for cu formatul anterior se poate realiza cu secvenţa următoare folosind while:

exp1;while (exp2)

{ instructiune; exp3;

}

Invers, o instrucţiune while de forma: while (exp) instructiune este echivalentă cu următoarea instrucţiune for:

for(; exp; ) instructiune.

Autorii limbajului C propun ca instrucţiunea for să se folosească cu prioritate pentru ciclurile care au pas.Exemple:Vom da o secvenţă de instrucţiuni care însumează elementele unui tablou:

s=0;for(i=0; i<n; i++) s=s+tab[i];sau scrisă mai compact:for (s=0, i=0; i<n; i++) s+=tab[i];

În continuare vom da un mic program care afişează numărul caracterelor citite de la intrarea standard stdin.

#include <stdio.h>void main(void){ long n; for (n=0; getchar()!=EOF; n++); printf (“\nnumarul caracterelor citite =%ld\n”,n);}sau scrisă cu instrucţiunea while

#include <stdio.h>void main(void){ long n=0; while (getchar()!=EOF) n++; printf (“\nnumarul caracterelor citite =%ld\n”,n);}

4.5.8. INSTRUCŢIUNEA do-while

Această instrucţiune realizează structura repetitivă condiţionată posterior (posttestată) dar modificată faţă de REPEAT .. UNTIL. Această instrucţiune s-a introdus pentru o mai mare flexibilitate în scrierea programelor. Formatul ei este: do

instructiune;59

Page 64: LIMBAJE DE PROGRAMARE

while (exp);Efectul:1) se execută instrucţiunea instructiune;2) se evaluează expresia exp din paranteze;3) dacă valoarea expresiei este zero se trece la instrucţiunea următoare instrucţiunii do-while;

altfel se revine şi se execută din nou instructiune.

Observaţii:1o. Structura realizată de instrucţiunea do-while poate fi realizată printr-o secvenţă în care se foloseşte instrucţiunea while astfel:

instructiune;while (exp) instructiune;

2o. Se observă că în cazul instrucţiunii do-while, corpul ciclului se execută cel puţin odată, spre deosebire de ciclurile while şi for unde corpul ciclului poate să nu se execute niciodată.Exemplu:Vom da un program care calculează rădăcina pătrată dintr-un număr real a>=0.

#include<stdio.h>#define EPS 1e-10void main (void){ double x1,x2,y,a; clrscr(); // sterge ecranul printf(“\ndati un numar real pozitiv a=”); if (scanf(“%lf”,&a) !=1 || a<0)

printf (“numarul citit nu este pozitiv\n”); else {

x2 = 1.0; do { x1 = x2;

x2 = 0.5 *(x1+a/x1); // formula de iteratie if ((y=x2-x1) < 0) y = -y; } while (y >= EPS); printf (“radacina patrata din:%g este: %.2lf\n”,a,x2); // 2 zecimale } //sfirsit else }

4.5.9. INSTRUCTIUNEA switch

Instrucţiunea switch permite realizarea structurii alternativa generalizată. Ea este echivalentă cu o imbricare de structuri de alternativă simple. Utilizarea instrucţiunii switch face în schimb programul mult mai clar.Formatul instrucţiunii switch este următorul:

switch (exp){ case c1: sir1

break; case c2: sir2

break; . . . case cn: sirn

break;

60

Page 65: LIMBAJE DE PROGRAMARE

default: sir}

unde: c1,. . . cn sunt constante sau constante simbolice; sir1, . . . ,sirn, sir sunt şiruri de instrucţiuni.

Efectul:1) se evaluează expresia din paranteză;2) se compară pe rând valoarea expresiei cu valorile constantelor c1, . . . , cn;3) dacă valoarea expresiei coincide cu valoarea lui ck, se execută secvenţa de instrucţiuni

definită prin sirk; în cazul în care valoarea expresiei nu coincide cu nici una din constantele c1, . . . , cn, se execută secvenţa de instrucţiuni definită prin sir;

4) după execuţia secvenţei sirk sau sir se trece la instrucţiunea următoare instrucţiunii switch, adică la prima instrucţiune aflată după acolada închisă care termină instrucţiunea switch respectivă; evident, acest lucru are loc dacă şirul care se execută nu impune, el insuşi, un alt mod de continuare a execuţiei, de exemplu o revenire din funcţia respectivă, un salt la o anumită instrucţiune, etc.

Observaţii:1o. Ramura default nu este obligatorie. În lipsa ei, dacă valoarea expresiei nu coincide cu nici una din constantele c1,. . . , cn, instrucţiunea switch respectivă nu are nici un efect.2o.Construcţia break reprezintă o instrucţiune. Ea termină fiecare ramură de instrucţiuni sir1, . . . , sirn, provocând saltul la instrucţiunea următoare instrucţiunii switch sau, cum se mai spune, realizează ieşirea din instrucţiunea switch. 3o. Instrucţiunea break nu este obligatorie. În cazul în care este absentă, se execută secvenţial următoarea ramură. De exemplu dacă avem secvenţa:

switch (exp){ case c1: sir1

case c2: sir2

} ea se execută în felul următor:

- dacă valoarea expresiei este egală cu c1 se execută sir1 şi apoi sir2;- dacă valoarea expresiei este egală cu c2 se execută sir2;- daca valoarea expresiei difera de valorile c1 şi c2 instrucţiunea switch de mai sus nu

este efectivă, se trece la instrucţiunea următoare care urmează după switch.- secvenţa de mai sus se putea realiza şi astfel:

if (exp = = c1){ sir1

sir2

}else if (exp = = c2) sir2

Exemplu:Vom citi din fişierul de intrare construcţii de forma: op1 operator op2, unde op1 şi op2 sunt numere întregi (operanzi întregi) iar operator este un operator aritmetic {“+”, “-“, “*”, “/”}. La ieşire se va scrie valoarea expresiei citite. De exemplu dacă se citeşte secvenţa 100/3 se va afişa rezultatul 33. Programul permite citirea şi evaluarea mai multor astfel de expresii, până la întâlnirea sfârşitului de fişier.

#include <stdio.h>61

Page 66: LIMBAJE DE PROGRAMARE

void main (void){ int op1,op2,operator,rezultat,i; while (( i=scanf(“%d %c %d”, &op1,&operator, &op2)) != EOF) if (i = = 3 ) // ramura adevarat inseamna ca s-au citit 3 campuri corecte { switch (operator)

{ case ‘+’: rezultat = op1 + op2 ; // avem adunare break;

case ‘-‘ : rezultat = op1 – op2; // avem scadere break;

case ‘*’ : rezultat = op1 * op2; // avem inmultire break;

case ‘/’ : // avem impartire intreaga if (op2 = = 0)

{ printf (“divizor nul\n”); rezultat = 0;} else rezultat = op1 / op2;

break; default : printf (“operator eronat\n”);

rezultat = 0; } // sfarsit switch

printf (“%d %c %d %d\n”, op1, operator, op2, rezultat); } else

printf (“expresie eronat\n”); // sfarsit if si while}

4.5.10. INSTRUCŢIUNEA break

Formatul instrucţiunii este următorul:break;

De obicei instrucţiunea break se foloseşte pentru a ieşi dintr-un ciclu. Dacă există mai multe cicluri imbricate instrucţiunea break va trece controlul la ciclul de nivel imediat superior (deci imbricarea rămâne, nu se iese din toate ciclurile). O altă utilizare este în instrucţiunea switch, după cum am observat în paragraful anterior.

Un alt exemplu de utilizare frecventă este ieşirea dintr-un ciclu infinit de forma:

for ( ; ; ){. . .

if (exp) break; . . .

}

4.5.11. INSTRUCŢIUNEA continue

Formatul instrucţiunii este următorul:continue;

Efectul:1) în ciclurile while şi do-while ea realizează saltul la evaluarea expresiei care decide asupra

continuării ciclului;2) în ciclul for ea realizează saltul la pasul de reiniţializare.

62

Page 67: LIMBAJE DE PROGRAMARE

Observaţie:1o. Instrucţiunea continue se utilizează numai în corpul unui ciclu, permiţând, după caz, să se treacă la pasul următor al ciclului sau să se iasă din ciclu.

4.5.12. INSTRUCŢIUNEA goto

Conform principiilor programării structurate instrucţiunea goto nu ar fi necesară. Dar ea a fost introdusă în limbaj, deoarece, în anumite cazuri, se dovedeşte a fi utilă, asigurând o flexibilitate mai mare în programare. De multe ori ieşirea dintr-un ciclu imbricat în alte cicluri se realizează mai simplu cu ajutorul instrucţiunii goto. În lipsa ei ar trebui să folosim mai mulţi indicatori şi teste asupra valorilor acestora pentru ieşirea din ciclu. Saltul făcut de goto se face la o instrucţiune care este prefixată de o etichetă.

Prin etichetă vom înţelege un nume urmat de caracterul “:”. Etichetele sunt locale unei funcţii.

Instrucţiunea goto are următorul format:

goto eticheta;

Efectul:1) se realizează saltul la instrucţiunea prefixată de eticheta al cărei nume se află scris după

cuvântul cheie goto.

4.5.13. APELUL ŞI REVENIREA DINTR-O FUNCŢIE

4.5.13.1. Apelul unei funcţii

În limbajul C funcţiile sunt de două tipuri:- funcţii care returnează o valoare la revenirea din ele;- funcţii care nu returnează nici o valoare la revenirea din ele.

O funcţie care nu returnează nici o valoare la revenirea din ea se apelează printr-o instrucţiune de apel. Ea are următorul format:

nume (lista_parametrilor_efectivi); (*)unde:

- nume este numele funcţiei;- lista_parametrilor_efectivi este fie vidă, fie se compune din una sau mai multe

expresii separate prin virgulă.Instrucţiunea de apel este un caz particular al instrucţiunii expresie. Parametrii efectivi

(de la apel) trebuie să corespundă cu cei formali (de la definirea funcţiei) prin ordine, tip şi număr.

În cazul în care o funcţie returnează o valoare, ea poate fi apelată fie printr-o instrucţiune de apel, fie sub forma unui operand al unei expresii.

Observaţii:1o. Dacă nu dorim să utilizăm valoarea returnată de funcţia respectivă, apelul se face printr-o

63

Page 68: LIMBAJE DE PROGRAMARE

instrucţiune de apel.2o. Dacă dorim să utilizăm valoarea returnată de funcţie, vom folosi apelul funcţiei drept operand într-o expresie, operandul având formatul (*).

Exemple de apeluri de funcţii folosite până acum sunt apelurile funcţiilor standard printf, scanf, getchar şi putchar. Funcţiile printf şi putchar au fost apelate prin instrucţiuni de apel, valorile returnate de ele nefiind utilizate. În schimb funcţiile scanf şi getchar au fost apelate în ambele moduri, atât prin instrucţiuni de apel, cât şi ca operanzi în diferite expresii.

4.5.13.2. Prototipul unei funcţii

O funcţie poate fi apelată dacă ea este definită în fişierul sursă înainte de a fi apelată. După cum am prezentat în lecţia 1 nu întotdeauna este posibil acest lucru şi în astfel de cazuri apelul funcţiei trebuie să fie precedat de prototipul ei.

Prototipul unei funcţii are ca scop să informeze compilatorul despre:- tipul valorii returnate de funcţie;- tipurile parametrilor.

În felul acesta, la apelul unei funcţii, compilatorul poate face teste cu privire la tipul expresiilor care reprezintă parametrii efectivi, precum şi unele conversii necesare asupra valorii returnate de funcţie.Observaţii:1o. Tipurile parametrilor pot să lipsească. În acest caz, compilatorul nu controlează tipurile parametrilor efectivi, singura informaţie conţinută de prototip fiind tipul valorii returnate de funcţia respectivă.2o. Absenţa atât a prototipului unei funcţii, cât şi a definiţiei funcţiei înainte de a fi apelată este posibilă; în acest caz se presupune că funcţia returnează o valoare de tip int.3o. În practică se recomandă utilizarea prototipurilor pentru toate funcţiile înainte de a fi apelate. În acest scop, ele vor fi scrise la începutul fişierelor sursă.

Formatele posibile ale unui prototip sunt:

formatul 1:tip nume (lista_declaratiilor_de_parametri);formatul 2:tip nume (lista_ tipurilor_parametrilor);formatul 3:tip nume (void);formatul 4:tip nume ();

Formatul 2 este cel mai utilizat. Formatul 3 se poate folosi pentru orice funcţie care nu are parametri. Formatul 4 se poate folosi pentru orice funcţie la al cărei apel nu se doresc teste referitoare la tipul parametrilor efectivi.

Funcţiile din biblioteca standard a limbajului C au prototipurile definite în fişierele de tip .h.

4.5.13.3. Apel prin valoare şi apel prin referinţă

La apelul unei funcţii, fiecărui parametru formal i se atribuie valoarea parametrului efectiv care-i corespunde. Deci, la apelul unei funcţii se transferă valorile parametrilor efectivi. Din această cauză se spune că apelul este prin valoare (call by value). În anumite limbaje de programare, la apel nu se transferă valorile parametrilor efectivi ci adresele acestora. În acest caz se spune că apelul este prin referinţă (call by refference).

Între cele două tipuri de apeluri există o diferenţă esenţială şi anume: în cazul apelului

64

Page 69: LIMBAJE DE PROGRAMARE

prin valoare funcţia apelată nu poate modifica parametrii efectivi din funcţia apelantă, neavând acces la ei. În cazul apelului prin referinţă, funcţia apelată, dispunând de adresele parametrilor efectivi, îi poate modifica.

În limbajul C apelul se realizează implicit prin valoare. În cazul că un parametru este numele unui tablou atunci transferul se realizează prin referinţă deoarece numele unui tablou este un pointer şi conţine adresa primului element al tabloului. Transferul prin referinţă se realizează cu ajutorul variabilelor de tip pointer şi cu ajutorul operatorului de adresă (&).

4.5.13.4. Revenirea dintr-o funcţie

Revenirea dintr-o funcţie se poate face în două moduri:- la întâlnirea instrucţiunii return;- după execuţia ultimei sale instrucţiuni, adică a instrucţiunii care precede acolada

închisă ce termină corpul funcţiei respective.

Instrucţiunea return are două formate:

return; sau return expresie;

Primul format se utilizează când funcţia nu returnează o valoare, iar cel de-al doilea când funcţia returnează o valoare. În acest ultim caz, funcţia returnează valoarea expresiei specificate.Observaţie:1o. Când revenirea se face după execuţia ultimei instrucţiuni a funcţiei nu se returnează o valoare; revenirea în acest caz, se face ca şi cum acolada închisă de la sfârşitul corpului funcţiei ar fi precedată de instrucţiunea return.

Exemplu: vom da un exemplu de apel al funcţiei care determină rădacina pătratică dintr-un număr nenegativ.

#include<stdio.h>double radacina_2 (double) // prototipul functiei

void main (void) // functia principala care citeste d // si afiseaza radacina patrata din d

{ double d; clrscr(); // sterge ecranul

if (scanf (“%lf”,&d) != || d<0)printf (“numarul dat este eronat\n”);

elseprintf (“d=%f, radacina patrata = %.10g\n”, d, radacina_2(d));

#define EPS 1e-10double radacina_2 (double x){ double x1,x2,y; x2 = 1.0; do { x1 = x2; x2 = 0.5 *(x1+x/x1); // formula de iteratie if ((y=x2-x1) < 0) y = -y; } while (y >= EPS);

65

Page 70: LIMBAJE DE PROGRAMARE

return x2;}

Observaţie:1o. Limbajul C dispune de o bibliotecă matematică în care sunt incluse o serie de funcţii pentru calculul valorilor funcţiilor elementare. Există o funcţie numită sqrt (cu prototipul double sqrt (double);). Fişierul care conţine biblioteca matematică se numeşte math.h şi trebuie inclus în fişierul sursă de lucru dacă se doreşte utilizarea funcţiilor definite în el.

Pentru algoritmul palindromului, scris în pseudocod, dăm şi varianta în limbajul C:

#include<stdio.h>#include<conio.h>void main(void){ long n,m,x; int c; printf ("\ndati un numar natural:"); scanf ("%ld",&n); x=n; m=0; while (x > 0) { c = x % 10; m = m*10+c; x = x / 10; } if (n == m) printf ("numarul %ld este palindrom",n); else printf ("numarul %ld nu este palindrom",n); getch();}

66