Sunteți pe pagina 1din 12

PRELEGERE XII

PROGRAMAREA CALCULATOARELOR ŞI LIMBAJE DE PROGRAMARE

Noţiunea de funcţie
Funcţia este elementul structural de bază în componenţa unui program din clasa limbajelor C. O
descriere sumară a funcţiei a fost deja prezentată în secţiunea Structura unui program C.
În majoritatea limbajelor procedurale pe lîngă subprograme de tip funcţie se utilizează şi subprograme
de tip procedură. Ceea ce le diferenţiază sînt modul de apelare şi numărul rezultatelor întoarse
programului principal. Funcţia se apelează prin numele ei şi returnează cel mult un rezultat, iar
procedura se activează printr-o instrucţiune de apel de procedură şi furnizează un număr nedeterminat
de rezultate. În limbajele de tip C se foloseşte doar noţiunea de funcţie, care nu impune restricţii asupra
numărului de rezultate furnizate, iar apelarea ei poate să constituie o instrucţiune de sine stătătoare sau
să se facă prin intermediul unor instrucţiuni de ieşire sau de tip expresie.
Astfel, noţiunea de funcţie permite, pe de o parte ca rezolvarea oricărei probleme, indiferent de
gradul ei de dificultate, să se facă prin descompunerea acesteia într-un număr arbitrar de subprobleme
simple, uşor de modificat, reprezentabile prin funcţii, iar pe de alta, presupune participarea unui număr
mai mare de programatori, care are ca efect scurtarea timpului de soluţionare a problemei (adică,
presupune lucrul în echipă). De asemenea, se adaugă creşterea facilităţii de lizibilitate şi urmărirea mai
uşoară a unui program vast, posibilitatea de codificare în limbaje de programare diferite a funcţiilor
care intervin în componenţa unui program şi nu în ultimul rînd, testarea şi depanarea funcţiilor pe
sisteme de calcul diferite, dar compatibile.
Funcţiile pot apela la rîndul alte funcţii sau se pot autoapela (funcţii recursive) şi astfel este posibil să
se ajungă la programe în care modulul principal (main()) să conţină doar cîteva linii de apeluri de
funcţii. De asemenea, am văzut că modulul principal main() este singura funcţie care controlează toate
celelalte funcţii componente. Aceasta este apelată şi activată la începutul lansării în execuţie a
programului.
Legătura între funcţiile apelante şi cele apelate se păstrează prin intermediul parametrilor
actuali (efectivi) şi respectiv, formali (fictivi), între ei existînd concordanţe de număr, de tip, de
dimensiune şi de loc. Parametrii actuali permit furnizarea de date spre funcţia apelată, iar rezultatul
returnat modulului apelant poate fi ignorat sau utilizat în cele ce urmează.
Definirea şi declararea funcţiilor, apelarea unei funcţii, mecanismul de transmitere al parametrilor,
funcţii recursive, pointerii de funcţii, funcţii cu liste variabile de parametri şi funcţii predefinite
constituie conţinutul secţiunilor din acest capitol.

I. Definirea unei funcţii


Pentru a accentua că noţiunea de funcţie este instrumentul de bază în rezolvarea problemelor prin
intermediul acestui limbaj şi că nu are importanţă gradul de complexitate al problemei de rezolvat, spre
deosebire de alte secţiuni, aici vom începe cu descrierea unei funcţii care rezolvă o problemă extrem de
simplă, şi anume, aflarea sumei z = x+y, unde x, y ∈ R. Ulterior, vom prezenta detalii tehnice cu
privire la structura generală a funcţiilor şi progresiv vom creşte gradul de dificultate al problemelor
rezolvate, pentru care vom utiliza mai multe funcţii în rezolvarea acestora sau vom construi pentru
aceeaşi funcţie mai multe versiuni.
Funcţia care rezolvă suma a dou numere reale are următoarea structură:
/* Functia z */
float z(float x, float y)
{ z = x+y; }.
Funcţia 12.1.1
Numele funcţiei este z, iar parametrii x şi y pot fi substituiţi de oricare două numere reale.
Sintaxa generală de definire a structurii unei funcţii urmăreşte şablonul folosit în limbajele de
nivel înalt ( Fortran, Pascal, etc.), şi anume :
[tip_rezultat] nume_funcţie([declaraţii_parametri_formali])
{ Directive preprocesor
Declaraţii_locale
Lista de instrucţiunii
}
După cum ştim deja, recunoaşterea funcţiei se face printr-un nume simbolic fixat de către
programator, iar la fiecare activare a ei se furnizează un număr nedeterminat de rezultate. Tipul
rezultatului este opţional, iar cel implicit este int (întreg). Dintre tipurile prezentate pînă acum doar
tipurile tablou şi structură nu sînt permise. Atributele static şi extern se asociază implicit
indentificatorului nume_funcţie.
Dacă funcţia nu furnizează nici un rezultat, atunci tip_rezultat este void (vid, lipsă). În acest
caz, prin intermediul instrucţiunii return, se poate transmite funcţiei apelante o valoare convenţională
care să reflecte informaţii asupra modului în care s-au desfăşurat prelucrările din structura funcţiei.
Dacă funcţia returnează un rezultat, atunci numele funcţiei este folosit, în general, pentru identificarea
rezultatului, în caz contrar se utililizează instrucţiunea return.
Declaraţiile de parametri formali cuprind un număr arbitrar de variabile, unde fiecare se asociază cu
un tip de dată oarecare, chiar dacă sînt mai multe variabile cu tipuri egale. Virgula este delimitatorul în
interiorul listei de declaraţii. Parantezele simple ce succed numele funcţiei sînt obligatorii atît la
definirea cît şi la activarea ei, chiar dacă lista de parametri este vidă, de exemplu main().
În situaţia parametrilor formali de tip tablou se recomandă ca dimensiunea tablourilor să se specifice
separat. Pentru parametrii de tip tablou unidimensional nu trebuie specificat numărul de elemente, iar
în cazul parametrilor de tip tablou multidimensional nu este necesar precizarea primei dimensiunii. În
faza de compilare a programului nu se foloseşte această informaţie, dar execuţia corectă a prelucrărilor
din structura funcţiei, în anumite context de rezolvare, necesită cunoaşterea ei. O altă recomandare se
face în cazul parametrilor formali de tip structură, utilizarea specificatorului typedef în definirea
structurii permite declararea adecvată a acestora.
Acoladele delimitează corpul funcţiei indiferent de componenţa ei. Acestea se scriu în linii
diferite şi se aliniază din aceeaşi coloană, dacă este posibil. Asocierea lor făcîndu-se după principiul
ultima deschisă - cu prima închisă. Acoladele împreună cu introducerea de rînduri libere, scot în
evidenţă mai bine corpul funcţiei respective şi sporesc gradul de urmărire a programului.
Declaraţiile de variabile din structura funcţiei au caracter local. Se reaminteşte că, declaraţiile locale au
timpul de viaţă limitat la durata de execuţie funcţiei care le conţine. Cu alte cuvinte, se pot utiliza
indentificatori egali în definiţia unor funcţii diferite sau în modulul principal de program fără să se
producă ambiguităţi.
Definţiile de funcţii nu pot fi imbricate, adică în interiorul unei funcţii nu se permit definiţiile altor
funcţii. De asemenea, nu este permis saltul forţat prin goto în exteriorul funcţiei (în altă funcţie sau în
modulul main()). În schimb, nu există restricţie de utilizare a lui goto în blocul de definiţie al funcţiei.
În situaţia cînd funcţia furnizează un rezultat, se recomandă coincidenţa între identificatorul
rezultatului şi numele simbolic al funcţiei. În caz contrar, terminarea execuţiei unei funcţii şi

2
întoarcerea în funcţia apelantă se determină prin instrucţiunea return [nume_rezultat];, unde
parantezele pătrate exprimă opţionaliatea argumentului nume_rezultat. Limbajul C nu permite ca
ultima instrucţiune a blocului de definţie să fie o instrucţiune expresie, unde numelui funcţiei i se
atribuie valoarea ce urmează să se returneze funcţiei apelante. Deci, absenţa instrucţiunii return
provoacă încheierea execuţiei funcţiei cu returnarea unui rezultat nu totdeauna corect. Rezultatul
furnizat este cel obţinut în ultimul pas de iterare într-o structura repetitivă (situaţie inoportună) sau prin
rezolvarea secvenţială a unor expresii de atribuire.
Funcţia suma determină suma cuburilor primelor n numere naturale. Aceasta furnizează un
rezultat întreg, care se păstrează în numele funcţiei (funcţia 12.1.2) sau în variabila s, cu simbolizarea
returnării rezultatului prin intermediul lui return, în funcţia 12.1.3. În primul program se procedează ca
şi cum ar exista un return înainte de acolada de închidere a blocului funcţiei.
/* Definire functie suma */ /*Definire functie suma*/
int suma(int n )
{ int i; suma = 0;
for (i=0; i<=n; ++i) suma += i*i*i;}
Funcţia 12.1.2
int suma(int n )
{ int i, s = 0;
for (i=0; i<=n; ++i) s += i*i*i;
return s; }
Funcţia 12.1.3

3
În programul prezentat în secţiunea următoare, funcţia suma va constitui obiectul
unor apeluri de funcţie.

II. Apelul de funcţie


Pentru apelarea unei funcţii nu există o instrucţiune marcată printr-un cuvînt cheie. Spre
deosebire de limbajele clasice, unde apelarea unei funcţii se face prin numele ei de
identificare şi constituie argument în instrucţiuni de atribuire sau de scriere, apelul de
funcţii C poate avea şi statut de instrucţiune de sine stătătoare. Ca poziţie apelul unei
funcţii determină o expresie de atribuire, fixează un argument în funcţia de ieşire sau
constituie o instrucţiune de apel (de exemplu, cum este apelul de procedură în Pascal).
Sintaxa generală a unui apel de funcţie este:
nume_funcţie(listă_parametri_actuali);
unde, parametrii actuali descriu, în fapt, contextul în care se activează funcţia.
Ca parametru actual poate fi: o constantă, o variabilă, o expresie, un tablou, un element
de tablou, o structură, un cîmp al unei variabile de tip structură, o funcţie sau un pointer
de funcţie. Numele unei funcţii se consideră un pointer către funcţia respectivă. În
consecinţă, o funcţie capătă atributele unui pointer şi poate fi folsită ca parametru actual
în apeluri de funcţii.
Dacă la definirea funcţiei absentează lista parametrilor fictivi, atunci lista
parametrilor actuali este vidă. În această situaţie, prezenţa parantezelor este obligatorie.
Activarea unei funcţii declanşează următoarele:
- substiturea parametrilor formali, atunci cînd există, prin parametri actuali.
Corespondenţa de număr, de tip, de dimensiune şi de loc între cele două liste de parametri
este obligatorie. Orice neconcordanţă este sancţionată prin eroare de sintaxă;
- funcţia preia datele furnizate de programul apelant prin intermediul parametrilor
actuali;
- se execută blocul funcţiei. La datele furnizate din programul apelant se adaugă,
atunci cînd există şi cele evidenţiate în declaraţiile din structura funcţiei;
- se revine la funcţia apelantă în locul în care s-a făcut apelul. Valoarea returnată,
atunci cînd există, poate fi folosită în mod nemijlocit ca operand într-o expresie, ca dată
de ieşire sau ca parametru actual într-un apel de funcţie. Prin instrucţiunea return se pot
genera diferite puncte de întoarcere în programul apelant, fără ca funcţia să se execute în
totalitate.
Apelul de funcţie iese în evidenţă doar atunci cînd este prezentă lista parametrilor
formali sau cînd argumentul instrucţiunii return nu este absent. În rest, funcţia are o
comportare constantă. De exemplu, funcţia afisare tipăreşte valoarea 100 indiferent de
poziţia apelului:
void afisare()
{ printf(“\n %d “, 100); }
Funcţia 12.2.1
Pentru a evita respingerea unui apel de funcţie C++ sau unele efecte nedorite la un apel
de funcţie C, pentru orice funcţie trebuie să se precizeze, la începutul fişierului sursă,
prototipul funcţiei. Mai multe despre acesta în secţiunea următoare.
Programul 12.2.1 cuprinde două activări ale funcţiei suma descrisă în secţiunea
anterioară. La primul apel, valoarea parametrului actual ( notat tot cu n) se introduce de
la tastatura, iar la cel de al doilea se utilizează o constantă. Apelurile funcţiei suma apar
ca argumente în printf(). Definiţia funcţiei suma s-a plasat înaintea modulului main().
/* Apelare functie suma */
#include <stdio.h>
#include <conio.h>
int suma(int n )
{ int i, suma = 0;
for ( i = 0; i <= n; ++i) suma += i*i*i; }
void main(void)
{ int n;
printf("\n Introduceti n = "); scanf("%d", &n);
printf("\n Suma_1 = %d Suma-2 = %d", suma(n), suma(5));
getch(); }
Introduceti n = 3
Suma_1 = 36 Suma_2 = 225
Programul 12.2.1

III. Declararea de funcţii


Definiţia unei funcţii, care se aranjează de obicei la sfîrşitul modulului principal main(),
este însoţită de o declarare a ei printr-un prototip situat la începutul programului.
Prototipul funcţiei cuprinde numai linia de definiţie a funcţiei în care se precizează tipul
rezultatului, numele funcţiei şi declaraţia de tip a fiecărui parametru formal. Amplasarea
la începutul programului doar a antetului funcţiei permite compilatorului verificarea
corectitudinii apelurilor ulterioare ale funcţiei:
[tip_rezultat] nume_funcţie([tip_1, tip2, …, tip_n]).
Numele simbolice de recunoaştere ale parametrilor nu sînt importante, dar precizarea lor
permite o mai bună documentare a programului şi o mai uşoară identificare a lor de către
compilator la apariţia unei erori de sintaxă:
[tip_rezultat] nume_funcţie([declaraţii_parametri_formali]).
Folosirea prototipurilor este cu caracter obligatoriu în C++, dar numai
recomandată în C. Apariţia unui apel de funcţie în absenţa unei definiţii sau declarări
prealabile, generează returnarea unui rezultat de tip întreg, fapt care are uneori
consecinţe nedorite.
Domeniul de vizibilitate al declarării unei funcţii poate avea caracter global sau
local, după cum este plasată aceasta în fişierul sursă, fie în afara oricărei funcţii, fie în
blocul unei funcţii.
Pentru funcţiile standard din biblotecile de tip C referitoare la un anumit tip de
aplicaţie, prototipurile, declarările de date şi macrodefiniţiile (macrou-rile) necesare sînt
organizate în fişiere antet (header) cu extensia .h şi înregistrate în directorul include. De
aceea, utilizarea unei funcţii standard într-un program necesită includerea, la începutul
acestuia prin directiva #include, a fişierului antet asociat.
Prin intermediul meniului help se poate afla fişierul antet care conţine definiţia unei
anumite funcţii standard. De asemenea, programatorul poate să-şi organizeze propriile
fişiere antet cu o structură asemănătoare celor standard.
Preluăm funcţia suma din secţiunea 4.1.1 cu unele schimbări. Scrierea
rezultatului se face în blocul funcţiei. Se introduce prototipul funcţiei, deoarece
amplasamentul ei este după modulul principal. Acesta se semnalează printr-o linie
comentariu. Apelurile funcţiei suma sînt linii distincte în funcţia apelantă şi au statutul de
instrucţiune.
/* Definirea functiei suma */
#include <stdio.h>
#include <conio.h>
int suma(int ); // Prototipul functiei suma
void main(void)
{ int n;
printf("\n Introduceti n = "); scanf("%d", &n);
suma(n); suma(5); getch(); }
int suma(int n )
{ int i, suma = 0;
for ( i = 0; i <= n; ++i) suma += i*i*i;
printf(“\n Suma(%d) = %d”,n, suma(n)); }
Programul 12.3.1

IV. Transmiterea parametrilor


Mecanismul de substituire sau înlocuire a parametrilor formali prin parametri actuali se
numeşte transmiterea sau transferul parametrilor. Transmiterea parametrilor se face
prin valoare sau prin adresă.

a) Transmiterea prin valoare


Transmiterea prin valoare permite furnizarea de valori unei funcţii din exteriorul ei.
Aceasta se produce ca şi cum substituirea parametrului formal prin cel actual ar fi o
instrucţiune expresie de tipul: parametru_formal = parametru_actual.
Valoarea parametrului actual se memorează în spaţiul de memorie (în stivă)
afectat parametrului formal. Ulterior, această valoarea poate fi schimbată prin
instrucţiunile care intervin în structura funcţiei. Valoarea parametrului actual nu suferă
nici o modificare pe tot parcursul procesului de transfer sau pe durata execuţiei funcţiei
apelată.
Transmiterea parametrilor este însoţită de eventuale conversii de tip, conform cu
declaraţiile de tip din lista parametrilor formali inclusă în definiţia funcţiei.
Dacă prototipul funcţiei precede apelul funcţiei, atunci, în mod similar, se aplică regulile,
utilizate în execuţia unei instrucţiuni expresie, privitoare la conversia tipului valorii
parametrului actual la cel al parametrului formal (nu deranjează absenţa numelui
acestuia). De asemenea, sînt posibile conversii de tip explicite prin intermediul
operatorului cast. Dacă o conversie nu este posibilă se afişează eroare.
Dacă prototipul funcţiei absentează sau nu apare înaintea unui apel de funcţie, atunci
mecanismul de transfer al parametrilor este urmat de o extindere automată a unor tipuri
de date.
Mecanismul de transmitere prin valoare nu se aplică, în forma prezentată mai sus,
parametrilor de tip tablou sau de tip structură. În situaţia aceasta se transmite funcţiei
adresa parametrului de tip tablou sau de tip structură. Motivaţia acestui lucru ar fi că, pe
de o parte tabloul şi structura conţin, în general, un număr mare de componente şi
înregistrarea acestora în zona de memorie afectată parametrului formal corespunzător ar
fi neeficientă, iar pe de alta, sintaxa limbajului nu permite, cum ar fi în Pascal,
modalitatea de a referi conţinutul acestora, în ansamblu, prin numele lor.
Două transmiteri prin valoare sînt descrie în programul 12.4.1.1, unde valoarea
returnată în programul apelant depinde de relaţia dintre a şi b. Valorile variabilelor a şi b
nu se modifică în urma activării funcţiei tr_val. În acest program prezenţa prototipului
funcţiei, descris prin linia
int tr_val(int , int );
este obligatorie deoarece definiţia funcţiei succede modulul main().
/* Functii - transmitere prin valoare */
#include <stdio.h>
#include <conio.h>
int tr_val(int , int );
void main(void)
{ int a, b;
clrscr();
printf("\n Introduceti a = "); scanf("%d", &a);
printf("\n Introduceti b = "); scanf("%d", &b);
printf("tr_val(a, b) : %d a=%d b=%d ", tr_val(a, b), a, b);
getch(); }
int tr_val(int x, int y)
{ if (x < y) x = x + 3; else y = y + 5; }
Introduceti a = 2
Introduceti b = 3
tr_val(a, b) : 5 a = 2 b = 3
Programul 12.4.1.1

b) Transmiterea prin adresă


Mecanismul de transmitere prin adresă permite modificarea directă a valorii unui
parametru actual de către operaţiile conţinute de funcţia apelată. Parametrii formali se
declară de tip pointer, iar în apelul funcţiei se precizează adresele parametrilor actuali
corespunzători.
Funcţia tr_adr din structura programului 12.4.2.1 este cea utilizată în programul
12.4.1.1 pentru descrierea transmiterii prin valoare. Declararea variabilelor a şi b în
antetul funcţiei de tip pointer permite un transfer prin adresă. În funcţie de valoarea
condiţiei, se returnează programului apelant valoarea modificată, fie a lui x, fie a lui y.
/* Functii - transmitere prin adresa */
#include <stdio.h>
#include <conio.h>
void tr_adr(int*, int* );
void main(void)
{ int a, b;
clrscr();
printf("\n Introduceti a= "); scanf("%d", &a);
printf("\n Introduceti b= "); scanf("%d", &b);
tr_adr(&a, &b);
printf(" a=%d b=%d ", a, b);
getch(); }
void tr_adr(int *x, int *y)
{ if (*x < *y) *x = *x + 3; else *y = *y + 5; }
Introduceti a = 3
Introduceti b = 2
a=3b=7
Programul 12.4.2.1
Am văzut deja la activarea funcţiei că transmiterea tablourilor unei funcţii se face
numai prin adresă. Precizarea numelui unui tablou este echivalentă, în fapt, cu precizarea
adresei primului octet a zonei de memorie alocată tabloului. Adică, în cazul parametrilor
de tip tablou trebuie să se ştie dimensiunile acestuia. Se poate omite specificarea
dimensiunii în cazul tabloului unidimensional sau a primei dimensiuni pentru cel
multidimensional. Referirea unui element de tablou se face prin intermediul indicilor,
care precizează poziţia elementului în structura tabloului.
Rezolvarea produsului a două matrice, cu ajutorul funcţiilor, se reia în programul
12.4.2.2. Funcţiile cit_mat, scr_mat şi prod_mat descriu operaţiile de citire a unei
matrice, de tipărire a unei matrice şi respectiv de aflare a produsului a două matrice
înlănţuite. Pentru orice apel de funcţie, transmiterea dimensiunilor se face prin valoare,
iar a matricilor, indiferent de operaţia suferită, prin adresă.
/* Utilizare functie - produsul a doua matrice */
#include <stdio.h>
#include <conio.h>
#define M 10
#define N 10
#define P 10
void cit_mat(float, int, int );
void prod_mat(int, int, int);
void scr_mat(int, int);
int m, n, p;
void main(void)
{ float a[M][N], b[N][P], c[M][P];
clrscr();
printf("\n Introduceti m = "); scanf("%d", m);
printf("\n Introduceti n = "); scanf("%d",n );
printf("\n Introduceti p = "); scanf("%p", p);
cit_mat(a[m][n], m ,n);
cit_mat(b[n][p], n, p);
/* Se calculeaza matricea c */
prod_mat(m, n, p);
scr_mat(m, p); getch(); }
/* Functia de citire matrice */
void cit_mat(float a[M][N],int m,int n)
{ int i, j;
char s[1];
printf("\n Introduceti numele matricei "); scanf("%s",
&s[1]);
printf(" : \n");
for ( i = 0; i<m; ++i)
for ( j = 0; j<n; ++j)
{ printf("%c( %d,%d)=", s[1], i, j); scanf("%f", &a[i][j]);
}
return; }
/* Functia de calcul al produsului */
void prod_mat(int m,int n, int p)
{ int i, j, k;
float c[M][P], a[M][N], b[N][P];
for ( i = 0; i<m; ++i)
for ( j = 0; j<p; ++j)
{ c[i][j] = 0;
for ( k = 0; k<n; ++k) c[i][j] += a[i][k]*b[k][j]; }
return; }
/* Functia de scriere matrice */
void scr_mat(int m, int p)
{ int i, j;
float c[M][P];
clrscr();
printf("\n\n Matricea rezultat: ");
for ( i = 0; i<m;++i)
for ( j = 0; j<p; ++j)
printf("\nc( %d,%d)=%6.2f", i, j, c[i][j]);
return; }
Programul 12.4.2.2
În limbajul C++, un tablou nu poate fi referit în totalitate prin numele său, dar
numele său echivalează cu adresa zonei de memorie alocată tabloului respectiv.

c) Transmiterea prin referinţă


Declararea unui indentificator ca referinţă de indentificatori, cu un tip de dată precizat,
permite simplificarea mecanismului de transfer al parametrilor şi al rezultatului unei
funcţii prin eliminarea parametrilor formali de tip pointer. Procesul de transfer este
rezolvat de către compilator la vedere chiar şi cînd există diferenţe de interpretare sau de
dimensiune în corespondenţa dintre parametrii actuali şi cei formali. Acest tip de
transfer nu este acceptat în C.
Sintaxa de definire a unei referinţe de indentificatori este următoarea:
tip_de_dată nume_variabilă = constantă;
tip_de_dată& nume_referinţă = nume_variabilă;. Numele de referinţă
reprezintă un alt indentificator de recunoaştere pentru nume_variabilă. Iniţializarea
referinţei cu adresa variabilei sau a unei constante este obligatorie.
Numai pentru declararea referinţei de indentificatori se poate utiliza, în mod
echivalent, una din următoarele două sintaxe de definire:
tip_de_dată &nume_referinţă = nume_variabilă; sau
tip_de_dată & nume_referinţă = nume_variabilă;.
Numele de referinţă poate înlocui nume_variabilă în orice tip de operaţie. De
exemplu, în secvenţa următoare pentru variabila x se introduce un sinonim rx, care poate
substitui pe x în orice apariţie existentă a lui x:
float x = 10.;
float & rx = x; //Echivalent cu float &rx = x; sau float & rx = x;
rx = 15.5; //Echivalent cu x = 15.5;
Nu sînt permise referinţe de cîmpuri de biţi, de tablouri de referinţe, de pointeri de
referinţe şi de variabile de tip referinţă.
La apelarea unei funcţii cu tansmitere prin referinţă a parametrilor actuali, funcţia
modifică direct valorilor parametrilor actuali şi efecutează corespunzător, acolo unde este
cazul, în mod implicit, conversia acestor valori. Apelurile de funcţii sînt rezolvate şi în
situaţia conversiilor degradante (de exemplu, float -> int), cu emiterea unor avertismente,
fără modificarea valorilor parametrilor actuali. În ultima situaţie, transmiterea prin
referinţă a parametrilor actuali presupune crearea în stivă a unor obiecte temporare
corespunzătoare parametrilor actuali, la fel ca la transmiterea prin valoare. Prelucrările
funcţii vor afecta numai obiectele temporare.
Dacă valorile parametrilor actuali nu trebuie modificate în procesul de transfer
prin referinţă, atunci se recomandă utilizarea modificatorului de acces const, care va
impune compilatorului sarcina de a verifica şi detecta operaţiile care pot să schimbe,
direct sau indirect, valorile respective.
De exemplu, în secvenţa următoare numai declarările care conţin modificatorul de
acces const sînt acceptate: const int &k = 5; // corect
int &k = 2; // incorect
float x;
const int &k = x; // corect
int &k = x; // incorect
Programul analizat în ultimile două secţiuni este reluat şi completat cu un transfer
prin referinţă în care intervin două conversii după care precizia tipului de dată are de
suferit.
/* Functii - transmitere prin referinta */
#include <stdio.h>
#include <conio.h>
void tr_ref(int*, int* );
void main(void)
{ int a, b;
float c = 1.75, g = 10.25;
clrscr();
printf("\n Introduceti a= "); scanf("%d", &a);
printf("\n Introduceti b= "); scanf("%d", &b);
tr_ref(a, b); //Apelul functiei tr_ref fara conversii de tip
printf("\n a=%d b=%d ", a, b);
tr_ref(c,g);//Apelul functiei tr_ref cu doua conversii degradante
printf("\n c=%4.2f g=%4.2f ", c, g);
getch(); }
void tr_ref(int& x, int& y)
{ if (x < y) x = x + 3; else y = y + 5;
printf("\n x=%d y=%d ", x, y); }
Introduceti a = 3
Introduceti b = 2
x=3y=7
a=3b=7
x = 5 y = 10
c = 1.75 g = 10.25
Programul 12.4.3.1
Deoarece apelul funcţiei tr_ref(c,g); necesită conversii de genul float -> int la
transmiterea parametrilor actuali prin referinţă, compilatorul crează două variabile
temporare pentru parametrii formali respectivi, iar valorile parametrilor actuali c şi g
rămîn nemodificate. Tot prin crearea unor obiecte temporare se relizează transmiterea
prin referinţă a parametrilor actuali cu dimensiuni largi. Operaţia de creare a unei copii
pentru astfel de valori ar fi mare consumatoare de spţiu în stivă şi ar îngreuna viteza de
execuţie a programului.
La transmiterea prin referinţă a rezultatului unei funcţii, linia de definiţie a
funcţiei are următorul format general:
tip_de_dată& nume_funcţie(listă_parametri_formali).
Nu se admite tipul automatic pentru variabila a cărei adresă se returnează funcţiei
apelante, deoarece durata ei de viaţă se limitează la blocul funcţiei apelate. Adresa
returnată va corespunde unei zone de memorie care nu mai este alocată, prin dispariţia
variabilei automate la încheierea execuţiei funcţiei apelate.
Pentru transferul structurilor ca parametru şi rezultat pentru o funcţie se
recomandă utilizarea pointerilor.

V. Instrucţiunea return
Instrucţiunea return ca şi continue, a fost preluată din limbajul Fortran. Aceasta permite
terminarea activităţii unei funcţii, într-un moment fixat al rezolvării cu sau fără returnarea
unei valori în funcţia care a generat apelul. Deci, în unele situaţii instrucţiunea return
produce un salt necondiţionat dintr-un anume punct al unei funcţii în programul apelant,
cu furnizarea cel mult a unei valori de un tip compatibil cu cel precizat în prototipul şi
antetul definirii ei, fără ca funcţia să se rezolve în totalitate. Aşa se întîmplă chiar dacă
tipul funcţiei este void (adică, nu se întoarce nici un rezultat). De asemenea, instrucţiunea
return este necesară şi în situaţia în care funcţia se execută în întregime, dar rezultatul
furnizat este memorat într-o variabilă diferită de cea care fixează numele simbolic al
funcţiei.
Formatul general al acestei instrucţiunii este: return expresie;.
Expresia este opţională. În cazul cînd este prezentă, valoarea ei va fi furnizată funcţiei
apelante, eventual cu aplicarea unei conversii implicite la tipul rezultatului ( ca la orice
atribuire).
Mai multe utilizări ale instrucţiunii return sînt prevăzute în definirea funcţiei
fact(), care poate fi găsită, chiar în două abordări, aproape în orice lucrare de programare
procedurală. Această funcţie determină n!, unde n este un număr natural. Calculul
factorialului se face pe baza relaţiei n! = 1x2x3x … xn.
Funcţia fact este apelată de mai multe ori în printf():
- de trei ori în determinarea, în ordine, a valorilor n!, k! şi (n-k)! ;
- de trei ori în expresia Cnk = n!/(k!(n-k)!).
Prototipul funcţiei int fact(int); precede definirea şi apelurile funcţiei.
Dacă n = 0, atunci funcţia returnează 1; altfel, valoarea expresiei n!.
/* Functia factorial */
#include <stdio.h>
#include <conio.h>
int fact(int);
void main(void)
{ int n, k;
clrscr();
printf("\n Introduceti n = "); scanf("%d", &n);
printf("\n Introduceti k = "); scanf("%d", &k);
printf("%d\! = %d %d\! = %d %d\! = %d \n
Combinari de %d luate cite %d : %d", n, fact(n), k,
fact(k), n-k, fact(n-k), n, k, fact(n)/(fact(k)*fact(n-k)));
getch(); }
int fact(int n)
{ int i, produs = 1;
if (n == 0) return 1;
{ for ( i = 1; i <= n; ++i) produs *= i; }
return produs;
}
Introduceti n = 5
Introduceti k = 2
n! = 120 k! =2 (n-k)! = 6
Combinari de 5 luate cite 2 : 10
Programul 12.5.1
În continuare se prezintă modalitatea de codificare a funcţiei fact(), unde prin intermediul
parametrului n se controlează atît structura for cît şi aflarea factorialului.
int fact(int n)
{ int i, produs = 1;
for (; n>1; --n) produs *= n;
return produs; }
Funcţia 12.5.1

S-ar putea să vă placă și