DECLARAREA sI DEFINIREA FUNCŢIILOR
Pentru a putea fi utilizata într-un program C, o functie trebuie mai întâi declarata si apo definita. O functie se declara pentru ca tipul ei, precum si tipul si numarul argumentelor sale sa fie precizate în vederea utilizarii ei viitoare de functia main sau de alte functii. O functie se defineste specificând tipul, numele si argumentele sale, precum si corpul propriu-zis de declaratii si instructiuni ale functiei.
Argumentele unei functii precizate la definirea ei numesc parametrii formali, în timp ce cele precizate la apelul ei se numesc parametrii actuali.
Înainte de a preciza mai multe despre functii, sa analizam un exemplu complet de program C care apeleaza functii.
Exemplu 4.1.
# include "studio.h"
/* Declaratii de functii */
unsegned long cmmdc ( unsigned long m, unsigned long n);
unsigened long cmmmc (unsigned long m, unisigened long n);
/* Definirile de functii */
/* Functia (program) principala */
main ( )
}
unsigned long a = 0, b = 0, d, m;
printf ("Program pt. Calcul c.m.m.d.c. si c.m.m.c. \n", \"a numerelor a si b\n");
do
while (a < = 0 b < = 0);
d = cmmdc (a,b);
m = cmmmc (a,b);
printf ("c.m.m.d.c. (%1u, %1u) = % 1u \n", a, b, d,);
printf ("c.m.m.m.c. (%1u, %1u) = % 1u \n", a, b, m,);
/* Definirea functiei cmmdc */
unsigned long cmmdc (unsigned long m, unsigned long n);
/* Definirea functiei cmmmc */
unsigned long cmmc (unsigned long m, unsigned long n)
Liniile 3 si 4 de la începutul programului sunt declaratii de functii. Cele doua functii cmmdc si cmmmc folosite în program se declara precizându-se tipul functiilor (unsigned long) si tipul argumentelor lor (tot unsigned long). Aceste declaratii sunt numite în standardul Ansi, prototipuri de functii.
Apoi, începând cu linia main ( ) si pâna la linia cu acolada dreapta } inclusiv, urmeaza functia (program) principala. În aceasta functie în bucla do. while. se preiau de la tastatura cele doua date de intrare a si b.
Observati ca a <= 0 sau b <= 0, cererea de introducere date se reia. Dupa care urmeaza apelul celor doua functii:
d = cmmdc (a,b);
m = cmmmc (a,b);
Cei doi parametrii a si b ai functiilor (numiti la apelare parametrii actuali) sunt de tip unsigned long. Daca ei n-ar fi fost asa, atunci compilatorul ar fi generat secvente pentru conversia lor la tipul cerut de prototipul functiei.
La sfârsitul functiei main ( ), prin cele doua printf - uri se afiseaza rezultatele, adica cmmdc si cmmmc ai numerelor a si b introduse de utilizator.
Linia } de la sfârsitul functiei main constituie si sfârsitul executiei programului.
Urmeaza apoi definirea celor doua functii apelate în functia main ( ). Observati ca prima linie de la definirea functiilor este identica cu prototipul (declararea) functiilor, cu deosebirea ca nu mai are la sfârsit caracterul ";". Dupa aceasta prima linie urmeaza între acolade corpul efectiv al functiei care contine declaratii si instructiuni. Pentru calculul c.m.m.d.c. se utilizeaza "Algoritmul lui Euclid". Mai observati ca în cea de-a doua functie pentru calculul c.m.m.m.c. se apeleaza din nou functia cmmdc.
Am vrut sa ilustram ca o functie, dupa ce a fost declarata, poate fi apelata atât din functia principala, cât si din orice alta functie, de aceea am scris doua functii. Ar fi fost ceva mai eficient daca am fi scris o singura functie în care sa se calculeze atât c.m.m.d.c., cât si c.m.m.m.c.
Acum, dupa ce am analizat acest exemplu, sa precizam mai multe despre declaratii si definiri de functii.
O functie se declara astfel:
[tip] nume-functie ( lista-declaratori-parametri );
constructie numita în standardul Ansi, prototip de functie.
În aceasta constructie "tip" reprezinta tipul functiei, adica mai precis, tipul valorii întoarse de functie. Amintim ca o functie poate întoarce orice valoare, de orice tip, mai putin un tablou sau o alta functie. Daca "tip" lipseste, atunci se considera implicit ca tipul functiei este int.
Daca o functie nu întoarce nici o valoare, pentru a se specifica prototipul ei se foloseste cuvântul "void", astfel:
void nume - functie (lista - declaratori - parametri);
În ceea ce priveste parametrii unei functii, dupa nume - functie, între paranteze acestea se specifica prin "lista - declaratori - parametri". Lista contine declaratori pentru parametri separati prin virgule. Pentru fiecare parametru se declara tipul si eventual numele acestuia.
Declararea tipurilor parametrilor ofera avantajul ca permite compilatorului sa faca verificari privind numarul si tipul parametrilor, si în caz de necesitate sa faca, la apelul functiei, conversiile astfel încât apelul sa se execute corect si sa elimine o posibila eroare, de altfel destul de dificil de depistat si eliminat.
Astfel, în exemplul anterior, prototipul:
unsigned long cmmdc (unsigned long m, unsigned long n );
asigura doua tipuri de conversii:
- daca în momentul apelului parametrii actuali nu sunt unsigned long, se face conversia lor la acest tip.
De exemplu la apelul:
..
int i, j;
..
. cmmdc (i,j);
..
compilatorul genereaza secvente de conversie pentru i si j la tipul unsigned long.
- daca valoarea introdusa de functie nu este de tip unsigned long, atunci se face conversia expresiei de la instructiunea: return (expresie); la tipul declarat, unsigned long.
Numele parametrilor din prototipul unei functii (exemplu m si n din cmmdc) au domeniul de valabilitate doar în cadrul prototipului si ei pot fi omisi. Astfel prototipul:
unsigned long cmmdc (unsigned long, unsigned long);
este echivalent cu cel prezentat mai sus.
Daca o functie nu are parametrii, atunci se foloseste tot cuvântul void, astfel:
tip nume - functie (void);
iar daca nici nu întoarce vreo valoare, se foloseste:
void nume - functie (void);
Mai amintim ca este permisa si utilizarea functiilor cu un numar variabil de parametri. În prototipul functiei parametrii obligatorii se pun la început, iar pentru a se preciza ca dupa ei urmeaza un numar variabil de parametrii se foloseste constructia ".". De exemplu:
int sprint f (char * buffer, char * format, .);
prototip care precizeaza ca functia sprint f este de tip int si are cel putin doi parametrii de tip pointer la char.
În ceea ce priveste functiile standard, acestea au prototipul specificat în fisierele antet ("header"), de exemplu: stdio.h, conio.h, graphics.h, etc. si de aceea programatorul trebuie sa aiba grija sa includa aceste fisiere prin directive # include scrise la începutul programului.
O functie se defineste în doua moduri . Prima modalitate în care linia de definitie a functiei (prima linie) seamana cu prototipul functiei:
[tip] nume - functie (lista - definitii - parametri)
Precizarile de la prototipul unei functii referitoare la tip sunt variabile si aici, în schimb lista - definitii - parametri se deosebeste de lista - declaratii - parametri de la prototip prin aceea ca numele parametrilor, în acest caz parametrii formali, sunt obligatorii. De asemenea, prima linie din definitia unei functii, nu trebuie sa se termine prin ";".
Aceasta modalitate de definire a functiilor este folosita în toate exemplele din aceasta carte.
A doua modalitate, foarte asemanatoare cu functiile din limbajul FORTRAN este:
[tip] nume - functii (lista - nume - parametri)
definitii parametrii
Diferenta fata de prima modalitate este aceea ca între paranteze nu mai sunt specificate tipul si numele parametrilor, ci numai numele acestora. Tipul parametrilor e specificat sub linia de definitie si înainte de blocul ce contine corpul propriu-zis al functiei.
De exemplu functia cmmdc definita astfel ar fi:
unsigned long cmmdc (m,n)
unsigned long m, n;
return (n);
}
Corpul propriu-zis al unei functii este:
adica, o instructiune compusa. Toate declaratiile care se fac în interiorul corpului unei functii sunt valabile numai în functia respectiva, si ele sunt invizibile pentru celelalte functii ale programului. Un exemplu în acest sens îl constituie declaratia:
unsigned long r;
facuta în interiorul functiei cmmdc.
Corpul unei functii poate fi si vid. Astfel este corecta definitia:
nimic ( )
care reprezinta o functie care nu face nimic. Asemenea functii sunt utile atunci când dorim sa testam un program cu mai multe functii, dintre care numai o parte sunt scrise. Functiile care nu sunt gata, dar sunt apelate din functia principala main, se declara ca mai sus (cu corp vid) pentru a putea evita erorile de compilare si deci pentru a testa restul programului.
În legatura cu prototipul unei functii, mai trebuie totusi spus ca, în limbajul C (standardul Ansi), acesta poate sa lipseasca, cu conditia ca definitia functiei sa se faca înainte de apelul functiei, întrucât declaratia unei functii se face automat la definitia ei. Cu toate acestea, utilizarea de prototipuri este recomandata pentru a se face conversia parametrilor actuali la tipul celor formali, si nu în ultimul rând pentru autodocumentarea programului.
În schimb, absenta prototipului combinata cu utilizarea unei functii înainte de definitia ei, produce eroare de compilare daca nu este de tip int, deoarece asa este ea tratata de compilator, ca fiind implicit de tip int.
În acest ultim caz mai facem mentiunea ca daca o functie nu are prototip si este utilizata înainte de definirea ei, sau daca are un numar variabil de parametrii, atunci au loc automat numai conversiile:
- char si short catre int;
- unsigned char si unsigned short catre unsigned;
si nu toate conversiile parametrilor actuali catre tipul parametrilor formali ca atunci când functia ar avea un prototip.
Observatie
O aceeasi functie poate fi declarata de mai multe ori cu conditia ca declaratiile sa nu fie incompatibile.
Compilatorul poate detecta eventualele incompatibilitati dintre prototipul unei functii si definitia ei în ceea ce priveste tipul functiei si a argumentelor ei decât daca ambele sunt prezente în acelasi fisier sursa. Deci, ar trebui ca fiecare fisier sursa care concura la formarea unui program executabil sa aiba declaratiile de prototipuri (si eventualele declaratii extern). Acest lucru se realizeaza, dupa cum vom vedea mai departe, prin directiva # include, care permite sa includem în fiecare fisier sursa o aceeasi copie a declaratiilor necesare.
|