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. |