Capitolul II
Limbaje de programare

2.1 Comunicația om-mașină
2.2 Tipuri de numere reprezentabile în calculator
2.3 Valori de adevăr
2.4 Șiruri de caractere
2.5 Tipuri primitive de valori ale unui limbaj de programare
2.6 Tablouri de elemente
2.7 Expresii de calcul
2.8 Variabile
2.9 Instrucțiuni

2.1 Comunicația om-mașină

Pentru a executa un program de calculator este necesar să putem comunica cu unitatea centrală pentru a-i furniza instrucțiunile necesare. Cel mai simplu mod de a le comunica este înscrierea codului instrucțiunilor direct în memoria calculatorului de unde pot fi citite de către unitatea centrală. Această cale este însă extrem de anevoioasă pentru programator pentru că el trebuie să învețe să se exprime coerent într-un limbaj ale cărui componente de bază sunt coduri numerice.

O alternativă mai bună este folosirea unui limbaj de comunicație format dintr-un număr foarte mic de cuvinte și caractere speciale împreună cu un set de convenții care să ajute la descrierea numerelor și a operațiilor care trebuiesc executate cu aceste numere. Limbajul trebuie să fie atât de simplu încât calculatorul să poată traduce singur, prin intermediul unui program numit compilator, frazele acestui limbaj în instrucțiuni ale unității centrale. Ori de câte ori vom imagina un nou limbaj de comunicație cu calculatorul va trebui să creăm un nou program compilator care să traducă acest limbaj în instrucțiuni ale unității centrale, numite uneori și instrucțiuni mașină.

În realitate folosirea termenului de limbaj de comunicație nu este extrem de fericită pentru că de obicei noi doar instruim calculatorul ce are de făcut și cum trebuie să facă acel lucru, fără să-i dăm vreo șansă acestuia să comenteze sarcinile primite. În continuare vom numi aceste limbaje simplificate limbaje de programare pentru a le deosebi de limbajele pe care le folosim pentru a comunica cu alți oameni și pe care le vom numi limbaje naturale.

Limbajele de programare trebuie să ne ofere o cale de descriere a modulului în care dorim să reprezentăm informațiile cu care lucrează programul, o cale de a specifica operațiile care trebuiesc executate cu aceste informații și o cale de a controla ordinea în care sunt executate aceste operații.

În plus, limbajele de programare trebuie să respecte următorul principiu fundamental: rezultatul execuției unei comenzi dintr-un limbaj de programare trebuie să fie complet determinat. Asta înseamnă că în limbajele de programare nu este permisă nici o formă de ambiguitate a exprimării.

Informațiile reprezentate în memoria calculatorului și prelucrate de către un program scris într-un limbaj de programare se numesc date. Tipurile de date care se pot descrie direct cu un anumit limbaj de programare se numesc tipurile de date elementare ale limbajului. Operațiile elementare posibil de exprimat într-un limbaj de programare se numesc instrucțiuni ale limbajului.

Limbajele de programare au evoluat foarte mult de la începuturile calculatoarelor. Tendința generală este aceea de apropiere treptată de limbajele naturale și de modul în care acestea descriu și utilizează informațiile. Abstracțiile exprimabile cu ajutorul limbajelor de programare sunt din ce în ce mai multe și cuprind o gamă din ce în ce mai largă a noțiunilor fundamentale cu care operează mintea umană.

Un alt aspect în care se reflectă evoluția limbajelor de programare este reprezentat de creșterea profunzimii și calității controlului pe care compilatoarele îl fac în momentul traducerii din limbaj de programare în limbaj mașină, astfel încât șansa de a greși la descrierea programului să fie cât mai mică.

Cu toate că limbajele de programare au făcut pași esențiali în ultimul timp, ele au rămas totuși foarte departe de limbajele naturale. Pentru a programa un calculator, trebuie să poți gândi și să poți descrie problemele într-unul dintre limbajele pe care acesta le înțelege. Din acest motiv, scrierea programelor continuă să rămână o activitate rezervată unui grup de inițiați, chiar dacă acest grup este în continuă creștere. ^

2.2 Tipuri de numere reprezentabile în calculator

Deși toate informațiile reprezentabile direct în memoria calculatorului sunt doar numere naturale, constructorii calculatoarelor de astăzi au diversificat tipurile acestora prin stabilirea unor convenții de reprezentare pentru numerele negative, reale și pentru caractere. Aceste convenții folosesc numerele naturale pentru reprezentarea celorlalte tipuri de numere. Uneori, reprezentările ocupă mai mult de un octet în memoria calculatorului, dimensiunile obișnuite fiind 1, 2, 4 sau 8 octeți. Sau, echivalent, 8, 16, 32 sau 64 de biți.

Numerele naturale sunt întotdeauna pozitive. Pentru reprezentarea unui număr întreg cu semn pot fi folosite două numere naturale. Primul dintre acestea, având doar două valori posibile, reprezintă semnul numărului și se poate reprezenta folosind o singură cifră binară. Dacă valoarea acestei cifre binare este 0, numărul final este pozitiv, iar dacă valoarea cifrei este 1, numărul final este negativ. Al doilea număr natural folosit în reprezentarea numerelor întregi cu semn conține valoarea absolută a numărului final. Această convenție, deși are dezavantajul că oferă două reprezentări pentru numărul zero, un zero pozitiv și altul negativ, este foarte aproape de reprezentarea numerelor cu semn folosită de calculatoarele moderne.

În realitate, convenția care se folosește pentru reprezentarea numerelor întregi cu semn este așa numita reprezentare în complement față de doi. Aceasta reprezintă numerele negative prin complementarea valorii lor absolute bit cu bit și apoi adunarea valorii 1 la numărul rezultat. Complementarea valorii unui bit se face înlocuind valoarea 1 cu 0 și valoarea 0 cu 1. Dacă avem de exemplu numărul 1, reprezentat pe un octet ca un șir de opt cifre binare 00000001, complementarea bitcu bit a acestui număr este numărul reprezentat pe un octet prin11111110.

Pentru a reprezenta valoarea -1 nu ne mai rămâne altceva de făcut decât să adunăm la numărul rezultat în urma complementării un 1 și reprezentarea finală a numărului întreg negativ -1 pe un octet este 11111111.

În această reprezentare, numărul 00000000 binar reprezintă numărul 0 iar numărul 10000000, care mai înainte reprezenta numărul 0 negativ, acum reprezintă numărul -128. Într-adevăr, numărul 128 se poate reprezenta în binar prin 10000000. Complementat, acest număr devine 01111111 și după adunarea cu 1, 10000000. Observați că numerele 128 și -128 au aceeași reprezentare. Convenția este aceea că se păstrează reprezentarea pentru -128 și se elimină cea pentru 128. Alegerea este datorată faptului că toate numerele pozitive au în primul bit valoarea 0 și toate cele negative valoarea -1. Prin transformarea lui 10000000 în -128 se păstrează această regulă.

Folosind reprezentarea în complement față de doi, numerele întregi reprezentabile pe un octet sunt în intervalul -128 până la 127, adică -27 până la 27 -1. În general, dacă avem o configurație de n cifre binare, folosind reprezentarea în complement față de doi putem reprezenta numerele întregi din intervalul închis -2n-1 până la 2n-1-1. În practică, se folosesc numere întregi cu semn reprezentate pe 1 octet, 2 octeți, 4 octeți și 8 octeți, respectiv 8, 16, 32 și 64 de biți.

Dacă dorim să reprezentăm un număr real în memoria calculatorului, o putem face memorând câteva cifre semnificative ale acestuia plus o informație legată de ordinul său de mărime. În acest fel, deși pierdem precizia numărului, putem reprezenta valori foarte aproape de zero sau foarte departe de această valoare.

Soluția de reprezentare este aceea de a păstra două numere cu semn care reprezintă cifrele semnificative ale numărului real respectiv un exponent care dă ordinul de mărime. Cifrele reprezentative ale numărului se numesc împreună mantisă. Numărul reprezentat în final este 0.mantisaEexponent. E are valoarea 256 spre deosebire de exponentul 10 pe care îl folosim în uzual. Dacă valoarea exponentului estefoarte mare și pozitivă, numărul real reprezentat este foarte departe de 0, înspre plus sau înspre minus. Dacă exponentul este foarte mare în valoare absolutăși negativ, numărul real reprezentat este foarte aproape de zero.

În plus, pentru a ne asigura de biunivocitatea corespondenței, avem nevoie de o convenție care să stabilească faptul că virgula este plasată imediat în fața cifrelor semnificative și că în fața acesteia se găsește o singură cifră 0. Numerele care respectă această convenție se numesc numere normalizate.

Această mutare a virgulei imediat în fața cifrelor semnificative poate să presupună modificarea exponentului care păstrează ordinul de mărime al numărului. Numerele fracționare se numesc în limbajul calculatoarelor numere în virgulă mobilă sau numere flotante tocmai din cauza acestei eventuale ajustări a poziției virgulei.

Cu această convenție nu se poate reprezenta orice număr real, dar se poate obține o acoperire destul de bună a unui interval al axei numerelor reale cu valori. Atunci când încercăm să reprezentăm în memoria calculatorului un număr real, căutăm de fapt cel mai apropiat număr real reprezentabil în calculator și aproximăm numărul inițial cu acesta din urmă. Ca rezultat, putem efectua calcule complexe cu o precizie rezonabilă.

Descrierea convenției exacte de reprezentare a numerelor reale în calculator depășește cadrul acestei prezentări. Unele dintre detalii pot fi diferite de cele prezentate aici, dar principiul este exact acesta. Convenția de reprezentare a numerelor reale este standardizată de IEEE în specificația 754.

Desigur, în unele probleme, lipsa de precizie poate să altereze rezultatul final, mai ales că, uneori, erorile se cumulează. Există de altfel o teorie complexă, analiza numerică, concepută pentru a studia căile prin care putem ține sub control aceste erori de precizie. Putem crește precizia de reprezentare a numerelor reale prin mărirea spațiului rezervat mantisei. În acest fel mărim numărul de cifre semnificative pe care îl păstrăm. În general, unitățile centrale actuale lucrează cu două precizii: numerele flotante simple, reprezentate pe 4 octeți și numerele flotante duble, reprezentate pe 8 octeți. ^

2.3 Valori de adevăr

Uneori, avem nevoie să memorăm în calculator valori de adevăr. Există doar două valori de adevăr posibile: adevărat și fals. Uneori, aceste valori de adevăr se mai numesc și valori booleene. Deși pentru memorarea acestor valori este suficientă o singură cifră binară, în practică aceste valori sunt reprezentate pe un întreg octet pentru că operațiile la nivel de bit sunt de obicei prea lente. Valorile booleene se pot combina între ele prin operații logice: “și”, “sau”, "negație”. ^

2.4 Șiruri de caractere

După cum spuneam mai înainte, deși orice informație reprezentată în calculator este în final un număr, mintea umană este obișnuită să lucreze cu cuvinte și imagini mai mult decât cu numere. De aceea, calculatorul trebuie să aibă posibilitatea să simuleze memorarea informațiilor de acest fel.

În ceea ce privește cuvintele, ele pot fi reprezentate în memoria calculatorului prin caracterele care le formează. Înșiruirea acestor caractere în memorie duce la noțiunea de șir de caractere. Pe lângă caracterele propriu-zise care construiesc cuvântul, un șir de caractere trebuie să poată memora și numărul total de caractere din șir, cu alte cuvinte lungimea sa.

În realitate, un șir de caractere nu conține doar un singur cuvânt ci este o înșiruire oarecare de caractere printre care pot exista și caractere spațiu. De exemplu, următoarele secvențe sunt șiruri de caractere: "Acesta este un șir de caractere", "Eugen", "ABCD 0123", "HGkduI;.!".

Fiecare limbaj de programare trebuie să ofere o convenție de reprezentare a șirurilor de caractere în calculator precum și o convenție de scriere a acestora în program. De obicei, cea de-a doua convenție este aceea că șirul de caractere trebuie închis între apostroafe sau ghilimele. În paragraful anterior de exemplu, am folosit ghilimele pentru delimitarea șirurilor de caractere.

Convenția de reprezentare în memorie diferă de la un limbaj de programare la altul prin modul în care este memorată lungimea șirului, precum și prin convenția de reprezentare în memorie a caracterelor: ASCII, Unicode sau alta.

Împreună cu fiecare tip de dată, limbajele de programare trebuie să definească și operațiile ce se pot executa cu datele de tipul respectiv. Pentru șirurile de caractere, principala operație este concatenarea. Prin concatenarea a două șiruri de caractere se obține un șir de caractere care conține caracterele celor două șiruri puse în prelungire. De exemplu, prin concatenarea șirurilor de caractere "unu" și ", doi", rezultă șirul de caractere: "unu, doi". ^

2.5 Tipuri primitive de valori ale unui limbaj de programare

Vom numi tipuri primitive de valori ale unui limbaj acele tipuri de valori care se pot reprezenta direct într-un anumit limbaj de programare. Pentru ca informația reprezentabilă să fie independentă de calculatorul pe care rulează programele, un limbaj de programare trebuie să-și definească propriile sale tipuri primitive, eventual diferite de cele ale unității centrale, tipuri care să generalizeze tipurile primitive ale tuturor unităților centrale.

Pentru fiecare dintre aceste tipuri primitive de valori, limbajul trebuie să definească dimensiunea locației de memorie ocupate, convenția de reprezentare și mulțimea valorilor care pot fi reprezentate în această locație. În plus, limbajul trebuie să definească operațiile care se pot executa cu aceste tipuri și comportarea acestor operații pe seturi de valori diferite.

Tipurile primitive ale unui limbaj sunt de obicei: numere de diverse tipuri, caractere, șiruri de caractere, valori de adevăr și valori de tip referință. Totuși, acest set de tipuri primitive, denumirea exactă a tipurilor și operațiile care se pot executa cu ele variază mult de la un limbaj de programare la altul. ^

2.6 Tablouri de elemente

Tipurile primitive împreună cu referințele, adică tipurile de date elementare, indivizibile ale unui limbaj, sunt insuficiente pentru necesitățile unei aplicații reale. De obicei este nevoie de organizări de date mai complicate în care avem structuri de date create prin asocierea mai multor tipuri de date elementare. Aceste structuri de organizare a informațiilor pot conține date de același tip sau date de tipuri diferite.

În cazul în care dorim să reprezentăm o structură conținând date de același tip, va trebui să folosim o structură clasică de reprezentare a datelor numită tablou de elemente. Practic, un tablou de elemente este o alăturare de locații de memorie de același fel. Alăturarea este continuă în sensul că zona de memorie alocată tabloului nu are găuri. De exemplu, putem gândi întreaga memorie internă a calculatorului ca fiind un tablou de cifre binare sau ca un tablou de octeți.

Informațiile de același fel reprezentate într-un tablou se pot prelucra în mod unitar. Referirea la un element din tablou se face prin precizarea locației de început a tabloului în memorie și a numărului de ordine al elementului pe care dorim să-l referim. Numărul de ordine al unui element se numește indexul elementului. De aceea, tablourile se numesc uneori și structuri de date indexate.

Să presupunem că, la adresa 1234 în memorie, deci începând cu octetul numărul 1234, avem un tablou de 200 de elemente de tip întregi cu semn reprezentați pe 4 octeți. Pentru a accesa elementul numărul 123 din tablou, trebuie să îi aflăm adresa în memorie. Pentru aceasta, trebuie să calculăm deplasamentul acestui element față de începutul tabloului, cu alte cuvinte, la câți octeți distanță față de începutul tabloului se găsește elementul numărul 123.

Pentru calcului deplasamentului, este nevoie să știm cu ce index începe numerotarea elementelor din tablou. De obicei aceasta începe cu 0 sau cu 1, primul element fiind elementul 0 sau elementul 1. Să presupunem, în cazul nostru, că numerotarea ar începe cu 0. În acest caz, elementul cu indexul 123 este de fapt al 124-lea element din tablou, deci mai are încă 123 de elemente înaintea lui. În aceste condiții, pentru a afla adresa elementului dat, este suficient să adăugăm la adresa de început a tabloului, deplasamentul calculat ca numărul de elemente din față înmulțit cu dimensiunea unui element din tablou. Adresa finală este 1234 + 123 * 4 = 1726.

Pentru a putea lucra cu elementele unui tablou este deci suficient să memorăm adresa de început a tabloului și indexul elementelor pe care dorim să le accesăm. Alternativa ar fi fost să memorăm adresa locației fiecărui element din tablou.

Unul dintre marile avantaje ale utilizării tablourilor este acela că elementele dintr-un tablou se pot prelucra în mod repetitiv, apelându-se aceeași operație pentru un subset al elementelor din tablou. Astfel, într-un program putem formula instrucțiuni de forma: pentru elementele tabloului T începând de la al treilea până la al N-lea, să se execute operația O. Numărul N poate fi calculat dinamic în timpul execuției programului, în funcție de necesități.

Elementele unui tablou pot fi de tip primitiv, referință sau pot fi tipuri compuse, inclusiv alte tablouri. ^

2.7 Expresii de calcul

Sarcina principală a calculatoarelor este aceea de a efectua calcule. Pentru a putea efectua aceste calcule, calculatorul trebuie să primească o descriere a operațiilor de calcul pe care le are de executat. Calculele simple sunt descrise cel mai bine prin expresii de calcul.

Expresiile sunt formate dintr-o serie de valori care intră în calcul, numite operanzi și din simboluri care specifică operațiile care trebuiesc efectuate cu aceste valori, numite operatori. Operatorii reprezintă operații de adunare, înmulțire, împărțire, concatenare a șirurilor de caractere, etc.

Operanzii unor expresii pot fi valori elementare precum numerele, șirurile de caractere sau pot fi referiri către locații de memorie în care sunt memorate aceste valori. Tot operanzi pot fi și valorile unor funcții predefinite precum sinus, cosinus sau valoarea absolută a unui număr. Calculul complex al valorii acestor funcții pentru argumentele de intrare este astfel ascuns sub un nume ușor de recunoscut.

Figura 2.1 Un exemplu de expresie și componentele acesteia ^

2.8 Variabile

Uneori, atunci când calculele sunt complexe, avem nevoie să păstrăm rezultate parțiale ale acestor calcule în locații temporare de memorie. Alteori, chiar datele inițiale ale problemei trebuie memorate pentru o folosire ulterioară. Aceste locații de memorie folosite ca depozit de valori le vom numi variabile. Variabilele pot juca rol de operanzi în expresii.

Pentru a regăsi valoarea memorată într-o anumită variabilă, este suficient să memorăm poziția locației variabilei în memorie și tipul de dată memorată la această locație, numit și tipul variabilei. Cunoașterea tipului variabilei este esențială la memorarea și regăsirea datelor. La locația variabilei găsim întotdeauna o configurație de cifre binare. Interpretarea acestor cifre binare se poate face numai cunoscând convenția de reprezentare care s-a folosit la memorarea acelor cifre binare. Mai mult, tipul de variabilă ne și spune câți octeți de memorie ocupă locația respectivă.

Pentru a putea referi variabilele în interiorul unui program, trebuie să atribuim câte un nume pentru fiecare dintre acestea și să rezervăm locațiile de memorie destinate lor. Această rezervare a locațiilor se poate face fie la pornirea programului fie pe parcurs.

Putem împărți variabilele în funcție de perioada lor de existență în variabile statice, care există pe tot parcursul programului și variabile locale care se creează doar în secțiunea de program în care este nevoie de ele pentru a fi distruse imediat ce se părăsește secțiunea respectivă. Variabilele statice păstrează de obicei informații esențiale pentru execuția programului precum numele celui care a pornit programul, data de pornire, ora de pornire sau numărul p . Variabilele locale păstrează valori care au sens doar în contextul unei anumite secțiuni din program. De exemplu, variabilele care sunt utilizate la calculul sinusului dintr-un număr, sunt inutile și trebuiesc eliberate imediat ce calculul a fost terminat. În continuare, doar valoarea finală a sinusului este importantă.

În plus, există variabile care se creează doar la cererea explicită a programului și nu sunt eliberate decât atunci când programul nu mai are nevoie de ele. Aceste variabile se numesc variabile dinamice. De exemplu, se creează o variabilă dinamică atunci când utilizatorul programului introduce un nou nume într-o listă de persoane. Crearea și ștergerea acestui nume nu are legătură cu faza în care se află rularea programului ci are legătură cu dorința celui care utilizează programul de a mai adăuga sau șterge un nume în lista persoanelor cu care lucrează, pentru a le trimite, de exemplu, felicitări de anul nou. ^

2.9 Instrucțiuni

Operațiile care trebuiesc executate de un anumit program sunt exprimate în limbajele de programare cu ajutorul unor instrucțiuni. Spre deosebire de limbajele naturale, există doar câteva tipuri standard de instrucțiuni care pot fi combinate între ele pentru a obține funcționalitatea programelor. Sintaxa de scriere a acestor tipuri de instrucțiuni este foarte rigidă. Motivul acestei rigidități este faptul că un compilator recunoaște un anumit tip de instrucțiune după sintaxa ei și nu după sensul pe care îl are ca în cazul limbajelor naturale.

Tipurile elementare de instrucțiuni nu s-au schimbat de-a lungul timpului pentru că ele sunt conținute în însuși modelul de funcționare al calculatoarelor Von Neumann.

În primul rând, avem instrucțiuni de atribuire. Aceste instrucțiuni presupun existența unei locații de memorie căreia dorim să-i atribuim o anumită valoare. Atribuirea poate să pară o operație foarte simplă dar realitatea este cu totul alta. În primul rând, valoarea respectivă poate fi rezultatul evaluării (calculului) unei expresii complicate care trebuie executată înainte de a fi memorată în locația de memorie dorită.

Apoi, însăși poziția locației în memorie ar putea fi rezultatul unui calcul în urma căruia să rezulte o anumită adresă. De exemplu, am putea dori să atribuim o valoare unui element din interiorul unui tablou. Într-o astfel de situație, locația de memorie este calculată prin adunarea unui deplasament la adresa de început a tabloului.

În fine, locația de memorie și valoarea pe care dorim să i-o atribuim ar putea avea tipuri diferite. În acest caz, operația de atribuire presupune și transformarea, dacă este posibilă, a valorii într-o nouă valoare de același tip cu locația de memorie. În plus, această transformare trebuie efectuată în așa fel încât să nu se piardă semnificația valorii originale. De exemplu, dacă valoarea originală era -1 întreg iar locația de memorie este de tip flotant, valoarea obținută după transformare trebuie să fie -1.0.

Transformarea valorilor de la un tip la altul se numește în termeni informatici conversie. Conversiile pot apărea și în alte situații decât instrucțiunile de atribuire, de exemplu la transmiterea parametrilor unei funcții, în calculul unei expresii.

Forma generală a unei instrucțiuni de atribuire este:

Locație = Valoare

Locația poate fi o adresă cu tip, un nume de variabilă sau o expresie în urma căreia să rezulte o adresă cu tip. Printr-o adresă cu tip înțelegem o adresă de memorie pentru care a fost specificat tipul valorilor care pot fi memorate în interior. O adresă simplă, fără tip, nu poate intra într-o atribuire pentru că nu putem ști care este convenția de reprezentare care trebuie folosită la memorarea valorii.

Al doilea tip de instrucțiune elementară este instrucțiunea condițională. Această instrucțiune permite o primă formă de ramificare a ordinii în care se execută instrucțiunile. Ramificarea depinde de o condiție care poate fi testată pe setul de date cu care lucrează aplicația. În funcție de valoarea de adevăr care rezultă în urma evaluării condiției se poate executa o instrucțiune sau alta. Modelul de exprimare a unei expresii condiționale este următorul:

Dacă condiția este adevărată

execută instrucțiunea 1

altfel

execută instrucțiunea 2.

Cele două ramuri de execuție sunt disjuncte în sensul că dacă este executată instrucțiunea de pe o ramură atunci cu siguranță instrucțiunea de pe cealaltă ramură nu va fi executată. După execuția uneia sau a celeilalte instrucțiuni ramurile se reunifică și execuția își continuă drumul cu instrucțiunea care urmează după instrucțiunea condițională.

Figura 2.2 Schema de funcționare a unei instrucțiuni condiționale

Condiția care hotărăște care din cele două instrucțiuni va fi executată este de obicei un test de egalitate între două valori sau un test de ordonare în care o valoare este testată dacă este mai mică sau nu decât o altă valoare. Condițiile pot fi compuse prin conectori logici de tipul “și”, “sau” sau “non”, rezultând în final condiții de forma: dacă variabila numită Temperatură este mai mică decât 0 și mai mare decât -10, atunci…

Instrucțiunile condiționale au și o variantă în care, în funcție de o valoare întreagă alege o instrucțiune dintr-un set de instrucțiuni și apoi o execută. Această formă este în realitate derivată din instrucțiunea condițională clasică, ea putând fi exprimată prin:

Dacă Variabila este egală cu Valoarea 1 atunci

execută instrucțiunea 1

altfel, dacă Variabila este egală cu Valoarea 2 atunci

execută instrucțiunea 2

altfel, dacă Variabila …

…

altfel

execută instrucțiunea implicită.

Un al treilea tip de instrucțiuni elementare sunt instrucțiunile de ciclare sau repetitive sau buclele. Acestea specifică faptul că o instrucțiune trebuie executată în mod repetat. Controlul ciclurilor de instrucțiuni se poate face pe diverse criterii. De exemplu se poate executa o instrucțiune de un număr fix de ori sau se poate executa instrucțiunea până când o condiție devine adevărată sau falsă.

Condiția de terminare a buclei poate fi testată la începutul buclei sau la sfârșitul acesteia. Dacă condiția este testată de fiecare dată înainte de execuția instrucțiunii, funcționarea ciclului se poate descrie prin:

Atâta timp cât Condiția este adevărată

execută Instrucțiunea

Acest tip de instrucțiuni de ciclare poată numele de cicluri while, cuvântul while însemnând în limba engleză "atâta timp cât”. În cazul ciclurilor while, există posibilitatea ca instrucțiunea din interiorul ciclului să nu se execute niciodată, dacă condiția este de la început falsă.

Figura 2.3 Schema de funcționare a unui ciclu while

Dacă condiția este testată de fiecare dată după execuția instrucțiunii, funcționarea ciclului se poate descrie prin:

Execută Instrucțiunea

atâta timp cât Condiția este adevărată.

Acest tip de instrucțiuni de ciclare poartă numele de cicluri do-while, cuvântul do însemnând în limba engleză "execută”. În cazul ciclurilor do-while instrucțiunea din interiorul ciclului este garantat că se execută cel puțin o dată, chiar dacă valoare condiției de terminare a buclei este de la început fals.

Figura 2.4 Schema de funcționare a unui ciclu do-while

După cum ați observat, probabil, dacă instrucțiunea din interiorul buclei nu afectează în nici un fel valoarea de adevăr a condiției de terminare a buclei, există șansa ca bucla să nu se termine niciodată.

Al patrulea tip de instrucțiuni sunt apelurile de proceduri. O procedură este o secvență de instrucțiuni de sine stătătoare și parametrizată. Un apel de procedură este o instrucțiune prin care forțăm programul să execute pe loc instrucțiunile unei anumite proceduri. După execuția procedurii, se continuă cu execuția instrucțiunii de după apelul de procedură.

Procedurile sunt utile atunci când, în zone diferite ale programului, dorim să executăm aceeași secvență de instrucțiuni. În aceste situații, putem să scriem secvența de instrucțiuni o singură dată și s-o apelăm apoi oriunde din interiorul programului.

De exemplu, ori de câte ori vom dori să ștergem toate semnele de pe ecranul calculatorului, putem apela la o procedură, pentru a nu fi nevoiți să scriem de fiecare dată secvența de instrucțiuni care duce la ștergerea ecranului. Procedura în sine, o vom scrie o singură dată și îi vom da un nume prin care o vom putea apela ulterior ori de câte ori avem nevoie.

Uneori secvența de instrucțiuni dintr-o procedură depinde de niște parametri adică de un set de valori care sunt precizate doar în momentul apelului procedurii. În aceste cazuri, procedura se poate comporta în mod diferit în funcție de valorile de apel ale acestor parametri. În funcție de tipul declarat al unor parametri și de tipul real al valorilor care sunt trimise ca parametri într-un apel de procedură, se poate întâmpla ca înainte de apelul propriu-zis să fie necesară o conversie de tip.

De exemplu, în cazul procedurii care calculează valoarea sinusului unui număr, parametrul de intrare este însuși numărul pentru care trebuie calculat sinusul. Rezultatul acestui calcul va fi diferit, în funcție de valoarea la apel a parametrului. În mod normal, parametrul va fi un număr flotant iar dacă la apel vom transmite ca parametru o valoare întreagă, ea va fi convertită spre o valoare flotantă.

În urma apelului unei proceduri poate rezulta o valoare pe care o vom numi valoare de retur a procedurii. De exemplu, în urma apelului procedurii de calcul a sinusului unui număr, rezultă o valoare flotantă care este valoarea calculată a sinusului. Acest tip de proceduri, care întorc valori de retur se mai numesc și funcții. Funcțiile definesc întotdeauna un tip al valorii de retur pe care o întorc. În acest fel, apelurile de funcții pot fi implicate și în formarea unor expresii de calcul sub formă de operanzi, expresia cunoscând tipul de valoare care trebuie așteptată ca valoare a apelului unei anumite funcții.

Analogia cu funcțiile pe care le-ați studiat la matematică este evidentă dar nu totală. În cazul funcțiilor scrise într-un limbaj de programare, două apeluri consecutive ale aceleiași funcții cu aceleași valori ca parametri se pot comporta diferit datorită dependenței de condiții exterioare sistemului format din funcție și parametrii de apel. În plus, se poate întâmpla ca modul de comportare al funcției și valoarea de retur să nu fie definite pentru anumite valori de apel ale parametrilor. Deși scrierea acestui tip de funcții este nerecomandabilă, în practică întâlnim astfel de funcții la tot pasul. ^

[cuprins]
(C) IntegraSoft 1996-1998