INSTRUCŢIUNI
5.1. SCURT ISTORIC AL METODELOR DE PROGRAMARE
Vom prezenta în continuare câteva metode de programare dar nu exhaustiv, nefiind aici cadrul adecvat (eventual într-un curs de Software Engineering). O clasificare cronologica a ceea ce vrem sa prezentam ar fi urmatoarea:
programarea artizanala;
programarea procedurala;
programarea modulara;
programarea structurata;
programarea prin abstractizarea datelor;
programarea orientata spre obiecte.
5.1.1. Programare artizanala
Aceasta metoda de fapt nu este o metoda propriu-zisa ci este prima modalitate de programare odata cu aparitia calculatoarelor. Intuitia si experienta programatorului joaca un rol important. Fiecare programator îsi are propriile reguli de programare. Programele sunt monolitice (un singur corp de instructiuni), lungi si greu de înteles de alt programator. Însusi cel ce a elaborat un astfel de program întâmpina dificultati de întelegere a propriului program dupa un timp oarecare.
5.1.2. Programare procedurala
Odata cu aparitia primelor limbaje de înalt nivel se utilizeaza programarea procedurala. Necesitatea ca anumite secvente de program sa fie folosite de mai multe ori duce la organizarea acestora în unitati distincte de program numite în diverse limbaje subprograme, subrutine, proceduri, etc. De multe ori procedurile trebuie sa fie generale deci procesarea sa faca abstractizare de valorile datelor. De exemplu o procedura de calcul al radicalului de ordinul 2 trebuie sa calculeze acest lucru din orice numar real pozitiv iar pentru cele negative sa semnaleze eroare. Procedurile trebuie deci parametrizate cu anumite variabile numite parametri formali. Valorile de la apel ale parametrilor formali se numesc parametri efectivi. Programarea procedurala are la baza deci utilizarea procedurilor, iar acestea realizeaza o abstractizare prin parametri. La apelare o procedura functioneaza dupa principiul cutiei negre (black box): se cunosc intrarile si iesirile rezultate din acestea dar nu si modul de transformare care nu e important în acest moment.
În majoritatea limbajelor procedurale de programare se considera 2 categorii de proceduri:
proceduri care definesc o valoare de revenire (denumite si functii);
proceduri care nu definesc o valoare de revenire.
În limbajele C si C++ procedurile de ambele categorii se numesc functii.
5.1.3. Programarea modulara
Pe masura ce complexitatea aplicatiilor a crescut, a aparut ideea de a descompune problemele în subprobleme mai simple care la rândul lor pot fi descompuse în altele mai simple si asa mai departe. În felul acesta se ajunge la o descompunere arborescenta a problemei date în subprobleme mai simple. Programarea subproblemelor devine o problema mai simpla si fiecare subproblema are o anumita independenta fata de celelalte subprobleme. De asemenea, interfata ei cu celelalte subprobleme este limitata si bine precizata prin procesul de descompunere a problemei initiale. De obicei, programarea unei subprobleme, componenta a descompunerii arborescente a problemei initiale, conduce la realizarea unui numar relativ mic de proceduri (functii). Aceste functii pot prelucra în comun anumite date. Unele dintre ele sunt independente de functiile realizate pentru alte subprobleme componente ale descompunerii arborescente. Altele realizeaza chiar interfata cu subproblemele învecinate.
Despre functiile obtinute în urma programarii unei subprobleme se obisnuieste sa se spuna ca sunt înrudite. De obicei, aceste functii, împreuna cu datele pe care le prelucreaza, se pastreaza într-un fisier si se compileaza independent.
O colectie de functii înrudite, împreuna cu datele pe care le prelucreaza în comun formeaza un modul. În felul acesta, problema initiala se realizeaza printr-un program alcatuit din module.
Programarea modulara are la baza elaborarea programelor pe module.
O parte din datele utilizate în comun de functiile modulului, sau chiar toate datele modulului, nu sunt necesare si în alte module. Aceste date pot fi protejate sau cum se mai spune, ascunse în modul.
Limbajul C si C++, permite ascunderea datelor în modul folosind date care au clasa de memorie static. Mai mult, pot fi declarate si functiile ca statice si atunci ele vor fi ascunse în modul (nu pot fi apelate din afara modului). Ascunderea functiilor în modul se face pentru acele functii care nu se utilizeaza la realizarea interfetei modulului cu celelalte module. Ascunderea datelor si functiilor în module permite protejarea datelor si preîntâmpina utilizarea eronata a functiilor.
5.1.4. Programarea structurata
Descompunerea unei probleme în subprobleme mai simple se poate face succesiv în mai multe etape, pâna când subproblemele sunt direct programabile sub forma unor proceduri sau module. Aceasta descompunere succesiva se mai numeste rafinare pas cu pas (stepwise refinement).. Evident ca se obtine o descompunere arborescenta. Procedurile se pot organiza sau nu în module. În cadrul procedurilor se folosesc anumite structuri de control a executiei. Aceasta impune o anumita disciplina a programarii. Structurile de control de sunt:
secventa;
iteratia (pretestata, posttestata, cu numar prestabilit de ciclari);
alternativa (simpla, completa, generalizata).
Instructiunea de baza (primitiva) în cadrul acestor structuri de control este instructiunea de atribuire.
Aceasta abordare a programarii s-a nascut din necesitatea eliminarii instructiunii de control GO TO care face saltul neconditionat la o instructiune precizata, alta decât instructiunea urmatoare ei. Profesorul Dijsktra de la Universitatea din Eindhoven spunea, prin anul 1965, "calitatea unui programator este invers proportionala cu numarul de instructiuni GO TO folosite" si a impus D-structurile de control:
secventa;
iteratia pretestata;
alternativa simpla.
D-structurile se regasesc în toate limbajele procedurale. Corespondenta ar fi:
secventa este echivalenta cu executia instructiunilor în ordinea scrierii lor în programul sursa;
b) iteratia pretestata echivalenta cu WHILE ... DO;
c) alternativa simpla echivalenta cu IF ... THEN.
O ilustrare grafica a celor trei D-structuri se da în continuare.
da
S1 C
nu da
S2 S C
S nu
.
Sn
S-a demonstrat ulterior (Bohm si Jacopini) ca orice algoritm se poate descrie doar cu D-structurile dar pentru o mai buna lizibilitate si întelegere a programelor sursa s-au adaugat si iteratia postestata (REPEAT ... UNTIL), iteratia cu numar prestabilit de ciclari (FOR ... DO), alternativa completa (IF ... THEN ... ELSE) si alternativa generalizata (CASE ... OF).
În unele limbaje se folosesc si alte structuri pe lânga cele de mai sus pentru o cât mai fidela reflectare a algoritmului.
5.1.5. Programarea prin abstractizarea datelor
În toate aceste tehnologii anterioare se urmareste mai mult organizarea programului si mai putin rezolvarea cât mai naturala a problemei. Programarea prin abstractizarea datelor si programarea orientata spre obiecte propun metodologii în care conceptele deduse din analiza problemei sa poata fi reflectate cât mai fidel în program si sa se poata manevra cu instantieri ale acestor concepte cât mai natural. Se realizeaza o mai mare fidelitate a programului fata de problema. De exemplu daca într-o problema în care se proceseaza numere complexe e nevoie sa se lucreze într-o forma cât mai apropiata cu forma matematica se poate introduce tipul COMPLEX (tip care nu exista în limbajele de programare) si apoi se pot declara variabile de acest tip. Mai mult ar trebui sa se poata face toate operatiile matematice asupra datelor de tip COMPLEX. În general un TAD (Tip Abstract de Date) are doua componente fundamentale:
datele membru (reflecta reprezentarea tipului);
functiile membru (reflecta comportamentul tipului).
5.1.6. Programarea orientata spre obiecte
Un neajuns al programarii prin abstractizarea datelor este faptul ca nu permite exprimarea legaturilor dintre diferite concepte (TAD-uri). Singura legatura dintre concepte care se poate exprima, este aceea ca datele membru ale unei clase pot fi obiecte ale unei alte clase. Acest lucru nu este suficient în cazul în care conceptele sunt strâns dependente între ele formând structuri ierarhice. Exprimarea ierarhiilor conduce la atribute suplimentare cum sunt cele de mostenire. Aceste atribute conduc la un nou model de programare pe care îl numim programare orientata obiect.
În vârful unei ierarhii se afla fenomenul sau forma de existenta care are trasaturi comune pentru toate celelalte componente ale ierarhiei respective. Pe nivelul urmator al ierarhiei se afla componentele care pe lânga trasaturile comune de pe nivelul superior, mai au si trasaturi suplimentare, specifice. O ierarhie, de obicei, are mai multe nivele, iar situarea unui element pe un nivel sau altul al ierarhiei este uneori o problema deosebit de complexa. Dam în exemplul urmator o ierarhie arborescenta pentru conceptele de paralelogram, dreptunghi, romb si patrat.
Paralelogram
Dreptunghi Romb
Patrat
Daca paralelogramul se afla în vârful ierarhiei atunci pe nivelul imediat inferior se aseaza dreptunghiul (paralelogramul cu un unghi drept) dar si rombul (paralelgramul cu 2 laturi alaturate congruente). Apoi patratul se poate defini fie ca un dreptunghi cu laturile congruente fie ca un romb cu un unghi drept. Conceptul de pe fiecare nivel se observa ca mosteneste proprietatile conceptului imediat superior din care este derivat.
La ora actuala, toate ramurile cunoasterii stiintfice sunt pline de ierarhii rezultate în urma clasificarii cunostintelor acumulate în perioada lunga de observare a fenomenelor si formelor de existenta a lumii materiale si spirituale. Clasificarile ierarhice ale cunostintelor pot fi întâlnite atât în domeniile care pleaca de la cele mai concrete forme ale lumii materiale, cum sunt botanica, zoologia, biologia, etc cât si în domenii care studiaza concepte dintre cele mai abstracte, cum ar fi matematica sau filozofia.
Aceste ierarhii sunt rezultatul definirii conceptelor dupa regula includerii "genul proxim si diferenta specifica".
Limbajul C dispune de un set bogat de instructiuni care permit scrierea de:
programe structurate,
programe flexibile,
programe compacte.
Totodata limbajul C permite aplicarea metodelor de programare procedurala, programare modulara si programare structurata. Pe lânga aceste metodologii limbajul C++ permite si programarea prin abstractizarea datelor si programarea orientata spre obiecte.
Vom descrie în continuare instructiunile limbajului C. Ca o caracteristica sintactica toate instructiunile limbajului se termina prin caracterul ";", exceptie facând instructiunile care se termina cu acolada închisa.
Limbajul C dispune de un set bogat de instructiuni care permit scrierea de:
programe structurate,
programe flexibile,
programe compacte.
Totodata limbajul C permite aplicarea metodelor de programare procedurala, programare modulara si programare structurata. Pe lânga aceste metodologii limbajul C++ permite si programarea prin abstractizarea datelor si programarea orientata spre obiecte.
Vom descrie în continuare instructiunile limbajului C. Ca o caracteristica sintactica toate instructiunile limbajului se termina prin caracterul ";", exceptie facând instructiunile care se termina cu acolada închisa.
5.2. INSTRUCŢIUNEA VIDĂ
Instructiunea vida se reduce la caracterul ";". Ea nu are nici un efect. Adesea este nevoie de ea la constructii în care se cere prezenta unei instructiuni, dar nu este necesar sa se execute nimic în punctul respectiv.
5.3. INSTRUCŢIUNEA EXPRESIE
Instructiunea expresie se obtine scriind punct si virgula dupa o expresie, deci:
expresie;
Exista cazuri particulare ale instructiunii expresie:
expresia de atribuire, care de altfel este cel mai important caz particular al instructiunii expresie:
expresie;
apelul unei functii:
nume_functie (par1, par2, . . . parn);
incrementarile si decrementarile pre si post fixate:
variabila++; ++variabila;
variabila- -; - - variabila;
Exemplu:
void main (void)
5.4. INSTRUCŢIUNEA COMPUSĂ
Instructiunea compusa este o succesiune de declaratii urmate de instructiuni, succesiune inclusa între acolade:
Pot lipsi declaratiile sau instructiunle dar nu în acelasi timp. Daca delaratiile sunt prezente, atunci ele definesc variabile care sunt valabile numai în instructiunea compusa respectiva.
Exemplu:
Observatii:
1o. Dupa acolada inchisa a unei instructiuni compuse nu se pune ";".
2o. Corpul unei functii are aceeasi structura ca si instructiunea compusa, deci o functie are formatul:
antetul functiei
instructiune compusa
5.5. INSTRUCŢIUNEA if
Instructiunea if permite sa realizam o ramificare a executiei în functie de valoarea unei expresii. Ea are doua formate ce permit aplicarea structurii de alternativa simpla si compusa.
Formatul 1:
if (expresie) instructiune;
Efectul:
se evalueaza expresia din paranteze;
daca valoarea expresiei este diferita de zero (deci conform conventiei are valoarea adevarat), atunci se executa instructiune, altfel se trece la instructiunea urmatoare.
Formatul 2:
if (expresie) instructiune_1;
else instructiune_2;
Efectul:
se evalueaza expresia din paranteze;
daca valoarea expresiei este diferita de zero (deci conform conventiei are valoarea adevarat), atunci se executa instructiune_1, altfel se executa instructiune_2; apoi în ambele cazuri se trece la instructiunea urmatoare.
Observatii:
1o. Se pot folosi instructiuni if imbricate, nivelul de imbricare fiind oarecare (deci nu exista o limitare a numarului de imbricari).
2o. Pentru mai multe imbricari se foloseste regula asocierii if-lui cu else astfel:
un else se pune în corespondenta cu primul if care se afla înaintea lui în textul sursa si nu este inclus în instructiunea care îl precede pe el si nici nu îi corespunde deja un else.
Exemple
void main (void)
void main (void)
// de-al doilea if este inclus in instructiunea compusa care
else a=0; // il precede pe if
void main (void) // citeste trei intregi si scrie minimul dintre ei
5.6. INSTRUCŢIUNEA while
Instructiunea while are urmatorul format:
while (expresie) instructiune;
Cu ajutorul instructiunii while se realizeaza structura repetitiva pretestata (conditionata anterior).
Efectul:
se evalueaza valoarea expresiei din paranteze;
daca expresia are valoarea diferita de zero, atunci se executa instructiune si se reia punctul 1), altfel se trece la instructiunea urmatoare instructiunii while.
Deci instructiune se executa repetat atâta timp cât expresia din paranteza este diferita de zero. Se observa ca daca expresia are valoarea zero de la început, atunci instructiune nu se executa niciodata.
Antetul ciclului while este constructia while (expresie) iar instructiune formeaza corpul ciclului. În cazul în care este necesar sa se execute repetat mai multe instructiuni, se utilizeaza o instructiune compusa formata din instructiunile respective.
Exemplu:
Vom crea un program care citeste un întreg n si scrie n!. Algoritmul în pseudocod este urmatorul:
Citeste n
f=1
i=2
CâtTimp i<=n executa
f=f*i;
i=i+1
SfârsitCâtTimp
Scrie n,f
Programul în C este:
#include<stdio.h>
void main (void)
printf("\nn=%d, iar n!=%g\n",n,f);
Corpul ciclului while se poate scrie mai compact astfel:
while (i<=n) f*=i++;
5.7. INSTRUCŢIUNEA for
Instructiunea for, ca si instructiunea while, se utilizeaza pentru a realiza o structura repetitiva pretestata. Formatul instructiunii este:
for(exp1; exp2; exp3) instructiune;
Antetul ciclului este definit de for(exp1; exp2; exp3) iar instructiune formeaza corpul ciclului. Prima expresie exp1 constituie partea de initializare a ciclului, iar exp3 este partea de reinitializare a ciclului. Conditia de continuare a ciclului este exp2. De obicei exp1 si exp3 reprezinta atribuiri.
Efectul:
se executa secventa de initializare definita de expresia exp1;
se evalueaza exp2; daca exp2 are valoarea zero, atunci se iese din ciclu, adica se trece la instructiunea urmatoare instructiunii for, altfel se executa instructiunea din corpul ciclului;
se executa apoi secventa de reinitializare definita de exp3, apoi se reia secventa de la punctul 2).
Observatii:
1o. Ca si în cazul instructiunii while, instructiunea din corpul ciclului for poate sa nu se execute niciodata daca exp2 are valoarea zero chiar la prima evaluare.
2o. Expresiile din antetul instructiunii for pot fi si vide; totusi caracterele ";" vor fi întotdeauna prezente.
3o. Comparând instructiunile for si while observam ca instructiunea for cu formatul anterior se poate realiza cu secventa urmatoare folosind while:
exp1;
while (exp2)
Invers, o instructiune while de forma: while (exp) instructiune este echivalenta cu urmatoarea instructiune for:
for(; exp; ) instructiune.
Autorii limbajului C propun ca instructiunea for sa se foloseasca cu prioritate pentru ciclurile care au pas.
Exemple:
Vom da o secventa de instructiuni care însumeaza elementele unui tablou:
s=0;
for(i=0; i<n; i++) s=s+tab[i];
sau scrisa mai compact:
for (s=0, i=0; i<n; i++) s+=tab[i];
În continuare vom da un mic program care afiseaza numarul caracterelor citite de la intrarea standard stdin.
#include <stdio.h>
void main(void)
sau scrisa cu instructiunea while
#include <stdio.h>
void main(void)
5.8. INSTRUCŢIUNEA do-while
Aceasta instructiune realizeaza structura repetitiva conditionata posterior (posttestata) dar modificata fata de REPEAT .. UNTIL. Aceasta instructiune s-a introdus pentru o mai mare flexibilitate în scrierea programelor. Formatul ei este: do
instructiune;
while (exp);
Efectul:
se executa instructiunea instructiune;
se evalueaza expresia exp din paranteze;
daca valoarea expresiei este zero se trece la instructiunea urmatoare instructiunii do-while; altfel se revine si se executa din nou instructiune.
Observatii:
1o. Structura realizata de instructiunea do-while poate fi realizata printr-o secventa în care se foloseste instructiunea while astfel:
instructiune;
while (exp) instructiune;
2o. Se observa ca în cazul instructiunii do-while, corpul ciclului se executa cel putin odata, spre deosebire de ciclurile while si for unde corpul ciclului poate sa nu se execute niciodata.
Exemplu:
Vom da un program care calculeaza radacina patrata dintr-un numar real a>=0.
#include<stdio.h>
#define EPS 1e-10
void main (void)
while (y >= EPS);
printf ("radacina patrata din:%g este: %.2lf\n",a,x2); // 2 zecimale
} //sfirsit else
}
5.9. INSTRUCTIUNEA switch
Instructiunea switch permite realizarea structurii alternativa generalizata. Ea este echivalenta cu o imbricare de structuri de alternativa simple. Utilizarea instructiunii switch face în schimb programul mult mai clar.
Formatul instructiunii switch este urmatorul:
switch (exp)
unde: c1,. . . cn sunt constante sau constante simbolice;
sir1, . . . ,sirn, sir sunt siruri de instructiuni.
Efectul:
se evalueaza expresia din paranteza;
se compara pe rând valoarea expresiei cu valorile constantelor c1, . . . , cn;
daca valoarea expresiei coincide cu valoarea lui ck, se executa secventa de instructiuni definita prin sirk; în cazul în care valoarea expresiei nu coincide cu nici una din constantele c1, . . . , cn, se executa secventa de instructiuni definita prin sir;
dupa executia secventei sirk sau sir se trece la instructiunea urmatoare instructiunii switch, adica la prima instructiune aflata dupa acolada închisa care termina instructiunea switch respectiva; evident, acest lucru are loc daca sirul care se executa nu impune, el insusi, un alt mod de continuare a executiei, de exemplu o revenire din functia respectiva, un salt la o anumita instructiune, etc.
Observatii:
1o. Ramura default nu este obligatorie. În lipsa ei, daca valoarea expresiei nu coincide cu nici una din constantele c1,. . . , cn, instructiunea switch respectiva nu are nici un efect.
2o.Constructia break reprezinta o instructiune. Ea termina fiecare ramura de instructiuni sir1, . . . , sirn, provocând saltul la instructiunea urmatoare instructiunii switch sau, cum se mai spune, realizeaza iesirea din instructiunea switch.
3o. Instructiunea break nu este obligatorie. În cazul în care este absenta, se executa secvential urmatoarea ramura. De exemplu daca avem secventa:
switch (exp)
ea se executa în felul urmator:
daca valoarea expresiei este egala cu c1 se executa sir1 si apoi sir2;
daca valoarea expresiei este egala cu c2 se executa sir2;
daca valoarea expresiei difera de valorile c1 si c2 instructiunea switch de mai sus nu este efectiva, se trece la instructiunea urmatoare care urmeaza dupa switch.
secventa de mai sus se putea realiza si astfel:
if (exp = = c1)
else if (exp = = c2) sir2
Exemplu:
Vom citi din fisierul de intrare constructii de forma: op1 operator op2, unde op1 si op2 sunt numere întregi (operanzi întregi) iar operator este un operator aritmetic . La iesire se va scrie valoarea expresiei citite. De exemplu daca se citeste secventa 100/3 se va afisa rezultatul 33. Programul permite citirea si evaluarea mai multor astfel de expresii, pâna la întâlnirea sfârsitului de fisier.
#include <stdio.h>
void main (void)
else rezultat = op1 / op2;
break;
default : printf ("operator eronat\n");
rezultat = 0;
} // sfarsit switch
printf ("%d %c %d %d\n", op1, operator, op2, rezultat);
} else
printf ("expresie eronat \n"); // sfarsit if si while
5.10. INSTRUCŢIUNEA break
Formatul instructiunii este urmatorul:
break;
De obicei instructiunea break se foloseste pentru a iesi dintr-un ciclu. Daca exista mai multe cicluri imbricate instructiunea break va trece controlul la ciclul de nivel imediat superior (deci imbricarea ramâne, nu se iese din toate ciclurile). O alta utilizare este în instructiunea switch, dupa cum am observat în paragraful anterior.
Un alt exemplu de utilizare frecventa este iesirea dintr-un ciclu infinit de forma:
for ( ; ; )
5.11. INSTRUCŢIUNEA continue
Formatul instructiunii este urmatorul:
continue;
Efectul:
în ciclurile while si do-while ea realizeaza saltul la evaluarea expresiei care decide asupra continuarii ciclului;
în ciclul for ea realizeaza saltul la pasul de reinitializare.
Observatie:
1o. Instructiunea continue se utilizeaza numai în corpul unui ciclu, permitând, dupa caz, sa se treaca la pasul urmator al ciclului sau sa se iasa din ciclu.
5.12. INSTRUCŢIUNEA goto
Conform principiilor programarii structurate instructiunea goto nu ar fi necesara. Dar ea a fost introdusa în limbaj, deoarece, în anumite cazuri, se dovedeste a fi utila, asigurând o flexibilitate mai mare în programare. De multe ori iesirea dintr-un ciclu imbricat în alte cicluri se realizeaza mai simplu cu ajutorul instructiunii goto. În lipsa ei ar trebui sa folosim mai multi indicatori si teste asupra valorilor acestora pentru iesirea din ciclu. Saltul facut de goto se face la o instructiune care este prefixata de o eticheta.
Prin eticheta vom întelege un nume urmat de caracterul ":". Etichetele sunt locale unei functii.
Instructiunea goto are urmatorul format:
goto eticheta;
Efectul:
se realizeaza saltul la instructiunea prefixata de eticheta al carei nume se afla scris dupa cuvântul cheie goto.
5.13. APELUL sI REVENIREA DINTR-O FUNCŢIE
5.13.1. Apelul unei functii
În limbajul C functiile sunt de doua tipuri:
functii care returneaza o valoare la revenirea din ele;
functii care nu returneaza nici o valoare la revenirea din ele.
O functie care nu returneaza nici o valoare la revenirea din ea se apeleaza printr-o instructiune de apel. Ea are urmatorul format:
nume (lista_parametrilor_efectivi); (*)
unde:
nume este numele functiei;
lista_parametrilor_efectivi este fie vida, fie se compune din una sau mai multe expresii separate prin virgula.
Instructiunea de apel este un caz particular al instructiunii expresie. Parametrii efectivi (de la apel) trebuie sa corespunda cu cei formali (de la definirea functiei) prin ordine, tip si numar.
În cazul în care o functie returneaza o valoare, ea poate fi apelata fie printr-o instructiune de apel, fie sub forma unui operand al unei expresii.
Observatii:
1o. Daca nu dorim sa utilizam valoarea returnata de functia respectiva, apelul se face printr-o instructiune de apel.
2o. Daca dorim sa utilizam valoarea returnata de functie, vom folosi apelul functiei drept operand într-o expresie, operandul având formatul (*).
Exemple de apeluri de functii folosite pâna acum sunt apelurile functiilor standard printf, scanf, getchar si putchar. Functiile printf si putchar au fost apelate prin instructiuni de apel, valorile returnate de ele nefiind utilizate. În schimb functiile scanf si getchar au fost apelate în ambele moduri, atât prin instructiuni de apel, cât si ca operanzi în diferite expresii.
5.13.2. Prototipul unei functii
O functie poate fi apelata daca ea este definita în fisierul sursa înainte de a fi apelata. Dupa cum am prezentat în lectia 1 nu întotdeauna este posibil acest lucru si în astfel de cazuri apelul functiei trebuie sa fie precedat de prototipul ei.
Prototipul unei functii are ca scop sa informeze compilatorul despre:
tipul valorii returnate de functie;
tipurile parametrilor.
În felul acesta, la apelul unei functii, compilatorul poate face teste cu privire la tipul expresiilor care reprezinta parametrii efectivi, precum si unele conversii necesare asupra valorii returnate de functie.
Observatii:
1o. Tipurile parametrilor pot sa lipseasca. În acest caz, compilatorul nu controleaza tipurile parametrilor efectivi, singura informatie continuta de prototip fiind tipul valorii returnate de functia respectiva.
2o. Absenta atât a prototipului unei functii, cât si a definitiei functiei înainte de a fi apelata este posibila; în acest caz se presupune ca functia returneaza o valoare de tip int.
3o. În practica se recomanda utilizarea prototipurilor pentru toate functiile înainte de a fi apelate. În acest scop, ele vor fi scrise la începutul fisierelor sursa.
Formatele posibile ale unui prototip sunt:
formatul 1: tip nume (lista_declaratiilor_de_parametri);
formatul 2: tip nume (lista_ tipurilor_parametrilor);
formatul 3: tip nume (void);
formatul 4: tip nume ();
Formatul 2 este cel mai utilizat. Formatul 3 se poate folosi pentru orice functie care nu are parametri. Formatul 4 se poate folosi pentru orice functie la al carei apel nu se doresc teste referitoare la tipul parametrilor efectivi.
Functiile din biblioteca standard a limbajului C au prototipurile definite în fisierele de tip .h.
5.13.3. Apel prin valoare si apel prin referinta
La apelul unei functii, fiecarui parametru formal i se atribuie valoarea parametrului efectiv care-i corespunde. Deci, la apelul unei functii se transfera valorile parametrilor efectivi. Din aceasta cauza se spune ca apelul este prin valoare (call by value). În anumite limbaje de programare, la apel nu se transfera valorile parametrilor efectivi ci adresele acestora. În acest caz se spune ca apelul este prin referinta (call by refference).
Între cele doua tipuri de apeluri exista o diferenta esentiala si anume: în cazul apelului prin valoare functia apelata nu poate modifica parametrii efectivi din functia apelanta, neavând acces la ei. În cazul apelului prin referinta, functia apelata, dispunând de adresele parametrilor efectivi, îi poate modifica.
În limbajul C apelul se realizeaza implicit prin valoare. În cazul ca un parametru este numele unui tablou atunci transferul se realizeaza prin referinta deoarece numele unui tablou este un pointer si contine adresa primului element al tabloului. Transferul prin referinta se realizeaza cu ajutorul variabilelor de tip pointer si cu ajutorul operatorului de adresa (&).
5.13.4. Revenirea dintr-o functie
Revenirea dintr-o functie se poate face în doua moduri:
la întâlnirea instructiunii return;
dupa executia ultimei sale instructiuni, adica a instructiunii care precede acolada închisa ce termina corpul functiei respective.
Instructiunea return are doua formate:
return; sau return expresie;
Primul format se utilizeaza când functia nu returneaza o valoare, iar cel de-al doilea când functia returneaza o valoare. În acest ultim caz, functia returneaza valoarea expresiei specificate.
Observatie:
1o. Când revenirea se face dupa executia ultimei instructiuni a functiei nu se returneaza o valoare; revenirea în acest caz, se face ca si cum acolada închisa de la sfârsitul corpului functiei ar fi precedata de instructiunea return.
Exemplu: vom da un exemplu de apel al functiei care determina radacina patratica dintr-un numar nenegativ.
#include<stdio.h>
double radacina_2 (double) // prototipul functiei
void main (void) // functia principala care citeste d
// si afiseaza radacina patrata din d
while (y >= EPS);
return x2;
Observatie:
1o. Limbajul C dispune de o biblioteca matematica în care sunt incluse o serie de functii pentru calculul valorilor functiilor elementare. Exista o functie numita sqrt (cu prototipul double sqrt (double);). Fisierul care contine biblioteca matematica se numeste math.h si trebuie inclus în fisierul sursa de lucru daca se doreste utilizarea functiilor definite în el.
|