Home - Rasfoiesc.com
Educatie Sanatate Inginerie Business Familie Hobby Legal
Doar rabdarea si perseverenta in invatare aduce rezultate bune.stiinta, numere naturale, teoreme, multimi, calcule, ecuatii, sisteme



Biologie Chimie Didactica Fizica Geografie Informatica
Istorie Literatura Matematica Psihologie

Calculatoare


Index » educatie » » informatica » Calculatoare
» Medii de programare - Suport de curs - Elemente de algebra booleana


Medii de programare - Suport de curs - Elemente de algebra booleana




Medii de programare

Suport de curs

Elemente de algebra booleana

Generalitati

Transferul, prelucrarea si pastrarea datelor numerice sau nenumerice in interiorul unui calculator se realizeaza prin intermediul circuitelor de comutare. Aceste circuite se caracterizeaza prin faptul ca prezinta doua stari stabile care se deosebesc calitativ intre ele. Starile sunt puse in corespondenta cu valorile binare “0” si “1” sau cu valorile logice “adevarat” si “fals” (din acest motiv se mai numesc si circuite logice). Pornind de la aceste considerente, un domeniul al logicii matematice, (stiinta care utilizeaza metode matematice in solutionarea problemelor de logica) numit “algebra logicii” si-a gasit o larga aplicare in analiza si sinteza circuitelor logice. Algebra logicii opereaza cu propozitii care pot fi adevarate sau false. Unei propozitii adevarate i se atribuie valoarea “1”, iar unei propozitii false i se atribuie valoarea “0”. O propozitie nu poate fi simultan adevarata sau falsa, iar doua propozitii sunt echivalente din punct de vedere al algebrei logice, daca simultan ele sunt adevarate sau false. Propozitiile pot fi simple sau compuse, cele compuse obtinandu-se din cele simple prin legaturi logice de tipul conjunctiei , disjunctiei sau negatiei




Bazele algebrei logice au fost puse de matematicianul englez George Boole (1815-1864) si ca urmare ea se mai numeste si algebra booleana. Ea a fost conceputa ca o metoda simbolica pentru tratarea functiilor logicii formale, dar a fost apoi dezvoltata si aplicata si in alte domenii ale matematicii. In 1938 Claude Shannon a folosit-o pentru prima data in analiza circuitelor de comutatie.

Definirea axiomatica a algebrei booleene

Algebra booleana este o algebra formata din:

elementele

2 operatii binare numite SAU si SI, notate simbolic + sau si sau

1 operatie unara numita NU negatie, notata simbolic sau

Operatiile se definesc astfel:

SI SAU NU

0 0 + 0 = 0 0 = 1

0 0 + 1 = 1 1 = 0

1 1 + 0 = 1

1 1 + 1 = 1

Axiomele algebrei booleene sunt urmatoarele:

Fie o multime M compusa din elementele x1, x2, . xn, impreuna cu operatiile si +. Aceasta multime formeaza o algebra daca:

Multimea M contine cel putin 2 elemente distincte x1 x2 (x1,x2I M);

Pentru x1 I M, x2 I M avem:

x1 + x2 I M si x1 x2 I M

Operatiile si + au urmatoarele proprietati:

a.       sunt comutative

x1 x2 = x2 x1

x1 + x2 = x2 + x1

b.      sunt asociative

x1 (x2 x3) = (x1 x2) x3

x1 + (x2 + x3) = (x1 + x2) + x3

c.       sunt distributive una fata de cealalta

x1 (x2 + x3) = x1 x2 + x1 x3

x1 + (x2 x3) = (x1 + x2) (x1 + x3)

Ambele operatii admit cate un element neutru cu proprietatea:

x1 + 0 = 0 + x1 = x1

x1 x1 = x1

unde 0 este elementul nul al multimii, iar 1 este elementul unitate al multimii.

Daca multimea M nu contine decat doua elemente, acestea trebuie sa fie obligatoriu elementul nul 0 si elementul unitate 1; atunci pentru x I M exista un element unic notat cu x cu proprietatile:

principiul contradictiei

principiul tertului exclus

x este inversul elementului x.

In definirea axiomatica a algebrei s-au folosit diferite notatii. In tabelul urmator se dau denumirile si notatiile specifice folosite pentru diverse domenii:

Matematica

Logica

Tehnica

Prima lege de compozitie

x1 + x2

Disjunctie

x1 x2

SAU

x1 + x2

A doua lege de compozitie

x1 x2

Conjunctie

x1 x2

SI

x1 x2

Elementul invers

Negare

x

NU

Proprietatile algebrei booleene

Plecand de la axiome se deduc o serie de proprietati care vor forma reguli de calcul in cadrul algebrei booleene. Aceste proprietati sunt:

Principiul dublei negatii

dubla negatie duce la o afirmatie

Idempotenta

x x = x

x + x = x

Absorbtia

x1 (x1 + x2) = x1

x1 + (x1 x2) = x1

Proprietatile elementelor neutre

x x 1 = x

x + 0 = x x + 1 = 1

Formulele lui De Morgan

Aceste formule sunt foarte utile datorita posibilitatii de a transforma produsul logic in suma logica si invers.

Formulele pot fi generalizate la un numar arbitrar de termeni:

Principiul dualitatii – daca in axiomele si proprietatile algebrei booleene se interschimba 0 cu 1 si + cu , sistemul de axiome ramane acelasi, in afara unor permutari.

Verificarea proprietatilor se poate face cu ajutorul tabelelor de adevar si cu observatia ca doua functii sunt egale daca iau aceleasi valori in toate punctele domeniului de definitie. Prin tabelul de adevar se stabileste o corespondenta intre valorile de adevar ale variabilelor si valoarea de adevar a functiei.

Obs. Comutativitatea si asociativitatea pot fi extinse la un numar arbitrar, dar finit, de termeni, indiferent de ordinea lor.

Baze de numeratie

Omul, din cele mai vechi timpuri incerca sa realizeze masuratori sau comparatii. Deoarece intelesurile 'mai inalt ca' sau' mai mare ca' nu erau suficiente a trebuit sa introduca numere pentru a putea exprima aceste lucruri. Era usor de spus ca un obiect era de doua ori mai mic ca altul sau de trei ori mai valoros. Aceste comparatii sau masuratori se faceau uneori intre obiecte destul de diferite intre ele si erau specifice trocului. (ex. iti dau 8 bucati mari de sare pentru un miel etc.).

In vechime oamenii socoteau pe degetele de la maini, astfel ca avand zece degete erau capabili sa realizeze operatii cu valori pana la zece. Evident ca, pentru a nu tine minte prea multe lucruri trebuiau sa-si noteze calculate realizate. Exista mai multe solutii care rezolvau problema. Cea mai simpla spunea sa scrie un semn pentru fiecare unitate (de ex. pentru 3 obiecte erau desenate fie | | |, fie ooo sau orice alt ceva). Pentru a putea reprezenta valori mai mari, trebuiau pur si simplu scrise prea multe astfel de simboluri a. i. idea era de a nascocii alte simboluri care sa reprezinte valorile mai mari. Astfel, romanii foloseau diferite simboluri pentru a indica diferite 'ponderi' ale valorilor (ex. V pentru 5, X pentru 10, L pentru 50). Aceasta metoda s-a dovedit a fi greoaie deoarece operatiile simple erau destul de greu de realizat (de exemplu adunati MCLIV cu CMIX).

O alta idee era de a folosi cate un simbol diferit pentru fiecare valoare de reprezentat astfel in scrierea araba (care de fapt este la origine chinezeasca) avem simbolurile 0, 1, 2, 3, . , 9 (deci in total 10). Numerele mai mari ca10 erau scrise pe pozitia a doua a treia, etc. (astfel ca de fapt 23 nu insemna 2 obiecte si inca 3 ci de doua ori 10 obiecte si inca 3). Acest stil de a scrie valori este folosit si in zilele noastre si este cunoscut de catre toata lumea.

Un numar de mai multe cifre scris in baza 10 de forma:

(1)

poate fi vazul ca o suma de produse de forma

(2)

Se observa ca, in expresia de mai sus valoarea 10 este constanta si de fapt reprezinta baza de numeratie folosita.

Baza de numeratie este o valoare care indica numarul de simboluri folosite pentru a reprezenta un numar. Astfel, baza zece foloseste 10 simboluri pentru reprezentare. Un numar care este scris in baza de numeratie b

(3)

are forma de mai jos.

(4)

O alta baza de numeratie foloseste mai putine simboluri (de exemplu baza de numeratie 7 va folosi doar 7 simboluri 0, 1, 2, 3, 4, 5, si 6), sau mai multe de exemplu baza 15 are nevoie de 15 simboluri. Pentru ca nu exista decat 10 simboluri pentru a reprezentat cifre si nu putem folosi decat un singur simbol pentru reprezentare pentru fiecare cifra in parte. Desi putem folosi orice fel de simbol pentru a reprezenta cifre a caror valoare este mai mare ca 10, totusi vom utiliza litere (majuscule sau minuscule de fapt nu conteaza) pentru a reprezenta simbolurile lipsa. Astfel pentru baza 15 vom avea 15 simboluri si anume 0, 1, 2, 3, 4, 5, 6,7, 7, 8, 9, A, B, C, D, E (A va inlocui un simbol care va contine 10 unitati, B 11, C 12, etc.).

Exemple de numere corecte sunt 123(5), 7A3(11) etc.

Exemple de numere scrise gresit sunt 264(5), 73X2(16), deoarece simbolurile 6 respectiv X nu exista in bazele de numeratie 5 respectiv 16.

Transformarea numerelor intregi din alta baza de numeratie in baza 10

Pentru aceasta actiune, plecam de la prezentarea unui numar in baza de numeratie b conform (4). Pentru a transforma un numar intreg din orice baza de numeratie in baza zece trebuie sa desfacem grupele prin care a fost scris numarul respectiv. Astfel nu avem nimic de facut altceva decat sa calculam in baza 10 valoarea finala folosind pentru aceasta formula (4)

Exemplu: 745(8)=7*82+4*81+5*80 adica

Un caz mai complicat este daca baza de numeratie este mai mare ca zece si cuprinde numere a caror valoare este mai mare ca zece

Exemplu:

3CB(15)=3*152+C*151+B*150=3*225+12*15+ 11*1=675+180+11=866(10)

60B(16)=6*162+0*161+B*160=1536+0+11 =1547(10)

Transformarea numerelor intregi din baza 10 in alta baza de numeratie

Pentru aceasta actiune, tinem cont de faptul ca un numar scris in baza 10 de fapt reprezinta o cantitate egala cu valoarea scrisa. El trebuie regrupat pentru a putea fi reprezentat in alta baza de numeratie. Operatia se realizeaza prin impartiri succesive ale caturilor rezultate la noua baza de numeratie. Procedand astfel vom obtine rezultatul scontat citind invers toate resturile obtinute

Exemplu1: 292(10)=?(6) Rezultatul final este 1204(6)

Exemplul2: 2575(10)=?(16)

10(A)

==15(F)

Observand ca 10 corespunde simbolului A si 15 simbolului B rezultatul final este A0F(16)

Cazuri speciale de transformari

Se stie ca, toate calculatoarele functioneaza in baza de numeratie 2, adica simbolurile folosite de calculator sunt doar 0 si 1. Acest lucru deranjeaza destul de mult deoarece oamenii sunt obisnuiti cu baza de numeratie 10 si le este foarte greu sa foloseasca noua baza de numeratie. Pentru ca programarea sa fie mai usoara trebuia gasita o cale de compromis. Aceasta cale ar fi utilizarea bazelor de numeratie 8 sau 16 care sunt relativ apropiate de baza 10 si pe de alta parte permit o schimbare usoara de baza.

Observam ca 8=23 sau ca 16=24. Acest lucru este foarte important pentru ca puterile lui doi au o anumita semnificatie (aratata mai jos).

Se stie ca folosind o cifra, in baza b putem obtine b valori diferite (adica doua pt. baza 2, cincisprezece pentru baza 15 etc.). Folosind doua cifre vom obtine b2 valori deoarece vom obtine pentru fiecare valoare posibila pentru a doua cifra b valori prin modificarea primei cifre adica b*b sau b2. De exemplu pentru baza 3 vom obtine 9 valori, pentru baza 2 patru valori etc. Generalizand, pentru o valoare de n cifre scrisa in baza b vom obtine bn valori. Comparand baza 8 cu baza 2 vom observa ca un numar de 3 cifre din baza doi are tocmai opt valori posibile (nici mai multe nici mai putine). La fel baza 16 se echivaleaza cu un numar de patru cifre in baza 2. Ne existand alte valori trecerea din baza 2 in baza 8 sau 16 se face doar prin grupari de cifre. Pentru usurinta in calcul putem folosi doua tabele, cu ajutorul carora putem realiza direct echivalarea.

Baza 2

Baza 8

Baza 2

Baza 16

A

B

C

D

E

F

Exemple:





D

B

A

=671DBA395(16)

Observatie: Folosind combinat metoda descrisa mai sus este foarte usor de convertit un numar din baza 8 in baza 16 sau invers folosind baza 2 ca baza intermediara.

Notiunea de algoritm

Algoritmul este un mod prin care o transformare este descrisa sistematic si conditional in desfasurarea sa.


Transformarea impune de obicei folosirea unor valori numite date de intrare sau parametrii de intrare pe care algoritmul le transforma in rezultate sau date de iesire.

Algoritmii au urmatoarele caracteristici:

generalizare: adica sunt conceputi pentru a rezolva cu succes mai multe probleme care au o cale de rezolvare asemanatoare

determinism: adica obtinerea de rezultate identice pentru valori de intrare identice

finitudine: procesul de prelucrare se termina dupa un numar finit de pasi; (exista cazuri in care programele scrise nu respecta aceasta conditie ceea ce conduce la bucla infinita)

claritate: toate actiunile sau conditionarile din algoritm sunt clar specificate

Exemplu:

Rezolvarea unei ecuatii de gradul I

Analiza problemei: Forma generala a unei ecuatii de gradul I este ax+b=0.

Date de intrare: a si b.

Date de iesire: radacina x a ecuatiei sau un mesaj ('x real' sau 'nu exista solutie' sau 'Exista o infinitate de solutii')

Procesul prelucrarii datelor de intrare poate fi descris astfel:

PAS 1. Se citesc valorile coeficientilor a, b;

PAS 2. Daca a=0 si b=0 atunci se 'Exista o infinitate de solutii';

PAS 3. Daca a=0 si b<>0 atunci se afiseaza 'nu exista solutie';

PAS 4. Daca a<>0 atunci solutia este x=-b/a;

PAS 5. Afiseaza valoarea lui x;

Pentru a reprezenta un algoritm putem alege orice model care repezita dezideratele aratate mai sus. Algoritmul din exemplul anterior a fost reprezentat prin pseudo cod. Pseudo codul este un mod de a descrie liber (fara ingradiri) a unui algoritm. El este apropiat de limbajul natural si de aceea este un excelent mod de a explica un algoritm. Din exemplul de mai sus reiese cu claritate finitudinea algoritmului si eficacitatea acestuia. Este un algoritm generalizat deoarece este conceput pentru a rezolva orice ecuatie de gradul I. Pentru a putea fi generalizata, orice solutie algoritmica are nevoie de a in cadrul algoritmului trebuie sa poata accepta date de intrare, de a le prelucra si de a le afisa ca rezultate. Aceste lucruri nu sunt posibile decat daca datele pot fi 'memorate'. Cea mai folosita cale de a memora si identifica date este aceea de a folosi variabile.

O variabila este o zona identificabila de memorie a carei valoare poate fi modificata prin actiunile algoritmului.

Caracteristicile variabilelor sunt

identificarea de obicei realizata printr-un nume (format dintr-o insiruire de litere si cifre);

tip de date. Este indicat ca sa se cunoasca ce fel de date vor fi memorate de catre variabila. Unele limbaje de programare (in special interpretoare) admit existenta unui tip de date variabil adica variabila poate in perioada sa de definitie sa-si modifice tipul.

zona de memorie care va contine valoarea la un moment dat a variabilei. Dimensiunea rezervata pentru o variabila este de obicei in functie de tipul de data pe care il contine. Localizarea exacta a zonei de memorie este de obicei cunoscuta doar in momentul executiei programului.

Variabila isi poate schimba valoarea pe parcursul parcurgerii algoritmului, dar la un moment dat o variabila nu poate avea decat o singura valoare. Valoarea variabilei poate fi utilizata de catre algoritm prin intermediul numelui: oriunde apare numele unei variabile se intelege de fapt valoarea pe care aceasta o are la momentul respectiv.

Prin tip de date se intelege gruparea datelor asemanatoare ca domeniu de definitie sub un nume numit identificator de tip de date. In general, tipurile de date utilizate in informatica sunt submultimi ale multimilor cunoscute din matematica. Limitarea plajei de valori este datorata caracterului finit al zonei de memorie folosite memorare. De exemplu, in limbajul C++ sunt utilizate, printre altele, urmatoarele tipuri:

int: valori intregi intre -32767 si 32768 (memorare pe 2octeti);

long int: valori intregi intre -2147483648 si 2147483647 (memorare pe 4octeti)

Unii autori considera inclusa in definitia tipului de date si multimea operatorilor si functiilor care se pot aplica elementelor tipului respectiv. De exemplu, pentru un tip intreg, exista operatii specifice: impartirea intreaga si modul aritmetic (restul impartirii intregi).

Prin constanta intelegem o valoare fixa, care nu se modifica de-a lungul aplicarii unui algoritm. Constantele se reprezinta uzual printr-o valoare efectiva:

3, -2, -123, 9452 sunt constante intregi

3.456, 2.000, 3.4000E+10 sunt constante reale

'a', 'B', '&' sunt constante de tip caracter

'ecuatia are o infinitate de solutii !' este o constanta sir de caractere

Modalitati de reprezentare a algoritmilor

Cea mai utilizata metoda de scriere a algoritmilor, cel putin pentru incepatorii in programare, este aceea a utilizarii schemelor logice, care descriu operatiile ce pot fi aplicate datelor prin simboluri grafice numite blocuri.

Sunt utilizate urmatoarele blocuri:

Rounded Rectangle: START1.Blocul START:

Este blocul care indica inceputul oricarei scheme logice si este folosit o singura data ( orice schema logica are un inceput unic).

Rounded Rectangle: STOP2.Blocul STOP

Este blocul care incheie schema logica si este, de asemenea , unic.

Parallelogram: CITESTE3.Blocul de citire

Blocul de citire corespunde operatiei de introducere a datelor, in timpul careia se asteapta ca utilizatorul sa introduca valori concrete, ce vor fi stocate in zona de memorie alocata variabilelor prezente dupa comanda CITESTE.

Cu alte cuvinte, pentru fiecare variabila al carui nume este precizat in blocul de citire se cere introducerea unei valori.

Parallelogram: SCRIE4. Blocul de scriere

Blocul de scriere corespunde operatei de afisare a valorii unei variabile sau a unui mesaj. Dupa comanda SCRIE se precizeaza o lista cu numele variabilelor a caror valoare va fi afisata si a mesajelor.

5.Blocul de calcul

Blocul de calcul corespunde de obicei operatiei numite atribuire, care are ca efect calcularea valorii expresiei si memorarea valorii obtinute in zona de memorie alocata variabilei. O atribuire este de forma

V expresie care se “citeste” asfel: variabila V isi va schimba valoarea, noua valoare fiin de fapt rezultatul evauarii expresie.

Schimbarea valorii unei variabile se poate realiza fie prin utilizarea unei atribuiri, fie prin citirea valorii prin intermediul unei operatii de citire.

! A nu se confunda atribuirea cu egalitatea. In programare, egaliatea apare ca un test in care expresia din stanga semnului de egaliatate este comparata cu expresia din dreapta acestui semn. In urma acestei operatii obtinandu-se doar doua valori posibile: adevarat (in cazul in care rezultatele sunt egale) si fals (in caz contrar). Spre deosebire de aceasta, atribuirea este o actiune, adica o modicare a unei stari de fapt.

6. Blocul de decizie


Blocul de decizie evalueaza valoarea de adevar a conditiei C si, daca aceasta este adevarata se trece pe ramura DA, iar daca este falsa pe ramura NU. Conditia C din blocul de decizie poate fi asemanata cu o intrebare care are doar doua raspunsuri posibile Da respectiv Nu. Exista foarte multe intrebari care au doar doua raspunsuri posibile De exemplu: Este aprisa lumina? sau Sunte-ti atenti? dar pentru a putea scrie programe cel mai des intalnim inrebari de forma unor expresii cu valoare logica exprimate prin egalitati sau alte relatii. De exmplu: b>3 se va interpreta ca o intrebare de genul “este b mai mare ca 3”.

6. Conectori

Conectarea blocurilor ce formeaza o schema logica se realizeaza cu ajutorul unor sageti, acestea indicand si sensul de efectuare a operatiilor ce compun schema logica.

Schema logica poate fi definita ca o succesiune de blocuri de tipurile prezentate mai sus, cu urmatoarele caracteristici:

contine un singur bloc Start si un singur bloc Stop;

in fiecare bloc intra o singura sageata si, cu exceptia blocului de decizie, iese o singura sageata (blocurile au o singura intrare si o singura iesire)

exista cel putin un “traseu” ce pleaca din blocul Start si, mergand in sensul sagetilor se ajunge in blocul Stop;

blocul de decizie poate contine, pe cele doua ramuri, orice combinatie de blocuri dintre cele prezentate;

O schema logica ce are aceste proprietati reprezinta un algoritm structurat.

Sa revenim putin asupra operatiei de atribuire, operatei care se efectueaza intr-un bloc de calcul si care implica definirea mai multor termeni:

Operatori, operanzi, expresii

Operator = operatie care se aplica datelor.

Tipurile de operatori uzuali sunt:

operatori aritmetici: adunare (+), scadere (-), inmultire (*), impartire (/), modul aritmetic (%); acestea sunt folositi pentru a crea expresii aritmetice.

operatori relationali: <, >, <= (mai mic egal), >=, != (diferit), = =(egal) ; acestea se folosesc pentru a crea conditii (impreuna cu operatorii logici), deoarece au ca rezultat o valoare logica.

operatori logici: && (si logic), || (sau logic), ! (negatie logica);

Obs. Operatorii enumerati mai sus pot fi utilizati in limbajul C si C++; pentru alte limbaje este indicata studierea documentatiilor pentru a vedea diferentele de notatie. Limbajul C++ defineste o multime de alti operatori, care vor fi studiati la momentul oportun.

Datele carora li se aplica operatorii se numesc operanzi. In functie de numarul operanzilor implicati, avem: operatori binari (+, -, *, /, &&, ||), unari (!), ternari (operatorul conditional adica ?: din limbajul C).

Operanzii pot fi constante, variabile, functii.

Expresia poate fi definita astfel:

(a)     Variabilele si constantele sunt expresii

(b)     Daca E1 este expresie atunci op_unar E1 este expresie;

(c)     Daca E1, E2 sunt expresii, atunci E1 op_binar E2 este expresie;

(d)     Orice expresie corecta se obtine prin aplicarea pasilor a)-c) de un numar finit de ori;

Exemple de expresii: 23.4+12/4-a*b+1, c && d || e && f, 1234, -765.34

Evaluarea unei expresii = obtinerea unei valori in functie de valorile operanzilor ce apar in expresie.

Daca expresia este numerica, atunci se obtine o valoare numerica, daca expresia contine operatori logici si relationali atunci se obtine o valoare logica (adevarat sau fals).

Atribuirea se desfasoara astfel:

(a)     Se evalueaza expresia din partea dreapta a atribuirii;

(b)     Valoarea obtinuta in urma evaluarii se memoreaza in zona de memorie a variabilei cu numele specificat in partea stanga a atribuirii.

Obs: Vechea valoare a variabilei se pierde in momentul efectuarii unei operatii de atribuire.

Fie urmatoarele atribuiri:

I=3;

I=I+1;

Ne punem intrebarea daca a doua atribuie este corecta. Din punct de vedere matematic, a doua atribuire este o absurditate insa, din punct de vedere al programarii este corecta si se realizeaza astfel:

(a)     a. Se evalueaza expresia I+1, iar rezultatul obtinut este 4 (vechea valoare a lui I, 3, la care se adauga o unitate);

(b)     b. Se stocheaza in I valoarea 4;

Exercitiul 1 : Ce valoare are variabila x dupa urmatoarea secventa de atribuiri:

x=3;

y=5;

x=x+y;

y=2*y;

x=x-y;

Exercitiul 2: (Bacalaureat 1999) Se da urmatoarea secventa de atribuiri:

a=10;

b=4;

a=a-b;

b=a+b;

a=b-a;

(a)     Ce valori au variabilele a si b ?

(b)     Ce efect au ultimele trei atribuiri ?

Exercitiul 3: Construiti schema logica pentru rezolvarea ecuatiei de gradul al II-lea.

Prezentarea notiunii de limbaj de programare

Am vazut in sectiunea precedenta cum pot fi reprezentati algoritmii prin utilizarea pseudo codului sau a schemelor logice, insa prelucrarea automata a datelor presupune scrierea algoritmului intr-o forma ce poate fi inteleasa de calculatorul electronic. Algoritmii vor fi scrisi intr-un “limbaj de programare”, care va contine operatii asemanatoare celor despre care am amintit, numite acum instructiuni.

Limbajul de programare contine:

ALFABETUL: o multime de simboluri pentru scrierea cuvintelor din limbaj

VOCABULARUL (LEXICUL): multime de cuvinte acceptate ca facand parte din limbaj

Se numeste UNITATE LEXICALA cea mai mica 'imbinare' de caractere din vocabular, care are un inteles. Exista un set de reguli privind combinarea unitatilor lexicale in cuvinte si a cuvintelor in 'fraze' (reguli de SINTAXA), respectarea regulilor ducand la obtinerea unor constructii corecte. SEMANTICA unui limbaj se refera la intelesul structurilor obtinute prin combinarea cuvintelor acceptate de limbaj.

In vocabular avem:

cuvinte cheie = cuvinte sau prescurtari ale unor cuvinte din limba engleza, ce reprezinta comenzi (instructiuni)

identificatori = nume folosite pentru variabile, tipuri de date si functii definite de utilizator. Un identificator este format dintr-un sir de caractere care incepe cu o litera si poate contine litere, cifre si caracterul '_' (underscore). Identificatorul nu poate contine spatii sau apostrof, virgula, ghilimele Identificatorii nu pot coincide cu cuvintele cheie.

Prin PROGRAM se intelege o succesiune de comenzi(instructiuni) de prelucrare a datelor, scrise intr-un limbaj de programare. Programul este memorat intr-o entitate numita fisier sursa (este un fisier text).

Prelucrarile dintr-un program C++ sunt grupate in FUNCTII. Rezolvarea unei probleme se face prin utilizarea unor functii definite in limbaj si / sau a unor functii scrise de programator, atunci cand functiile deja existente nu sunt suficiente. Functiile pe care limbajul le pune la dispozitia utilizatorului sunt grupate, dupa tipul de prelucrare oferit, in mai multe fisiere numite 'biblioteci' (fisiere HEADER). Pentru a putea utiliza o functie trebuie sa se specifice la inceputul programului numele bibliotecii care contine functia respectiva.

Orice program C++ trebuie sa contina o functie numita 'main' (un fel de “program principal”), instructiunile continute de aceasta fiind cele prelucrate atunci cand programul este lansat in executie.

Pentru a se putea obtine rezultatele prelucrarii datelor cu ajutorul programelor, trebuiesc parcurse urmatoarele faze:

scrierea programului (editarea textului sursa);

compilarea programului (= verificarea corectitudinii sintactice si semantice a textului sursa si prelucrarea sa fisier obiect)

editarea legaturilor (fisierul / fisierele obiect obtinute in urma compilarii sunt transformate intr-un fisier executabil, adica intr-un fisier care poate fi lansat in executie prin simpla scriere a numelui sau la prompterul sistemului de operare;

Numim mediu de programare un program care permite asistarea programatorului in toate fazele de elaborare a unui program, scris intr-un limbaj de programare (editare, depanare, compilare, executie). Mediul de programare Borland C++ poate fi lansat in executie prin tastarea comenzii bc la prompterul MSDOS.

In paragraful urmator vor fi prezentate elementele de baza ale limbajului C.

Prezentarea generala a limbajului C++

Alfabetul

Alfabetul limbajului este format din acele simboluri utilizate la reprezentarea entitatilor unui program, adica a unitarilor lexicale. Reamintim ca, prin unitati lexicale intelegem cele mai mici entitati cu valoare semantica (adica au o semnificatie), prin combinarea carora rezulta constructiile sintactice ('propozitii si fraze').

Alfabetul limbajului C se compune din urmatoarele categorii de simboluri:

Literele mari si mici ale alfabetului englez si caracterul de subliniere '_' (underscore)

Cifrele arabe: 0-9

Semne de punctuatie: ; , ‘ '

Alte caractere:

~, ^, <, >, =, ?, !, #, &,

Literele si cifrele, precum si caracterul underscore, de multe ori asimilat in multimea literelor, sunt utilizate pentru construirea identificatorilor si cuvintelor cheie, dupa reguli ce vor fi descrise in paragrafele corespunzatoare. In limbajul C se face diferenta dintre literele mici si majusculele corespunzatoare, deci identificatorul 'a' va fi diferit de identificatorul 'A', iar “turbo” va fi diferit de “TURBO” sau “TuRbO” sau orice alta combinatie majuscula minuscula.

Identificatori

Identificatorul reprezinta nume pe care le atribuim variabilelor, constantelor, functiilor, tipurilor de date definite de utilizator. Un identificator este o secventa de litere, cifre si caracterul underscore, primul caracter trebuind sa fie litera sau underscore. Folositi cu multa precautie identificatori care incep cu underscore, pentru a nu intra in conflict cu numele rutinelor sistem, a caror ortografiere nu se cunoaste (numele rutinelor sistem incep intotdeauna cu '_').

Regulile de formare a identificatorilor sunt aceleasi cu regulile din Pascal. Un identificator poate avea, teoretic, o lungime arbitrara, dar numai primele 31 de caractere sunt luate in considerare de compilator.

Identificatorii urmatori:

nume, Nume, NuME, NUMe

sunt diferiti, deoarece literele mici sunt considerate diferite de literele mari corespunzatoare.

Cuvinte rezervate (keywords)

Numele rezervate instructiunilor, tipurilor predefinite si sintaxei de definire a functiilor si tipurilor de date se numesc cuvinte cheie. Lista cuvintelor cheie ale limbajului C este:

auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

Identificatorii definiti de utilizator nu trebuie sa coincida cu cuvintele rezervate. In limbajul C++ se mai adauga cateva cuvinte cheie, care vor fi descrise la momentul oportun (in capitolul rezervat programarii orientate obiect).

Comentarii

Comentariile sunt acele siruri de caractere utilizate la explicarea programelor sursa, delimitate prin caractere speciale care determina ignorarea lor de catre compilator.

Un comentariu are urmatoarea forma:

/* sir de caractere */

sau

// sir de caractere

unde prin sir de caractere se intelege o secventa de caractere din setul caracterelor reprezentabile, mai putin combinatia */. Nu se admit comentariile imbricate. Comentariul poate fi scris pe mai multe linii daca este scris in prima forma. A doua forma este specifica Borland C si permite scrierea unor comentarii ce nu depasesc o linie.

Tipuri de date

Definitia notiunii de tip de data cuprinde, pe langa multimea de valori a tipului, si alte aspecte:

dimensiunea memoriei alocate

multimea operatiilor ce actioneaza asupra elementelor tipului

timpul de viata asociat datei, dat de clasa de memorie

Tipurile de date sunt:

tipuri de baza: tipuri predefinite in limbaj;

caracter (char)

intreg (int)

real (float, double)

tipuri derivate: tipuri definite de utilizator;

enumerare

referinta

structurate:

tablou

structura

uniune

Spre deosebire de Pascal, in C nu este definit tipul logic, considerandu-se ca o expresie este adevarata daca are o valoare ne nula (adica diferita de 0) si falsa in caz contrar. Tipul char difera de tipul caracter din Pascal, in C caracterele fiind de fapt numere intregi, care desemneaza o echivalenta cu codul ASCII. Se defineste si tipul void, care desemneaza fie 'orice tip', fie indica lipsa oricarui parametru intr-o functie.

Tipuri de date predefinite

Tipurile de baza definite in limbajul C sunt: intreg, real si caracter. Exista insa mai multe tipuri intregi si reale, in functie de numarul de biti pe care se pot memora valorile si daca valorile sunt cu sau fara semn. In plus, unele dintre aceste tipuri difera de la o implementare la alta a limbajului (in functie de calculatorul pe care se lucreaza). In tabelul urmator sunt prezentate tipurile fundamentale, memoria necesara stocarii valorilor de acel tip si limita valorilor ce pot fi memorate intr-o variabila de acel tip.

Tipul

Dimensiune memorie

Limita valorilor

char

1 byte

int

depinde de implementare (uzual 2 bytes)

short int

2 bytes

unsigned char

1 byte

unsigned int

depinde de implementare

long int

4 bytes

unsigned long int

4 bytes

float

4 bytes

double

8 bytes

long double

10 bytes

Primul tip este tipul caracter. O variabila de acest tip va avea ca valoare codul ASCII asociat caracterului respectiv. De exemplu, daca unei variabile i se atribuie caracterul ’a’, variabila va contine valoarea 97 (numarul de ordine al caracterului ‘a’ in codul ASCII).

Tipul intreg cel mai des utilizat in programe este int, dar numarul de bytes pe care se memoreaza valorile de acest tip difera de la o implementare la alta a limbajului C (in functie de tipul calculatorului). Astfel, tipul int este echivalent cu short int sau long int, in functie de implementare.

Celelalte tipuri intregi sunt obtinute prin folosirea prefixului signed sau unsigned, indicand daca valorile respective sunt cu semn (contin si valori negative), respectiv fara semn ( valori naturale).

Ultimele trei tipuri din tabel sunt tipuri reale, diferenta dintre ele constand in cantitatea de memorie utilizata, intervalul de valori si precizia memorarii valorilor (numarul de zecimale retinute). Valorile reale se reprezinta conform notatiei din standardul IEEE (folosind semnul, mantisa si exponentul). De exemplu, pentru tipul float se utilizeaza reprezentarea in simpla precizie, folosind un bit de semn, 7 biti pentru exponent si 24 de biti pentru mantisa. Aceasta reprezentare are un exponent in limita a 10-37 si 1038 cu pana la 7 zecimale precizie. Valoarea maxima a unui float este de 1.701411 E38.

Din tabel se observa ca nu exista un tip logic, asa cum este el definit in alte limbaje de programare. In limbajul Pascal, tipul logic e definit ca multimea cu valorile impreuna cu operatorii logici cunoscuti (si, sau , negatia logica). In limbajul C nu s-a definit acest tip dar se foloseste urmatoarea conventie: o expresie este considerata adevarata daca valoarea obtinuta la evaluarea ei este ne nula si falsa in caz contrar. Variabilele care se vor folosi ca variabile logice vor fi declarate de tip intreg.

O alta caracteristica aparte o constituie introducerea tipului void, pentru a desemna “nimic” sau “orice tip”, in functie de contextul in care este utilizat. In exemplele de pana acum tipul void a fost folosit pentru a indica faptul ca functia main nu intoarce nici o valoare.

Pentru a putea utiliza o variabila intr-un program C, este obligatorie declararea acesteia, astfel:

[clasa_de_memorie] TIP_DE_DATE lista_variabile;

unde:

clasa_de_memorie specifica locul unde se va aloca memorie pentru variabila si durata sa de viata, notiuni la care vom reveni mai tarziu). Specificarea clasei de memorie este optionala;

TIP_DE_DATE este orice tip predefinit sau derivat;

lista_variabile contine lista variabilelor de acel tip, despartite prin virgula;

Exemple:

int a,b,c;

char ch;

float x,y;

long int z;

int g=7;

Observatie: La declararea variabilei intregi g s-a realizat si initializarea acesteia ( adica stabilirea unei valori initiale).

Variabilele care sunt declarate in interiorul corpului unei functii se numesc variabile locale, iar cele declarate in afara oricarei functii din program se numesc variabile globale. Variabilele globale vor putea fi utilizate in toate functiile ce compun programul, iar variabilele locale doar in cadrul functiei in care au fost definite.

Citirea si afisarea datelor. Primele programe in C++

Pentru introducerea datelor prin intermediul tastaturii avem nevoie de functia CIN, iar pentru afisarea valorilor sau mesajelor de functia COUT.

Exemplul 1: Cel mai simplu program este programul care nu realizeaza nici o prelucrare:

void main( )

Exemplul 2: Sa se afiseze pe ecran un mesaj.

#include <iostream.h>

void main()

Observatii:

Linia #include <iostream.h> declara ca se va utiliza biblioteca de functii cu numele 'iostream.h', din care face parte functia de afisare cout.

Functia main() este functia principala a unui program C, prelucrarile continute de aceasta fiind primele efectuate la executarea unui program.

Prelucrarile ce apar intr-o functie trebuiesc scrise intre

Dupa scrierea unei comenzi (functie / instructiune) se pune ;

Exemplul 3: Reluam exemplul precedent:

#include <iostream.h>

void main()

Observatii:

Caracterul 'n' are ca efect afisarea a ceea ce urmeaza pe linia urmatoare a ecranului.

Mesajul trebuie precedat de <<

Exemplul 4: Sa se scrie un program pentru insumarea a doua numere.

#include <iostream.h>

void main()

Observatii:

Se folosesc trei variabile pentru a memora numerele si suma acestora. Prin 'int a' se declara folosirea unei variabile de tip intreg, avand numele 'a'.

'cin >> a' determina citirea unei valori de la tastatura, valoare ce va fi memorata in zona de memorie a variabilei 'a'. Cel care introduce date trebuie sa tasteze o valoare intreaga, altfel semnalandu-se eroare.

Daca se afiseaza un mesaj, continutul acestuia trebuie pus intre ghilimele. Daca se doreste afisarea valorii unei variabile atunci se scrie, dupa <<, numele acesteia.

Executand programul de mai sus veti observa modul de afisare al rezultatului.



In textul programului au fost introduse mici comentarii, precedate de //, pentru a facilita intelegerea textului sursa.

Exemplul 5: Scrieti un program care sa afiseze urmatorul meniu:

Bauturi

Bere . . . ..12500

Vin . . . . 34764

Suc . . . . . 5000

Cafea Nes . .10000

Expreso . . . 6000

#include <iostream.h>

void main( )

Observatii: S-a folosit caracterul t pentru a se “incepe” un nou paragraf. Caracterele “t” si “n” fac parte dintre caracterele numite caractere escape, deoarece sunt caractere ne afisabile pe ecran decat prin utilizarea unor simboluri (secvente de evitare).

Functia fscanf

Este utilizata pentru citirea datelor dintr-un fisier sau de la tastatura (dispozitivul standard de intrare) si are urmatoarea sintaxa:

fscanf(tipul_valorii_citite, variabila);

Modul de functionare este urmatorul: se citeste de la tastatura, in variabila data, o valoare de tipul specificat.

Tipul valorii citite se specifica prin

%caracter,

unde caracter poate fi:

c - daca se citeste un caracter

d sau i - int

x - intreg in baza 16

- intreg in baza 8

u - intreg fara semn

hd, hi, hx, ho, hu, hx - short int

ld, li, l0, lu, lx - long int

s - sir de caractere

f , e, g, E, G- float

le, lg, lf- double

Le, Lg, Lf - long double

Inaintea variabilei care stocheaza valoarea citita se va scrie intotdeauna & (operatorul adresa), ca in exemplul urmator:

fscanf ('%f', &a);

Se citeste o valoare de tip float, valoarea fiind stocata in variabila a.

Observatie: Nu se va folosi operatorul & pentru citire atunci cand variabila este un pointer (care este el insusi o adresa, dar asta mai tarziu)

Daca dorim sa citim mai multe valori cu aceeasi functie fscanf, vom specifica pentru fiecare variabila tipul valorii:

int a;

char ch;

float f;

fscanf('%i%c%f',&a, &ch, &:f);

Observatii:

Sirul de caractere care defineste tipul valorii citite se numeste descriptor de format.

Numarul descriptorilor de format trebuie sa fie acelasi cu numarul variabilelor din lista

Ordinea de scriere a descriptorilor este importanta

Nu introduceti alte caractere intre descriptori decat cele permise

Functia fprintf

Functia fprintf este utilizata pentru scrierea datelor la dispozitivul standard de iesire. Sintaxa este:

fprintf ( tipul_valorii_scrise, variabila) ;

Semnificatia parametrilor este aceeasi ca si pentru fscanf. Functia are ca efect scrierea pe ecran a unei valori de tipul specificat, valoare stocata in variabila. Nu se mai foloseste operatorul &.

Exemplu: Calculati suma a doua numere.

int a, b, suma;

printf('Primul numar =');

scanf('%i',&a);

printf('Al doilea numar = ');

scanf('%i',&b);

suma = a+b;

printf('Suma dintre %i si %i este %i', a, b, suma);

Operatori

Operatorii constituie unul din conceptele cele mai importante si mai interesante care stau la baza unui limbaj de programare. Limbajul C este vestit pentru marea varietate de operatori pe care-i pune la dispozitia programatorului rezultand si o diversitate de expresii ce se pot forma pe baza acestora. Dupa cum se stie, o expresie este formata din variabile, constante, functii si operatori. In acest paragraf voi aminti cateva din categoriile de operatori ai limbajului C:

Operatorii aritmetici:

adunare, - scadere, * produs, / impartire, % - restul impartirii intregi

, -- incrementare si decrementare

Operatori relationali:

< , >

egal

<= mai mic sau egal

>= mai mare sau egal

diferit

Operatori logici:

&& - si logic

- sau logic

- negatie logica

Operatorii logici si relationali sunt implicati in formarea expresiilor cu valoare logica. Spre exemplu:

a<2, 7/5+2<s-1, (X>-3) &&(x<3), !(a==b)

sunt expresii cu valoare logica.

Operatori de atribuire.

In sectiunea precedenta am amintit, vorbind despre reprezentarea algoritmilor prin scheme logice, de operatia de atribuire. In limbajul C se considera ca atribuirea este un operator. Atribuirea are urmatoarea sintaxa:

variabila = expresie;

Modul de functionare este urmatorul:

se evalueaza expresia;

valoarea obtinuta este stocata in zona de memorie a variabilei, realizandu-se eventuale conversii de tip.

valoarea rezultata in urma atribuirii este valoarea atribuita variabilei.

Observatie: Atribuirea fiind un operator, se considera ca operanzii sunt variabila din partea stanga, respectiv expresia din partea dreapta. Orice expresie trebuie sa aiba, in urma evaluarii sale, o valoare (un rezultat). Valoarea expresiei de atribuire este valoarea obtinuta prin evaluarea expresiei din partea dreapta a atribuirii.

Daca valoarea obtinuta prin evaluarea expresiei din dreapta atribuirii este de alt tip decat tipul variabilei din stanga atunci se incearca conversia tipului valorii la tipul variabilei. Nu intotdeauna conversia este posibila, caz in care se va afisa un mesaj de eroare in urma compilarii.

Exemplul 1 : Fie urmatoarea secventa de program:

void main(void)

Observatie: Executand programul de mai sus se vor obtine valori diferite pentru variabilele a si c, desi expresia care apare in dreapta ambelor atribuiri este aceeasi. Pentru a se obtine valoarea 3, iar pentru c se obtine 3,75. Explicatia sta in modul in care se realizeaza conversiile de tip in cadrul atribuirilor. Valoarea expresiei 3./2.+9./4. este 3,75 dar variabilei a i se va atribui partea intreaga a acestei valori, adica 3. Variabila c este de tip float (numar real), deci nu va mai fi nevoie de conversia rezultatului evaluarii expresiei la tipul variabilei.

Exemplul 2:

void main(void)

Observatie: Executand acest program se va afisa valoarea 2 si nu valoarea 2,5 asa cum ar fi fost de asteptat. Explicatia este urmatoarea: operatorul / realizeaza, atunci cand operanzii sunt intregi, impartirea intreaga, deci vom obtine catul impartirii intregi dintre 5 si 2. Exista doua modalitati de rezolvare a problemei: fie inlocuim expresia cu 5./2. (5.=5.0) fie utilizam operatorul de conversie explicita ( ). Acest nou operator ( numit cast), se utilizeaza astfel

( tip_de_date) expresie;

Semnificatia este urmatoarea: Se cere convertirea valorii rezultate din evaluarea expresiei la o valoare de tipul specificat intre paranteze. In exemplul de mai sus vom inlocui atribuirea cu:

a=(float) 5/2;

(rezultatul expresiei va fi de tip float, deci variabilei a i se va atribui valoarea 2.5).

Exercitiu: Fie urmatorul program, care calculeaza media aritmetica a trei numere a,b,c , primele doua introduse de la tastatura.:

void main(void)

Executati acest program si identificati greselile. Corectati greselile gasite.

operatori de atribuire compusa:

+=, -=, *=, /=, %=

Operatorii de atribuire compusa au fost introdusi pentru a fi utilizati in locul atribuirilor de tipul:

v=v+expresie;

v=v-expresie;

v=v*expresie; e.t.c

unde:

v-variabila

expresie-orice expresie corecta in limbajul C

In locul acestor expresii se vor folosi:

v+=expresie;

v-=expresie; e.t.c

Folosirea operatorilor de atribuire compusa sporeste lizibilitatea programului si viteza de executie.    Daca atribuirea este de forma:

v=v+1;

sau

v=v-1;

se pot utiliza operatorii de incrementare si decrementare ++ si --. Expresiile de mai sus se vor putea scrie:

v++;

respectiv

v--;

Exemplul 3: Se da urmatorul program:

void main(void)

Observatie: Cand apar expresii de tipul :

++variabila

sau

--variabila;

spunem ca se realizeaza o preincrementare, respectiv predecrementare. In cazul preincrementarii, variabila va primi ca valoare 1+valoarea initiala. Daca preincrementarea apare intr-o expresie se realizeaza intai aceasta operatie si abia apoi celelalte operatii care mai apar in expresie (preincrementarea are prioritate mai mare). In atribuirea a = ++I se realizeaza intai preincrementarea, in urma careia i va avea valoarea 4, si abia apoi se realizeaza atribuirea (a va avea valoarea 4).

In expresia b = a — se realizeaza o postdecrementare, adica se modifica valoarea variabilei a doar dupa ce s-au efectuat celelalte operatii din expresie. In cazul nostru, b va primi valoarea lui a (4, in momentul acela) si abia apoi se va micsora valoarea lui a cu o unitate.

Exercitiu: Executati programul de mai sus si urmariti rezultatele care se obtin.

Mai exista operatori de atribuire compusa, folosindu-se operatorii logici ce actioneaza la nivel de bit, dar despre acestia vom vorbi intr-o sectiune viitoare. Tot atunci vom discuta si despre atribuirea multipla.

Instructiunea alternativa (IF


Pana acum am folosit ca exemple programe simple, care contineau doar functiile de intrare / iesire si operatorul de atribuire. Atunci cand elaboram programe ceva mai complexe este necesar ca, intr-un anumit punct al programului, sa decidem continuarea acestuia in functie de o conditie. In primul laborator am dat ca exemplu de algoritm rezolvarea ecuatiei de gradul 1 (pseudocod si schema logica). Intr-o schema logica ni se permitea utilizarea unui bloc de decizie de urmatorul tip:

cu semnificatia: daca este adevarata conditia se executa prelucrarea B, altfel se executa prelucrarea A.

In limbajul C este utilizata instructiunea IF, cu urmatoarea sintaxa:

if (expresie) instructiune1;

[else instructiune2;}

unde:

expresie - orice expresie valida in limbajul C

instructiune1, instructiune2 - orice instructiune corecta din limbajul C ;

Mod de functionare:

se evalueaza expresia

daca expresia este ne nula (conventia pentru adevarat) se executa instructiune1

daca expresia este nula (falsa) se executa instructiune2

se trece la urmatoarea instructiune a programului

Observatie: alternativa else instructiune2 poate sa lipseasca.

Exemplul 4: Sa se scrie un program pentru calcularea maximului a doua numere.

#include <iostream.h>

void main(void)

Exemplul 5: Scrieti un program care calculeaza maximul a 3 numere.

#include <iostream.h>

void main (void)

Observatie: Dupa cum se vede din exemplu, instructiune1 si instructiune2 pot fi de asemenea instructiuni if. Spunem ca sunt instructiuni if imbricate. A se observa si modul cum a fost scris programul, astfel incat sa se vada clar carei instructiuni if ii apartine fiecare else.

Exemplul 6: Se dau trei numere intregi a,b,c. Sa se testeze daca numerele date pot fi lungimile laturilor unui triunghi.

#include <iostream.h>

void main (void)

Observatie: Nu am mai verificat daca a, b , c sunt pozitive.

Exemplul 7. Reluam exemplul precedent cu intentia de a folosi mai putine instructiuni if.

#include <iostream.h>

void main (void)

Observatie: Am folosit o singura instructiune if, cele trei conditii anterioare fiind conectate prin operatorul SI logic. Dupa cum se stie, daca avem propozitiile p,q,r, atunci propozitia p&&q&&r va fi adevarata daca toate propozitiile componente sunt adevarate si falsa daca una dintre propozitiile componente este falsa.

Este indicata aceasta scriere pentru ca sporeste lizibilitatea programului.

Exemplul 8: Rezolvarea ecuatiei de gradul I.

#include <iostream.h>

void main(void)

else

if (b==0) cout<<”n x apartine lui R “;

else cout <<”n Ecuatia nu are solutii”;

Observatie: In cazul in care a este ne nul avem de realizat doua operatii: atribuirea valorii -b/a variabilei x si afisarea valorii radacinii. Atunci cand se realizeaza mai multe operatii acestea trebuiesc incluse intre .

Instructiuni iterative

Toate problemele prezentate pana acum au putut fi rezolvate folosind numai functii de citire / scriere, atribuiri si instructiunea de decizie (IF). Cele mai multe dintre probleme vor necesita structuri de date mai complexe precum si folosirea unor noi instructiuni, care sa permita repetarea de un numar oarecare de ori a unor parti din algoritm.

Sa luam ca exemplu algoritmul de calcul al sumei a 2 numere introduse de la tastatura. El consta in citirea valorilor pentru cele doua numere si afisarea sumei acestora. Nu era nevoie decat de doua variabile, cate una pentru fiecare numar. Acest exemplu este doar unul didactic, in practica ne intalnindu-se prea des cazuri in care sa fie nevoie de a suma doua numere. Generalizarea problemei pentru n numere va modifica substantial algoritmul nostru. Nu vom putea folosi cate o variabila pentru fiecare numar introdus deoarece nu cunoastem exact cate numere avem (n este introdus de utilizatorul programului, altfel algoritmul nu ar mai fi general). Chiar daca s-ar sti ca avem, sa zicem, 2500 de numere ar fi cam dificil sa utilizam 2500 de variabile distincte.

Problema noastra se poate rezolva simplu daca utilizam o instructiune iterativa (ciclica), care sa ne permita repetarea de n ori a urmatoarei secvente de operatii:

citim o valoare folosind variabila “a”;

adunam valoarea citita la rezultatul partial, memorat in variabila “s”;

Instructiunile ce descriu aceste prelucrari ciclice sunt compuse din doua parti:

corpul ciclului, format din prelucrarile ce se doresc a fi realizate de mai multe ori;

conditia, pe baza careia se stabileste daca se vor mai executa prelucrarile din ciclu (este obligatorie executarea prelucrarilor din corpul instructiunii de un numar finit de ori);

Corpul ciclului sau conditia trebuie sa contina acele operatii care, dupa efectuarea de un numar de ori a corpului instructiunii, sa determine schimbarea valorii de adevar a conditiei, permitand incheierea executarii instructiunii ciclice si trecerea la urmatoarele instructiuni ale algoritmului. In cazul in care este neglijat acest aspect, programul se va executa la infinit.

Conditia trebuie sa fie o expresie cu valoare logica. Reamintesc faptul ca in limbajul C++ nu exista un tip de date logic, dar se utilizeaza conventia ca orice valoare ne nula sa fie considerata ca adevar iar valoarea zero ca valoarea fals. In consecinta, orice expresie cu valoare de tip intreg va putea fi utilizata drept conditie intr-o instructiune iterativa.

In finalul paragrafului o chestiune de terminologie: o executie a corpului instructiunii poarta numele se iteratie.

Instructiunile iterative pot fi clasificate, in functie de momentul evaluarii conditiei, astfel:

cicluri cu test initial (While si For)- evaluarea conditiei se face inaintea fiecarei iteratii;

cicluri cu test final (Do .. While)- evaluarea conditiei se face la sfarsitul fiecarei iteratii;

Instructiunea While

Instructiunea While (“atat timp cat”) este o instructiune iterativa cu test initial si are urmatoarea sintaxa:

while ( <conditie>) instructiune;

unde:

<conditie> - orice expresie cu valoare intreaga;

instructiune - orice instructiune valida a limbajului;

Mod de functionare:

daca expresia este adevarata se executa prelucrarile din ciclu;

altfel, se trece la urmatoarea instructiune de dupa while;

Cu alte cuvinte, prelucrarile din ciclu se executa atat timp cat conditia este adevarata. Daca expresia este falsa de la inceput corpul ciclului nu se va executa deloc.

Problema propusa in paragraful precedent se poate rezolva astfel:

Exemplul 1 Suma a n numere introduse de utilizator

#include <iostream.h>

void main()

cout<<'Suma este= '<<suma;

Exemplul 2: Suma primelor n numere naturale.

#include <iostream.h>

void main()

cout<<'Suma este: '<<suma;

Observatie: Se poate utiliza o scriere mai compacta, care elimina si necesitatea utilizarii variabilei i:

while (n--) suma+=n;

Modul de executie este urmatorul:

valoarea conditiei este valoarea variabilei n, valoare care scade cu o unitate dupa fiecare iteratie (postdecrementare)

conditia devine falsa atunci cand valoarea lui n devine zero;

Instructiunea do .. while

Instructiunea do .. while este o instructiune iterativa cu test final si are urmatoarea sintaxa:

do

instructiune;

while (<conditie>)

Mod de functionare:

se executa corpul instructiunii;

se evalueaza conditia: daca aceasta este adevarata se reia executia, altfel se trece la urmatoarea instructiune din program;

Deosebirea esentiala fata de instructiunea while este aceea ca expresia se evalueaza dupa iteratie.

In cazul in care conditia este falsa de la inceput, corpul instructiunii se executa o singura data. Utilizarea instructiunii do .. while este mai putin frecventa (se foloseste pentru acele prelucrari care trebuiesc executate cel putin o data).

Daca rescriem exemplul 1 utilizand do .. while, obtinem:

do

while(i<=n);

Instructiunea for

Una dintre cele mai puternice instructiuni iterative ale limbajului C (si nu numai) este instructiunea for, care are urmatoarea sintaxa:

for (expresie1; expresie2; expresie3) [instructiune];

unde:

expresie1, expresie2, expresie3 - expresii valide in C++;

instructiune - orice instructiune a limbajului C++;

Parantezele patrate semnifica faptul ca instructiunea este optionala;

Mod de executie:

expresiile au urmatoarea semnificatie generala:

expresie1 - expresie de initializare;

expresie2 - conditia de continuare a executiei ciclului;

expresie3 - expresie de actualizare;

atat timp cat expresie2 este adevarata se executa corpul ciclului;

de fiecare data se evalueaza expresia de actualizare, care are rolul esential de a determina ca, dupa un numar de iteratii, expresie2 sa devina falsa;

expresie1 se evalueaza o singura data;

Exemplul 3: Reluam exemplul 1 folosind instructiunea for:

#include <iostream.h>

void main()

cout<<”suma =”<<suma;

Observatii:

Variabila i este utilizata pe post de “contor” al instructiunii for, numarand la a cata iteratie s-a ajuns.

Executia instructiunii for se incheie atunci cand numarul de iteratii devine egal cu n. Initializarea lui i cu 1 se realizeaza o singura data, la inceput; i <= n este conditia de continuare a executiei; i++ se efectueaza dupa fiecare executie a ciclului (postincrementare).

Aceasta forma a instructiunii for este cea mai utilizata. Un alt mod de a descrie executia acesteia este urmatorul: pentru i de la 1 la n se executa corpul instructiunii.

Nu este obligatoriu ca valoarea lui i sa se mareasca de fiecare data cu o unitate.

Nu este obligatorie utilizarea tuturor celor trei expresii din for, dar este obligatorie scrierea separatorului “;” .

Exemplul de mai sus se poate rescrie astfel:

for( ;n--; )

Notiunea de tablou

In laboratorul precedent explicam de ce nu putem sa utilizam 2500 de variabile distincte atunci cand dorim sa insumam 2500 de numere. Rezolvarea propusa atunci se baza pe citirea repetata a cate unei valori, folosind o variabila a, urmata de adaugarea acesteia la suma partiala. Dezavantajul metodei este acela ca nu se pot memora toate valorile citite ci doar ultima (citind o valoare intr-o variabila, vechea valoare a acesteia se pierde). Cele mai multe programe presupun prelucrari complexe asupra datelor de intrare, deci va fi nevoie sa memoram sirul nostru de numere astfel incat sa poata fi utilizat si in alte prelucrari, nu numai pentru calcularea sumei.

Pentru a rezolva astfel de situatii s-a introdus in limbajele de programare notiunea de tablou. Tablourile ne permit memorarea unui numar mare de valori utilizand o singura variabila.

Prin tablou se intelege un numar de elemente de acelasi tip, numit tip de baza, stocate intr-o zona compacta de memorie.

Un tablou poate fi definit astfel:

tip_de_baza nume [dimensiune1] [dimensiune2] . [dimensiune_n];

unde:

tip_de_baza = tipul elementelor tabloului;

n = numarul de dimensiuni al tabloului;

[dimensiune_i] = numarul de elemente pe dimensiunea I

tabloul are dimensiune1* . *dimensiune_n elemente

Notiunea de tablou multidimensional poate fi inteleasa mai bine dupa parcurgerea notiunilor referitoare la vectori si matrice.

Tablouri unidimensionale

Declararea unui tablou unidimensional:

tip_de_baza nume[dimensiune];

unde dimensiune specifica numarul de elemente al vectorului.

De exemplu:

int a[30]; declara un vector ce contine 30 de elemente de tip int,

float b[50]; declara un vector cu 50 elemente reale;

“Numerotarea” elementelor se face de la 0 la dimensiune-1, adica cele 30 de elemente ale primului tablou sunt: a[0], a[1], a[2], . , a[29].

Spunem ca tabloul este o variabila indexata, deoarece fiecare element al tabloului poate fi “gasit” / “utilizat” cunoscand numarul sau de ordine. Vectorul este un sir de valori in care se cunoaste precis care este primul, al doilea, . .,ultimul element. “Numarul de ordine” al unui element se numeste indice.

Exemplul 1: Sa se calculeze minimul elementelor unui sir de n numere, utilizand vectori.

#include <iostream.h>

void main()

// se calculeaza minimul

min=a[0]; //initializam minimul cu primul element din sir

for(i=1;i<n;i++)

if (a[i]<min)

min=a[i];

//afisare minim

cout<<”Minimul este=”<<min;

Dupa cum se poate observa din programul de mai sus, fiecare element al sirului se poate utiliza ca si cum ar fi o variabila de tip int independenta, deci valoarea unui element al vectorului poate fi modificata independent de celelalte elemente. Sa presupunem ca utilizatorul introduce pentru n valoarea 4. Initial, elementele vectorului nu au o valoare bine definita; putem reprezenta grafic vectorul astfel:

Sa presupunem ca utilizatorul introduce valorile 3, 7, 2, 9 pentru cele patru elemente ale vectorului. Reprezentarea vectorului a va fi urmatoarea:

a[0]

a[1]

a[2]

a[3]

Valoarea oricarui element al vectorului poate fi modificata fie printr-o atribuire, fie prin introducerea unei valori (folosind functia cin, de exemplu) de la tastatura. Daca vom introduce atribuirea

a[2]=23;



valoarea elementului al treilea din vectorul a nu va mai fi 2 (vechea valoare) ci 23. De asemenea, daca se scrie:

cin>>a[2];

valoarea elementului va fi cea introdusa de utilizator de la tastatura.

Exemplul 2: Se considera n numere intregi introduse de la tastatura. Sa se afle cate numere sunt pare si cate impare.

#include <iostream.h>

#include <conio.h>

void main()

//initializam variabilele

pare=0;

impare=0;

//luam fiecare element din v si testam daca acesta este sau nu par

for(j=0;j<n;j++)

if (v[j] % 2 = =0)

pare++;

else impare++;

//afisam rezultatul

cout<<”Am gasit “<<pare<<” numere pare si “<<impare;

In programul de mai sus verificam, pentru fiecare element, daca acesta se imparte exact la doi, caz in care am mai descoperit un element par. In caz contrar, numarul elementelor impare se mareste cu unu.

Am utilizat operatorul % , numit si modulo aritmetic, care are ca rezultat restul impartirii lui v[j] la 2.

Exemplul 3: Sa se introduca de la tastatura un cuvant si sa se afiseze.

#include <iostream.h>

#include <conio.h>

void main()

Observati cu atentie cum am declarat variabila ce va memora cuvantul: folosim un vector cu elemente de tip caracter. Acest mod de declarare ne permite sa lucram cu oricare dintre caracterele ce formeaza cuvantul. De exemplu, daca utilizatorul introduce cuvantul salariu, vectorul nostru de caractere arata cam asa:

Daca dorim sa modificam putin cuvantul putem scrie:

cuvant[6]=’i’;

si vom obtine cuvantul salarii.

Exemplul 4: Sa se citeasca un cuvant si sa se gaseasca numarul de vocale pe care le contine.

#include <iostream.h>

#include <conio.h>

# include <string.h>

void main()

Observatii:

Am utilizat functia strlen, care are ca rezultat numarul de caractere al sirului dat ca parametru. Functia poate fi utilizata doar daca a fost inclus fisierul antet string.h, fisier ce contine functiile ce actioneaza asupra sirurilor de caractere.

“Mecanismul “algoritmului este urmatorul: se ia cate un caracter din sirul introdus si se verifica daca elementul respectiv contine unul dintre caracterele a, e, i, o, u. De fiecare data cand conditia este adevarata se incrementeaza numarul de vocale.

Mai multe despre sirurile de caractere vom spune dupa capitolul dedicat pointerilor.

Exemplul 5 (Cautare secventiala) Se considera un sir de n elemente intregi si un numar intreg x. Sa se verifice daca numarul x face parte din sirul considerat.

#include <iostream.h>

#include <conio.h>

void main()

//se citeste elementul x

cout<<”x=”;

cin>>x;

gasit=0;

j=0;

//cautam x in sirul v

while( (j<n)&&(!gasit))

if(gasit) cout<<”Elementul apartine sirului dat !”;

else cout<<”Elementul nu apartine sirului !”;

Am utilizat instructiunea while, conditia de efectuare a ciclului fiind (j<n) && (!gasit). Corpul instructiunii while se va executa atat timp cat nu s-a ajuns la sfarsitul sirului si elementul x nu a fost gasit in v. Conditia de mai sus devine falsa in doua cazuri: fie am epuizat elementele sirului (j a ajuns egal cu n), fie am gasit un element egal cu x. Dupa executarea ciclului while testam daca variabila gasit este ne nula (adevar), caz in care x apartine lui v, altfel x nu apartine sirului.

Exemplul 6. (sortare) Se considera un sir cu n elemente reale. Sa se aranjeze elementele in ordine crescatoare utilizand acelasi vector.

#include <iostream.h>

#include <conio.h>

void main()

//sortarea crescatoare a sirului v

for(j=0;j<n-1;j++)

for(k=j+1; k<n;k++)

if (v[j]>v[k])

//afisarea valorilor sortate crescator

for(k=0;k<n;k++) cout<<v[k]<<”t”;

Tablouri bidimensionale (matrice)

Pe langa vectori, cel mai utilizat tip de tablou de numere este tabloul bidimensional, numit de cele mai multe ori matrice. Declararea unei matrice se face astfel:

tip_de_baza nume[dimensiune_1][dimensiune_2];

Exemplu:

double a[10][5]; //tablou cu 10 linii si 5 coloane de elemente reale

int a[3][2]; //tablou cu 3 linii si 2 coloane de elemente intregi

Pentru a putea avea acces la valoarea unui element al matricei, trebuie sa precizam linia si coloana pe care se afla acesta. Un element poate fi specificat prin a[i][j], i - reprezentand linia si j - coloana. In cazul vectorilor citeam o valoare reprezentand numarul de elemente. Pentru matrice vom citi o valoare m - numarul maxim de linii si o valoare n - numarul maxim de coloane. O matrice cu m linii si n coloane va avea m * n elemente.

Daca m = n atunci matricea se numeste matrice patrata de ordin n.

Exemplul 7. Sa se citeasca elementele unei matrice si sa se afiseze.

#include <iostream.h>

#include <conio.h>

void main()

//afisarea elementelor matricei

for(i=0;i<m;i++)

Exemplul 8. Se dau doua matrice cu m linii si n coloane. Sa se calculeze matricea suma.

int a[20][20], b[20][20], c[20][20];

int m,n;

int i, j;

//se citesc dimensiunile m, n si cele doua matrice a si b

//calculam matricea suma

for(i=0;i<m;i++)

for(j=0;j<n;j++) c[i][j]=a[i][j]+b[i][j];

//se afiseaza matricea c

Atunci cand lucram cu matrice avem nevoie de doi indici: i pentru a parcurge liniile matricei si j pentru a parcurge coloanele acesteia. Pentru fiecare valoare a lui i, j ia toate valorile intre 0 si n-1, deci se parcurge linia i.

In ultimul exemplu am utilizat trei matrice. Pentru mai multa claritate, putem declara un tip ale carui elemente sa fie matrice:

typedef int matrice[20][20];

matrice a, b, c;

Am definit un tip de date (tip de date definit de utilizator) ale carui elemente sunt matrice cu maxim 20 de linii si coloane cu elemente intregi. Numele noului tip este matrice. Declararea variabilelor de acest tip se poate face oriunde in programul in care apare definitia. Definirea unui tip de date se poate face numai prin utilizarea cuvantului cheie typedef inaintea declaratiei. Daca definim mai multe tipuri se va folosi typedef pentru fiecare definitie.

Notiunea de pointer

Pointerii au fost introdusi in limbajele de programare pentru a putea rezolva mai eficient anumite probleme sau pentru a da mai multa claritate anumitor programe.

O prima definitie poate fi urmatoarea:

Pointerul este o variabila ce contine adresa unui obiect.

Obiectul a carei adresa este retinuta de pointer poate fi:

variabila

functie

Fie urmatorul exemplu:

int x;

int *px;

Am definit o variabila de tip intreg x si o variabila pointer, care poate contine adresa unei variabile de tip intreg. Simbolul * ce apare In stanga variabilei px arata ca px este o variabila pointer.

Prin atribuirea

px=&x;

Pointerul va avea ca valoare adresa de memorie alocata variabilei x (vezi laboratorul nr.1, definitia variabilei). Operatorul unar & este utilizat pentru a se obtine adresa variabilei x (operator unar = are un singur operand)

Acum putem sa lucram cu continutul variabilei x (adica cu valoarea acesteia) prin intermediul pointerului px, deci indirect, fara sa mai folosim variabila x. La prima vedere, aceasta modalitate de lucru poate parea dificila si nu tocmai utila. Necesitatea utilizarii pointerilor va apare cu mai multa claritate in sectiunea dedicata sirurilor de caractere si functiilor.

Exemplul 1. Fie programul urmator:

#include <iostream.h>

void main()

In programul de mai sus am introdus valorile variabilelor intregi x si y, am definit un pointer la variabila x si am atribuit acestuia adresa de memorie alocat variabilei x. Sa analizam atent linia:

cout<<'x are valoarea '<<*px;

Prin *px se intelege valoarea aflata in zona de memorie a carei adresa este memorata in pointerul px. Valoarea afisata va fi chiar valoarea introdusa pentru x deoarece, inainte de afisare, pointerul px a primit ca valoare adresa variabilei x, adresa la care se afla valoarea acesteia (valoare dobandita prin utilizarea functiei cin).

Atribuirea *px=y; va modifica valoarea care se afla la adresa memorata de px, valoare care va fi valoarea introdusa de utilizator pentru variabila y. Astfel va fi modificata chiar valoarea pe care o are variabila x.

Fireste ca era mai simplu sa folosim atribuirea x=y; care are acelasi efect si ne scuteste de de-a mai folosi pointeri, insa exemplul este pur didactic.

Operatorul unar * este folosit sub forma *variabila_pointer, valoarea acestei expresii fiind valoarea care se gaseste in memorie la adresa memorata de pointerul ce apare ca operand. In concluzie, prin px avem acces la adresa variabilei x, iar prin *px la valoarea variabilei x.

Vom spune ca un pointer “refera” indirect un obiect sau ca “pointeaza”(arata) la obiectul respectiv. Variabilele pointer pot fi incadrate ca fiind de tip referinta.

Exemplul 2. Sa se calculeze suma a doua numere reale folosind pointeri.

#include <iostream.h>

void main()

Pointeri si tablouri

In limbajul C, exista o foarte stransa legatura intre pointeri si tablouri, astfel ca pointerii si tablourile sunt tratate la fel. Orice program in care apar tablouri poate fi modificat astfel incat sa foloseasca pointeri in locul tablourilor. In aceasta sectiune vom discuta despre legatura dintre pointeri si vectori (tablouri unidimensionale).

Fie urmatoarele declaratii:

int a[20];

int *pa;

Am declarat o variabila a , care este un vector cu maxim 20 elemente intregi si un pointer la o variabila de tip intreg. Dupa cum se stie, o valoare int are nevoie de 16 biti pentru a fi memorata, adica 2 bytes ( o variabila int poate retine numere intregi intre -32768 si 32767, vezi curs Bazele Informaticii). Pentru tabloul a vor fi alocati 2· 20=40 bytes consecutivi in memorie adica, pentru primul element a[0] sunt alocati primii 2 bytes, pentru a[1] urmatorii 2 bytes, . , pentru a[19] ultimii 2 bytes din cei 40 alocati.

Fie atribuirea: pa=&a[0];

Dupa aceasta atribuire, pointerul pa contine adresa primului element al vectorului, adica pa pointeaza la inceputul vectorului a.

Daca scriem pa=&a[3]; atunci pa va referi elementul al 4-lea din vectorul a, iar *pa va contine valoarea sa.

Operatiile care se pot realiza cu pointeri sunt:

comparatia

adunarea unui pointer cu un intreg

scaderea unui intreg dintr-un pointer

Doi pointeri pot fi comparati folosind operatori relationali. In comparatia:

if(p1==p2) cout<<”Adrese identice”;

else cout<<”Adrese diferite”;

se verifica daca adresa memorata de p1 este aceeasi cu adresa retinuta de p2, unde p1 si p2 sunt pointeri de acelasi tip. Se poate compara un pointer cu valoarea NULL (sau 0). Un pointer are valoarea NULL (valoare nedefinita) daca nu refera nici un obiect. Adunarea unui pointer cu un intreg este definita numai atunci cand pointerul refera un tablou (un element al tabloului). Scaderea este definita in acelasi caz.

Exemplul 3. Sa se citeasca elementele unui vector si sa se afiseze acestea utilizand pointeri.

#include <iostream.h>

void main()

//afisarea vectorului folosind pointeri

pa=&a[0];

for(i=0;i<n;i++)

Prima pate a programului nu contine elemente noi, doar a doua parte meritand atentie. Mai intai initializam pointerul pa cu valoarea primului element al vectorului a. Ciclul for contine urmatoarele prelucrari:

afiseaza valoarea aflata la adresa indicata de pointer;

aduna pointerul pa cu 1

Incrementarea pointerului pa are ca efect modificarea adresei memorate in pa. Noua adresa este adresa zonei de memorie corespunzatoare elementului urmator, o adresa cu 2 bytes mai mare decat precedenta. Observam ca marirea pointerului cu o unitate inseamna de fapt trecerea la urmatorul element din vector.

Daca vom introduce pentru n o valoare mai mare decat 20 (numarul maxim de elemente ale vectorului, asa cum reiese din declaratie) atunci pointerul pa va depasi zona de memorie alocata vectorului si va referi o adresa la care se pot afla date importante pentru program. Urmarile pot fi imprevizibile, de la blocarea programului pana la blocarea sau inchiderea calculatorului !!!

Notiunea de functie. Structura si definirea functiilor

Notiunea de functie este o notiune de mare importanta in informatica, orice limbaj de programare furnizand facilitati de lucru cu functii.

In matematica, functia era definita ca fiind tripletul (A, B, f), unde:

A - domeniul de definitie;

B - codomeniul sau domeniul de valori;

f - lege, conventie, prin care fiecarui element din domeniul de definitie i se asociaza un unic element din codomeniu;

In informatica, notiunea de functie difera putin de modul matematic de abordare. In limbajul C, orice program trebuie sa contina obligatoriu o functie numita main. Daca prelucrarile ce compun programul sunt foarte complexe, utilizarea unei singure functii duce la un program greu de inteles si depanat. In acest caz este bine ca problema de rezolvat sa fie impartita in sub probleme prin a caror combinare sa se obtina rezolvarea problemei initiale. Pentru rezolvarea fiecarei sub probleme se poate utiliza cate o functie separata. Functia main (“programul principal”) nu va mai fi de mare intindere, ea continand doar apeluri catre functiile deja definite.

Decizia de a folosi o functie poate fi luata si in cazul in care anumite prelucrari trebuiesc realizate de mai multe ori, cum se va vedea si in exemplul de mai jos.

Exemplul 1. Sa se calculeze aria a n triunghiuri, daca se cunosc lungimile laturilor acestora.

#include <iostream.h>

#include <conio.h>

#include <math.h>

float aria(int a, int b, int c)

void main()

Programul trebuie sa calculeze aria a n triunghiuri, deci calculul ariei trebuie realizat de n ori. Am definit functia aria, care calculeaza aria triunghiului cu lungimile laturilor a, b, c prin formula lui Heron. Structura functiei este urmatoarea:

antet:

float aria(int a, int b, int c)

care contine urmatoarele elemente:

tipul rezultatului functiei (codomeniul) - rezultatul functiei va fi o valoare de tipul declarat;

numele functiei: - numele nu poate fi identic cu un cuvant cheie

lista parametrilor formali - variabilele de care depinde calcularea rezultatului (“argumentele functiei”); pentru fiecare parametru formal se specifica tipul.

corpul functiei:

Corpul functiei contine declaratiile variabilelor utilizate in functie si prelucrarile realizate de functie. Variabilele utilizate in functia aria, numite si variabile locale, sunt semi perimetrul p si aria s. Nu declaram variabilele a, b, c (parametrii formali nu se declara in corpul functiei) si nici nu citim valori pentru ele in cadrul functiei.

Orice functie care are drept codomeniu un tip diferit de tipul void trebuie sa contina in corpul functiei o instructiune

return expresie;

unde expresie este orice expresie corecta in C cu valoare de tipul declarat pentru codomeniu.

Instructiunea return este utilizata pentru a semnala faptul ca valoarea functiei este valoarea expresiei.

In programul principal (functia main) se citeste numarul de triunghiuri n. Instructiunea for va repeta de n ori (pentru fiecare triunghi) urmatoarele prelucrari:

se citesc valori pentru variabilele a, b, c care reprezinta lungimile laturilor triunghiului curent;

se calculeaza aria triunghiului pentru valorile date;

se afiseaza valoarea ariei triunghiului curent;

Modul de utilizare al functiei aria este interesant. Atribuirea:

S=aria(a,b,c);

are urmatorul efect;

se evalueaza expresia din dreapta atribuirii, expresie ce consta din apelul functiei aria pentru valorile a, ,b, c

evaluarea functiei inseamna executarea prelucrarilor din functie pentru valorile parametrilor formali a, b, c

valoarea intoarsa de functie este valoarea variabilei s, care apare in return;

valoarea este memorata in zona de memorie a variabilei S.

Functiile pot fi clasificate astfel:

functii predefinite = functii deja definite de autorii mediului de programare C si grupate, in functie de utilitatea lor, in biblioteci numite fisiere header. De exemplu, in biblioteca math.h sunt grupate functiile matematice, in string.h avem functiile de lucru cu siruri de caractere, in iostream.h functii pentru introducerea si afisarea datelor, in malloc.h si alloc.h

functii pentru alocarea memoriei .

functii definite de utilizator = functii scrise de creatorul unui program pentru acele prelucrari pentru care nu exista functii predefinite (caz destul de frecvent);

O functie poate sa aiba drept codomeniu orice tip predefinit scalar (intreg, caracter sau real), tip compus (uniune sau structura), precum si pointeri la orice tip. Tipul void inseamna ca functia nu returneaza nici o valoare.

Programatorul poate sa stranga functiile definite de el intr-un fisier header propriu, care se poate include in program prin

#include “nume_fisier.h”.

Apelul functiilor si transferul parametrilor

Orice aparitie a numelui functiei insotit de un numar de valori egal cu numarul parametrilor formali se numeste apel al functiei.

Puteam apela functia si in modul urmator:

cout<<aria(3,4,5);

Aceasta instructiune are ca efect afisarea ariei triunghiului cu laturile de lungimi 3, 4, 5. Observati ca nu este obligatoriu ca parametrii din apel sa fie variabile, ci pot fi expresii.

Daca functia noastra ar aparea intr-o expresie ca 23*aria(3,4,5)-2*aria(2,6,6), evaluarea expresiei ar incepe cu executarea functiei aria pentru valorile 3, 4, 5 si apoi pentru 2, 6, 6; dupa ce se obtin rezultatele celor doua apeluri ale functiei se realizeaza evaluarea celorlalte operatori (inmultirile si apoi scaderea).

Functia in care apare un apel al altei functii se numeste functie apelanta.

Parametrii sunt de doua tipuri:

parametri formali:

valoare

referinta (pointer)

parametri efectivi (actuali)

Parametrii sunt utilizati pentru a putea transmite din functia apelanta valorile necesare prelucrarilor efectuate de functia apelata. Atunci cand parametrii formali sunt utilizati doar pentru a transmite valori in functia apelata, acesti parametrii se numesc parametri valoare. Daca se doreste transmiterea ca parametru a unei variabile, in scopul modificarii valorii acesteia astfel incat modificarea sa fie disponibila in functia apelanta, avem de-a face cu un parametru formal referinta (se realizeaza printr-o variabila de tip pointer).

Pentru a putea apela o functie, trebuie sa precizam, pe langa numele acesteia, valorile parametrilor pentru care se realizeaza apelul (parametri actuali). Numarul parametrilor actuali dintr-un apel de functie trebuie sa fie acelasi cu numarul parametrilor formali din definitia functiei apelate si sa corespunda ca tip.

De exemplu, in programul de mai sus, nu puteam apela functia aria ca s = aria(4.56, 5, 5); deoarece primul parametru formal din definitia functiei este definit ca de tip int, iar parametrul actual (4.56) este de tip real (incompatibilitate de tip).

Nu este obligatoriu sa existe parametrii formali intr-o functie daca nu este nevoie. De asemenea, nu toate functiile intorc o valoare. Aceste aspecte sunt tratate in urmatoarele exemple.

Exemplul 2. Sa se scrie o functie care afiseaza pe ecran un numar dat de asteriscuri.

void asterisc(int nr)

Functia are un singur parametru formal, un intreg nr reprezentand numarul de asteriscuri ce trebuie afisate. Functia poate fi apelata prin asterisc(10); care are ca efect afisarea a 10 asteriscuri.

Functia nu intoarce nici o valoare, deoarece nu este necesar (n-are sens sa intoarcem vreo valoare), deci codomeniul va fi tipul void. Din moment ce functia nu intoarce vreo valoare, nu se va mai utiliza return.

Exemplul 3. Scrieti o functie care sa afiseze un mesaj.

void mesaj( )

Functia mesaj nu intoarce nici o valoare si nici nu contine parametrii formali (nu exista valori care sa fie necesare executiei functiei). Apelul functiei se va face prin: mesaj(), exact ca si pentru functia pre definita clrscr( ).

Daca am dori ca functia sa afiseze un mesaj dat, obtinem:

void mesaj(char s[80])

Apelul functiei poate fi: mesaj(“Traiasca pacea si bunastarea poporului chinez !!!”);

Exemplul 4. Scrieti o functie care stabileste daca un numar este prim.

#include <iostream.h>

int este_prim(int n)

void main()

Exercitiul 1: Folositi functia este_prim pentru a afisa toate numerele prime mai mici decat un numar intreg dat.

Exemplul 5. Scrieti o functie care sa citeasca un vector de numere intregi.

void citeste(int a[30], int n)

Functia are doi parametri: variabila de tip vector care va retine numerele citite si variabila n, care ne spune cate elemente trebuiesc citite. Functia main poate sa arate astfel:

void main ()

Exercitiul 6: Scrieti o functie pentru afisarea elementelor unui vector si adaugati-o exemplului precedent.

Exemplul 7. Scrieti o functie care calculeaza suma elementelor dintr-un vector.

int suma(int a[40], int n)

Este necesar sa introducem ca parametru si numarul de elemente din vector n.

Exercitiul 8: Folositi functia de mai sus in programul de la exercitiul precedent.

Exemplul 9. Scrieti o functie care calculeaza ax, unde a real si x intreg pozitiv.

float putere(float a, int x)

Exercitiul 10. Modificati functia din exemplul 7 astfel incat sa poata ridica pe a si la puteri negative.

Exemplul 11. Scrieti o functie pentru a calcula cel mai mare divizor comun a 2 numere intregi folosind algoritmul lui Euclid.

#include <iostream.h>

int cmmdc(int m, int n)

do

while(r!=0);

return m;

void main()

Exemplul 12 Sa se scrie o functie care verifica daca un intreg x apartine unui vector v.

int cauta(int v[50], int n)

Exemplul 13. Sa se scrie o functie care realizeaza suma a doua matrice cu m linii si n coloane.

void suma_mat(int a[20][20], int b[20][20], int c[20][20], int m, int n)

Parametrii de care depinde rezolvarea sarcinii sunt:

a - prima matrice

b - a doua matrice

c - matricea suma (rezultatul sumei)

m - numarul de linii

n - numarul de coloane

Exercitiul 5. Sa se scrie functia main corespunzatoare functiei de mai sus, precum si functii pentru citirea si afisarea unei matrice.

O situatie interesanta apare atunci cand vrem ca variabilele ce apar ca parametri formali sa poata fi modificate in cadrul functiei, modificarile fiind disponibile in functia apelanta.

Sa luam urmatorul exemplu:

Exemplul 14. Sa se scrie o functie care interschimba valorile a doua variabile intregi.

Varianta urmatoare este gresita:

void schimba(int x, int y)

void main()

La prima vedere functia pare corecta, insa lucrurile stau tocmai pe dos. Daca introducem valorile 5 pentru a si 7 pentru b, programul ar trebui sa schimbe intre ele valorile celor doua variabile (a trebuie sa devina 7, iar b sa devina 5). Programul de mai sus nu va realiza acest lucru. Explicatia este ca, atunci cand parametri formali sunt parametri valoare, chiar daca se modifica valoarea lor in cadrul functiei aceste modificari nu vor avea efect in functia apelanta (in cazul nostru, in functia main nu va fi sesizabila schimbarea realizata in functia schimba).

Problema se poate rezolva daca parametrii din functia schimba vor fi parametri referinta (pointeri).

Varianta corecta este urmatoarea:

void schimba(int *x, int *y)

Singura schimbare din functia main este apelul functiei, care devine

schimba(&a, &b);

Explicatia corectitudinii acestei variante este aceea ca se lucreaza cu adresele variabilelor, deci orice modificare a continutului zonelor de memorie alocate variabilelor x si y va fi resimtita si in functia main.

Parcurgeti din nou exemplul 10 si priviti cu atentie antetul functiei suma_mat. Parametrul formal c este o matrice calculata ca suma matricelor a si b, deci valoarea elementelor lui c este modificata in cadrul functiei, dar parametrul formal este corect ? Raspunsul este afirmativ deoarece, dupa cum stiti, tablourile sunt tratate exact ca si pointerii, deci tablourile care apar ca parametrii formali se considera parametri referinta.

Variabile locale si variabile globale

Un program C este compus din una sau mai multe functii (cel putin functia main) repartizate in unul sau mai multe fisiere. Structura de principiu a unui program C este urmatoarea:

directive de preprocesare (de exemplu #include)

declaratii de tipuri si variabile globale (imediat dupa directivele de preprocesare, in afara oricaror functii)

definitii de functii

functia main()

Toate variabilele declarate in afara oricarei functii se numesc variabile globale, acestea putand fi utilizate in orice functie din program. Variabilele definite in interiorul unei functii se numesc variabile locale si pot fi utilizate doar in functia unde au fost declarate. Putem declara o variabila k in mai multe functii, fara sa fie influentata corectitudinea programului. Este de preferat ca variabilele globale sa fie in numar limitat, doar atunci cand este nevoie neaparata. Folosirea neatenta a variabilelor globale poate duce la erori greu depistabile.

Bibliografie

Suport de curs Universitatea de Vest Vasile Goldis

Liviu Nergrescu

Limbajul C++ Editura Albastra Cluj Napoca 1999

Doina Hrinciuc Logofatu

C++ probleme rezolvate si algoritmi Editura Polirom 2001

Ana Intuneric Cristina Sichim

Informatica Teste grila C/C++ Polirom 2003

Mariana Milosescu

Informatica Editura Didactica si Pedagocica 2005






Politica de confidentialitate


Copyright © 2020 - Toate drepturile rezervate