Documente Academic
Documente Profesional
Documente Cultură
Lucrarea nr. 12
POINTERI LA FUNCII
1. Declararea unui pointer la o funcie
Pointerii folosii pn acum au fost pointeri la diferite tipuri de date, dar este posibil s avem i pointeri la funcii. Pointerii la funcii sunt folosii din aceleai motive ca i pointerii la date: atunci cnd se dorete un alt nivel de indirectare, cnd dorim ca aceeai secven de cod s apeleze funcii diferite depinznd de condiiile concrete ale programului. Ca i n cazul pointerilor la date, pentru utilizarea pointerilor la funcii trebuie s declarm o variabil care s conin un pointer la funcie. Un pointer la o funcie se declar astfel: tip (*pf)(tip1 p1, tip2 p2, ..., tipn pn); unde tip este tipul funciei (tipul valorii returnate de funcie) tip1 p1, tip2 p2, ..., tipn pn este lista parametrilor funciei care va fi accesat prin intermediul pointerului. Numele parametrilor, adic p1, p2, ..., pn pot lipsi. Exemplu, dac scriem int (*pfi)(float a, int b); se declar pfi ca fiind un pointer la o funcie care va returna un ntreg. Ca i n alte declaraii * indic faptul c avem un pointer, iar ( ) arat c avem de a face cu o funcie. Parantezele din (*pfi) sunt necesare deoarece i n declaraii exist o anumit preceden a operatorilor (o anumit ordine de evaluare interpretare) ca i n expresii i cnd ordinea implicit nu este cea dorit, trebuie s o schimbm folosind parantezele de explicitare. n declaraii, ( ) - operatori de funcie i [ ] - operatorii de indexare sunt mai prioritari dect * indicnd pointerii. Fr parantezele menionate, declaraia de mai sus arat astfel: int *pfi(float a, int b); i declar o funcie pfi care va returna un pointer la ntreg. Cu parantezele explicite, int (*pfi)() ne spune c pfi este mai nti un pointer, c acest pointer indic o funcie i mai apoi c funcia respectiv returneaz un ntreg. Pointerii la funcii se pot defini i ca noi tipuri de date prin utilizarea declaraiei de tip typedef. De exemplu, putem scrie typedef int (*FPTR)(float a, int b);
1
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
i identificatorul FPTR este un sinonim pentru tipul de dat pointer la o funcie care returneaz un ntreg, astfel nct declaraia FPTR pfi; este echivalent cu int (*pfi)(float a, int b); O dat declarat, unui pointer la funcie i se poate atribui valoarea adresei de nceput a funciei dorite. Dac avem prototipurile urmtoarelor funcii int f1(float a, int b); int f2(float a, int b); int f3(float a, int b); atunci putem scrie: pfi = &f1; sau if (conditie) pfi = &f2; else pfi = &f3; Bineneles, nu vom fi restrni la aceste dou forme, putem asigna pointeri la funcii n orice condiii dorim. Al doilea exemplu poate fi scris mai compact: pfi = conditie ? &f2 : &f3; n aceste exemple am folosit operatorul &, aa cum am fcut pn acum pentru a genera un pointer. Totui cnd generm pointeri la funcii, operatorul & este opional, deoarece atunci cnd menionm numele unei funcii fr s o apelm menionm de fapt adresa funciei respective, numele unei funcii fiind de fapt un pointer la funcia dat. Astfel se poate scrie: pfi = f1; sau if (conditie) pfi = f2; else pfi = f3; sau pfi = conditie ? f2 : f3;
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
Faptul c un pointer la o funcie este generat automat cnd o funcie apare ntr-o expresie, dar nu este apelat este asemntor, i de fapt este de legat de faptul c un pointer la primul element al unui vector este generat automat atunci cnd un vector apare ntr-o expresie. Avnd o variabil pointer la o funcie care conine adresa unei funcii, putem apela (folosi) funcia respectiv astfel: 1. scriem numele variabilei pointer la funcie pfi 2. se folosete operatorul * n fa pentru "a accesa coninutul acelui pointer" *pfi Aceast expresie reprezint tocmai funcia pe care dorim s o folosim. 3. adugm lista de parametri n paranteze mpreun cu un set de paranteze pentru a avea precedena dorit a operaiilor: (*pfi)(arg1, arg2) Formm astfel apelul la funcie. Parantezele din expresia (*pfi) au aceeai explicaie ca la declararea pointerului la o funcie. Dac scriem *pfi(arg1, arg2) interpretarea este: apeleaz funcia pfi (care va returna un pointer), transfer-i argumentele arg1 i arg2 i ia coninutul de la adresa indicat de variabila pointer la ntoarcere. Totui ceea ce dorim s facem este urmtorul lucru: ia coninutul lui pfi (care este un pointer la o funcie), apeleaz funcia spre care pointeaz, transmindu-i argumentele arg1 i arg2. Din nou, parantezele explicite schimb precedena implicit, astfel nct se aplic mai nti operatorul * i apoi se apeleaz funcia. Expresia pfi(arg1, arg2) este echivalent cu (*pfi)(arg1, arg2). Atunci cnd apelm o funcie indicat de un pointer la funcie, operatorul * este opional. Este recomandabil folosirea lui pentru a scoate n evidena faptul c folosim un pointer la funcie i nu funcia propriu-zis. Pentru fiecare funcie folosit trebuie s avem un prototip pentru a permite compilatorului s genereze corect codul pentru apelul funciilor i s verifice dac funcia este apelat cu numrul i tipul adecvat pentru argumente.
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
n general nu se va ti dect n momentul rulrii programului care este funcia pointat de pfi, astfel nct compilatorul nu poate verifica dac apelul s-a fcut corect. Pe de alt parte, atunci cnd am declarat un pointer la funcie a trebuit s declarm tipul valorii returnate de funcia respectiv. De asemenea, putem declara prototipul argumentelor folosite de funcie, adic putem scrie: int (*pfi)(float a, int b); Acum tim c pfi este un pointer la o funcie care accept dou argumente i care returneaz un ntreg. Avnd toate acestea specificate, compilatorul va putea s verifice corectitudinea anumitor apeluri pentru funcia respectiv. De exemplu, dac scriem: (*pfi)(1, 2, 3) compilatorul va semnaliza eroare, pentru c el tie c funcia nu poate accepta dect dou argumente. De asemenea, compilatorul va verifica dac funciile spre care pointeaz variabila pointer la funcie au lista de argumente i valoarea de retur n concordan cu declaraia pentru pointer. Deci pentru o variabil pointer la funcie trebuie s declar att tipul valorii returnate, ct i tipul argumentelor din lista de argumente. Alte exemple de construire a pointerilor la funcii: double (*a)(int); float (*b)(char *); int * (*f1)(double, int); double *(*f2)(char *, int , double *);
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
unde (*p) poate fi i (*g). Exemple: int g(double); double f(int (*)(double)); 2.1. Exemplu 2.1.1. Calculul ariei domeniului mrginit de graficul unei funcii Valoarea ariei domeniului mrginit de graficul unei funcii este valoarea integralei acelei funcii, valoare calculat lund ca limite capetele intervalului de reprezentare. Fie funcia, considerat fr bucle:
(1)
dx =
ba n
i = 1,K, n 1
(2)
(3)
Ai =
f (x i ) + f (x i +1 ) (x i +1 x i ) 2
(4)
I n = Ai =
i =0
n 1
b a f (a) + f (b ) n1 + f (x i ) n 2 i =1
(5)
Aceast modalitate de calcul a ariei este acelai, indiferent de expresia funciei f. Dac vom lua n considerare de fiecare dat forma funciei f va trebui s scriem o funcie care calculeaz integrala pentru fiecare funcie pe care trebuie s o utilizm. Pointerii la funcii ne permit s transmitem ca parametru funcia pentru care vrem s calculm integrala astfel nct vom lua n considerare urmtorul prototip:
double integralaTrapez(double a, double b, int n, double (*f)(double));
n care:
a, b reprezint capetele intervalului de integrare n numrul de diviziuni ale intervalului f pointer la o funcie care primete un parametru de tip double i returneaz o
valoare de tip double. Aceast funcie calculeaz valorile funciei pentru care vrem s
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
calculm integrala ntr-un punct specificat ca parametru. Funcia poate fi o funcie proprie sau o funcie din biblioteca matematic (de exemplu sin, sqrt, etc). Funcia integralaTrapez implementeaz formula (5) i are forma:
double integralaTrapez(double a, double b, int n, double (*f)(double)) { double val; double dx = (b-a)/n; double x; val = ((*f)(a) + (*f)(b))/2; for(x=a+dx; x<b; x = x+dx) val = val + (*f)(x); val = val * dx; return val; } // Valoarea integralei
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
Exemplu: s se scrie un program care tabeloeaz funciile trigonometrice sin, asin, cos, acos, tan, atan ntre 0 i 1 rad cu pasul de 0.05.
#include <stdio.h> #include <math.h> int main(void) { double (*fm[])(double x) = { sin, asin, cos, acos, tan, atan}; int i; double x; double dx = 0.05; int nf = sizeof(fm)/sizeof(fm[0]); puts("Tabelul"); for(x=0; x<=1; x+= dx) { printf("x = %5.3lf f= ", x); for(i=0; i<nf; i++) printf("%5.3lf ", (*fm[i])(x)); printf("\n"); } return 0; }
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
struct S1 { ................ /* declaraii pentru ali membri */ FM f; }; /* echivalent cu double (*f) (double x); */ ................ /* declaraii pentru ali membri */
Putem construi astfel o structur care s conin numele unei funcii i un pointer la funcia respectiv. Exemplu:
struct functii { char *nume; double (*f) (double x); };
n programul principal putem iniializa un tablou de astfel de structuri pentru a-l folosi la construirea unui meniu. Exemplu:
struct functii tab_f[] = { {"sinus", sinus}, {"cosinus", cos}, {"tangenta", tan} };
n aceast funcie tab[i].nume este numele prelucrrii (funciei) dorite, iar apelul acestei funcii este (*tab_f[i].fm)(x) (tab_f este tabloul de structuri definit mai sus).
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
TEMA
Problema nr. 1
Folosind tablouri de pointeri la funcii, s se scrie un program care tabeleaz funciile de bibliotec sinus, cosinus i tangent pentru valori cuprinse ntre 0 i cu un pas egal cu /20.
Problema nr. 2
1
2
sin( x + 3x)dx
2
2 x
(x + 4 x + e )dx
0
Indicaie:
S se declare o structur norme care conine ca membri un pointer la caracter i un pointer la o funcie care returneaz un double i primete ca parametri un pointer la double i un ntreg (dup modelul structurii functii dat mai sus). S se scrie un program care calculeaz, pentru un tablou unidimensional (vector) urmtoarele norme:
x
= max x i
x 1 = x1 + x 2 + ... + x n
x
2
x1 + x 2 + ... + x n
- norma 2
Programul definete i iniializeaz (dup modelul dat mai sus) un tablou de structuri
norme cu numele i funciile care calculeaz cele trei norme, afieaz un meniu care
folosete acest tablou de structuri, iar prelucrarea dorit se va face prin intermediul pointerilor la funcii. Se poate folosi, ca exemplu, funcia meniu dat mai sus.
Problema nr. 4
S se defineasc o funcie generic de ordonare a unui ir de date (metoda folosit este metoda bulelor). Funcia primete ca parametri adresa zonei de memorie unde se
9
PROGRAMAREA CALCULATOARELOR
Lucrarea nr. 12
gsete irul de date (un pointer generic), numrul de date care se ordoneaz, dimensiunea unui element din ir, precum i un pointer la o funcie care realizeaz compararea a dou elemente din ir. S se defineasc o funcie generic de interschimbare a dou elemente dintr-un ir de date (funcie ce poate fi folosit pe orice tip de date). Folosind aceste funcii s se scrie un program care face ordonarea unor date citite de la tastatur i afieaz irul ordonat pe monitor. Aceste date pot fi: un ir de numere reale n dubl precizie sau un text.
Atenie! Trebuie scris o singur funcie de ordonare (sortare) care va fi folosit
att pentru sortarea textului ct i a irului de date numerice i o singur funcie de interschimbare.
Problema nr. 5
10