Functiile sint elementele de baza ale unui program scris in C;
orice program, de orice dimensiune, consta din una sau mai multe functii, care
specifica operatiile care trebuie efectuate. c9m10mv
O functie ofera un mod convenabil de incapsulare a anumitor calcule intr-o
cutie neagra care poate fi utilizata apoi fara a avea grija continutului ei.
Functiile sint intr-adevar singurul mod de a face fata complexitatii
programelor mari. Functiile permit desfacerea programelor mari in unele
mici si dau utilizatorului posibilitatea de a dezvolta programe, folosind ceea
ce altii au facut deja in loc sa o ia de la inceput.
Limbajul C a fost conceput sa permita definirea de functii eficiente si usor
de minuit. In general e bine sa concepem programe constituite din
mai multe functii mici decit din putine functii de dimensiuni mari. Un
program poate fi impartit in mai multe fisiere sursa in mod
convenabil, iar fisierele sursa pot fi compilate separat.
Mai precis, un program C consta dintr-o secventa de definitii externe. Acestea
sint definitii de functii si de date. Sintaxa definitiilor externe este
aceeasi ca si a tuturor declaratiilor.
7.1. Definitia functiilor
Sintaxa definitiei unei functii este urmatoarea:
Definitia-functiei: tip-functie?opt? nume-functie(lista-parametri) corp-functie
Lista-parametri: declaratie-parametru declaratie-parametru, lista-parametri
Corpul-functiei: instructiune-compusa
Dintre specificatorii de clasa de memorie numai extern si static sint
admisi. Un declarator de functie este similar cu un declarator pentru „functie
care returneaza ...” cu exceptia ca el listeaza parametrii formali ai
functiei care se defineste.
Dupa cum se observa, diferitele parti pot sa lipseasca; o functie minima este: dummy() AS functia care nu face nimic. Aceasta functie poate fi utila in programe,
tinind locul unei alte functii in dezvoltarea ulterioara a programului.
Numele functiei poate fi precedat de un tip, daca functia returneaza altceva
decit o valoare intreaga (vezi sectiunea 7.2).
Numele functiei poate fi de asemenea precedat de clasa de memorie extern sau
static. Daca nici un specificator de clasa de memorie nu este prezent, atunci
functia este automat declarata externa, deoarece in C nu se admite ca
o functie sa fie definita in interiorul altei functii.
Declaratia unei functii se face implicit la aparitia ei. Se interpreteaza ca
functie orice identificator nedeclarat anterior si urmat de o paranteza ’(’.
Pentru a evita surprize neplacute datorita neconcordantei dintre tipurile de
parametri aceasta practica nu este recomandata.
Dam mai jos un exemplu de functie complet definita:
int max(int a, int b, int c) A int m; m = (a>b) ? a : b; return (m>c) ? m : c;
S
Aceasta functie determina maximul dintre 3 numere date:
? int este un specificator de tip care indica faptul ca functia max returneaza
o valoare intreaga;
? max(int a, int b, int c) este declaratia functiei si a parametrilor formali;
? A . . . S este corpul functiei.
Sa ilustram mecanismul definirii unei functii scriind functia putere power(m,n)
care ridica un intreg m la o putere intreaga pozitiva n. Astfel
valoarea lui power(2,5) este 32. Aceasta functie desigur nu este completa, deoarece
calculeaza numai puteri pozitive de intregi mici.
Prezentam mai jos functia power si un program principal care o apeleaza, pentru
a putea vedea si structura unui program.
power(int x, int n) A int i, p; p = 1; for (i=1; i<=n; ++i) p = p * x; return p;
S
main() A int i; for (i=0; i<10; ++i) printf(" %d %d\n", i, power(2,i), power(-3,i));
S
Functia power este apelata in programul principal de doua ori. Fiecare
apel transmite functiei doua argumente.
Rezultatul este afisat de functia de biblioteca printf (vezi capitolul 11).
Numele functiei este urmat obligatoriu de paranteze, chiar daca lista parametrilor
este vida.
In general numele functiei este ales dupa dorinta utilizatorului. Totusi
in fiecare program trebuie sa existe o functie cu numele impus main; orice
program isi incepe executia cu functia main. Celelalte functii sint
apelate din interiorul functiei main. Unele dintre functiile apelate sint
definite in acelasi program, altele sint continute intr-o
biblioteca de functii.
Comunicarea intre functii se face prin argumente si valori returnate de
functii. Comunicarea intre functii poate fi facuta si prin intermediul
variabilelor externe.
O functie poate fi declarata si static. In acest caz ea poate fi apelata
numai din fisierul unde a fost definita.
Functiile in C pot fi folosite recursiv, deci o functie se poate apela
pe ea insasi fie direct, fie indirect.
In general insa recursivitatea nu face economie de memorie, deoarece
trebuie mentinuta o stiva cu valorile de prelucrat.
7.2. Apelul functiilor
In limbajul C orice functie este apelata prin numele ei, urmat de lista
reala a argumentelor, inchisa intre paranteze.
Daca intr-o expresie numele functiei nu este urmat imediat de o paranteza
’(’, adica functia nu apare pe pozitia de apel de functie atunci
nu se realizeaza imediat apelul functiei respective ci se genereaza un pointer
la functie (vezi sectiunea 9.11).
7.3. Revenirea din functii
Revenirea dintr-o functie se face prin intermediul unei instructiuni return.
Valoarea pe care o functie o calculeaza poate fi returnata prin instructiunea
return, care dupa cum am vazut are doua formate: return; return expresie;
Ca argument poate aparea orice expresie admisa in C. Functia returneaza
valoarea acestei expresii functiei apelante.
O functie nu returneaza in mod obligatoriu o valoare. O instructiune return,
fara expresie ca parametru, cauzeaza numai transferul controlului functiei apelante
nu si o valoare utila.
La rindul sau functia apelanta poate ignora valoarea returnata.
De obicei o functie returneaza o valoare de tip intreg. Daca se doreste
ca functia sa returneze un alt tip, atunci numele tipului trebuie sa preceada
numele functiei, iar programul trebuie sa contina o declaratie a acestei functii
atit in fisierul in care functia este definita cit si
in fisierul unde functia este apelata.
Pentru a evita orice confuzie se recomanda ca tipul valorii returnate de functie
sa fie intotdeauna precizat, iar daca dorim in mod expres ca functia
sa nu returneze o valoare sa folosim tipul void.
De exemplu, functia atof(s) din biblioteca asociata compilatorului converteste
sirul s de cifre in valoarea sa in dubla precizie. Vom declara functia
sub forma: double atof(char sai); sau impreuna cu alte variabile de tip double: double sum, atof(char sai);
Functiile nu pot returna masive, structuri, reuniuni sau functii.
Daca o functie returneaza o valoare de tip char, nu este nevoie de nici o declaratie
de tip din cauza conversiilor implicite. Totdeauna tipul char este convertit
la int in expresii.
7.4. Argumentele functiei si transmiterea parametrilor
O metoda de a comunica datele intre functii este prin argumente. Parantezele
mici care urmeaza dupa numele functiei inchid lista argumentelor.
In limbajul C argumentele functiilor sint transmise prin valoare.
Aceasta inseamna ca in C functia apelata primeste valorile argumentelor
sale intr-o copie particulara de variabile temporare (in realitate
pe stiva).
Functia apelata nu poate altera decit variabilele sale particulare, adica
copiile temporare.
Apelul prin valoare este o posibilitate, dar nu si o obligativitate. Apelul
prin valoare conduce la programe mai compacte cu mai putine variabile externe,
deoarece argumentele pot fi tratate ca variabile locale, si care pot fi modificate
convenabil in rutina apelata.
Ca exemplu, prezentam o versiune a functiei putere care face uz de acest fapt:
power(int x, int n) A int p; for (p=1; n>0; --n) p = p * x; return p;
S
Argumentul n este utilizat ca o variabila temporara si este decrementat pina
devine zero; astfel nu este nevoie de inca o variabila i. Orice operatii
s-ar face asupra lui n in interiorul functiei, ele nu au efect asupra
argumentului pentru care functia a fost apelata.
Daca totusi se doreste alterarea efectiva a unui argument al functiei apelante,
acest lucru se realizeaza cu ajutorul pointerilor sau a variabilelor declarate
externe.
In cazul pointerilor, functia apelanta trebuie sa furnizeze adresa variabilei
de modificat (tehnic printr-un pointer la aceasta variabila), iar functia apelata
trebuie sa declare argumentul corespunzator ca fiind un pointer. Referirea la
variabila de modificat se face prin adresare indirecta (vezi capitolul 9).
Printre argumentele functiei pot aparea si nume de masive. In acest caz
valoarea transmisa functiei este in realitate adresa de inceput
a masivului (elementele masivului nu sint copiate). Prin indexarea acestei
valori functia poate avea acces si poate modifica orice element din masiv.
7.5. Functii cu numar variabil de parametri
Pentru functiile cu numar variabil de parametri standardul ANSI defineste
o constructie speciala ... , numita elipsa. Acesta este de exemplu cazul functiilor
de citire si scriere cu format (familiile ...scanf si ...printf - a se vedea
capitolul 11). In acest caz, parametrii ficsi sint verificati la
compilare, iar cei variabili sint transmisi ca si cind nu ar exista
prototip de functie.
Pentru simplitatea expunerii am pastrat conventiile definite de standardele
mai vechi ale limbajului, pastrate si de standardul ANSI C. O functie poate
fi declarata fara nici un parametru, compilatorul deducind de aici ca
functia respectiva poate avea oriciti parametri de orice tip. Nu se recomanda
totusi o asemenea practica deoarece este posibil sa se creeze confuzii care
conduc la erori in executia programelor, erori care se corecteaza foarte
greu in cazul programelor mari.
7.6. Exemple de functii si programe
1. Acest exemplu este un program de cautare si imprimare a tuturor liniilor
dintr-un text care contin o anumita configuratie de caractere, de exemplu cuvintul
englezesc "the". Structura de baza a acestui program este:
while (mai exista linii sursa) if (linia contine configuratia de caractere) imprima linia
In scopul scrierii acestui program vom scrie trei functii. Functia getline
citeste o linie din textul sursa si returneaza lungimea ei. Aceasta functie
are doua argumente. Argumentul s indica numele masivului unde se va citi linia,
iar argumentul lim reprezinta lungimea maxima a unei linii care poate fi citita.
Functia index cauta configuratia de caractere dorita in interiorul liniei
si furnizeaza pozitia sau indexul in sirul s, unde incepe sirul
t, sau ?1 daca linia nu contine sirul t. Argumentele functiei sint s care
reprezinta adresa sirului de studiat si t care reprezinta configuratia cautata.
Functia care realizeaza afisarea este printf din biblioteca.
Programul care realizeaza structura de mai sus este:
#define MAXLINE 1000
#define EOF -1
getline(char sai, int lim) A
/* citeste o linie in s; returneaza lungimea */ int c, i; i = 0;
while (--lim>0 && (c=getchar())!= EOF && c!='\n') sai++i=c; if (c=='\n') sai++i=c; saii='\0'; return i;
S
index(char sai, char tai) A
/* returneaza pozitia din sirul s unde incepe sirul t, sau ?1 */ int i,j,k; for (i=0; saii!='\0'; i++) A for (j=i, k=0; taki!='\0' && saji==taki; j++, k++)
; if (taki=='\0') return i;
S return -1;
S
main() A
/* imprima toate liniile care contin cuvintul „the” */ char line aMAXLINEi;
while (getline(line, MAXLINE)>0) if (index(line,"the")>=0) printf("%s",line);
S
2. Functia atof converteste un sir de cifre din formatul ASCII intr-un
numar flotant in precizie dubla.
double atof(char sai) A
/* converteste sirul s in dubla precizie */ double val, power; int i, sign; for (i=0; saii==' ' || saii=='\n' || saii=='\t'; i++) ; /* sare peste spatii albe */ sign = 1; if (saii=='+' || saii=='-') sign = (sai++i=='+') ? 1 : -1; for (val=0; saii>='0' && saii<='9'; i++) val = 10 * val + saii - '0'; if (saii== '.') /* punct zecimal */ i++; for (power=1; saii>='0' && saii<='9'; i++)
A val = 10 * val +saii - '0': power *= 10;
S return sign * val / power;
S
3. Functia atoi converteste un sir de cifre in echivalentul lor numeric
intreg.
atoi(char sai) A /* converteste sirul s la un intreg */ int i,n; n = 0; for (i=0; saii>='0' && saii<='9'; i++) n = 10 * n +saii - '0'; return n;
S
4. Functia lower converteste literele mari din setul de caractere ASCII in
litere mici. Daca lower primeste un caracter care nu este o litera mare atunci
il returneaza neschimbat.
lower(int c) A if (c>='A' && c<='Z') return c + 'a' - 'A'; else return c;
S
5. Functia binary realizeaza cautarea valorii x intr-un masiv sortat
v, care are n elemente.
binary(int x, int vai, int n) A
/* cauta x in v0, v1, ..., vn-1 */ int low, high, mid; low = 0; high = n - 1;
while (low<=high) A mid = (low + high) / 2; if (x < vamidi) high = mid - 1; else if (x > vamidi) low = mid + 1; else /* s-a gasit o intrare */ return(mid);
S return -1;
S
Functia returneaza pozitia lui x (un numar intre 0 si n?1), daca x apare
in v sau ?1 altfel.
Exemplul ilustreaza o decizie tripla, daca x este mai mic, mai mare sau egal
cu elementul din mijlocul sirului vamidi.