Sunteți pe pagina 1din 10

PROGRAMAREA CALCULATOARELOR

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 *);

2. Pointeri la funcii ca argumente n alte funcii


n continuare s presupunem c o funcie f are ca parametru o funcie g. Dac se scrie: f(g); nseamn c lui f i se transmite un pointer la funcia g. Dac prototipul funciei g este tip_g g(lista_g); atunci prototipul funciei f n care g este parametru va fi tip_f f(tip_g (*g)(lista_g)); sau tip_f f(tip_g (*)(lista_g)); iar definiia funciei f va fi tip_f f(tip_g (*p)(lista_g)) { .}
4

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:

f : [a, b] R n n diviziuni, astfel c mrimea unei diviziuni va fi:

(1)

Pentru calculul integralei se va folosi metoda trapezelor. Intervalul [a,b] va fi mprit

dx =

ba n
i = 1,K, n 1

(2)

Punctele xi se vor calcula cu expresia:


x i = a + i dx = x i 1 + dx cu

(3)

iar aria corespunztoare unei diviziuni (aria parial) este:

Ai =

f (x i ) + f (x i +1 ) (x i +1 x i ) 2

(4)

Integrala se obine ca sum a ariilor pariale:

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

Ce semnific urmtoarele declaraii?


int f1(int a, double (*f)(float *b)); int f2(int a, double f(char c), int (*g)(int d, int *e)); float *f3(double *a, int * (*f)(double)); double (*fc)(int a, float ff(void)); int *(*fd)(float *a, double *f(int *d));

3. Tablouri de pointeri la funcii


Ca orice tip de date pointerii la funcii se pot grupa n tablouri. Exemplu: avem un tablou de pointeri la funcii care primesc ca parametru un double i returneaz un double: double (*fm[10])(double); Pentru citirea acestor declaraii folosim aa-numite regul de citire dreapta-stnga: se pornete de la numele variabilei fm, n dreapta avem [10] (deci este un;tablou, aici de 10 elemente); mergem n stnga: avem * (tablou de 10 elemente pointeri); din nou n dreapta ( (tablou de 10 elemente pointeri la funcii); din nou stnga double (tablou de 10 pointeri la funcii care returneaz un double); la dreapta double lista de argumente a funciilor. n concluzie avem un tablou de 10 elemente, fiecare din ele este un pointer la o funcie care returneaz un double i primete ca parametru o dat de tip double.
6

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; }

4. Pointeri la funcii ca membri n structuri


Pointerii la funcii pot apare i ca membri n structuri de date. Aceast construcie poate fi utilizat pentru construirea unor meniuri, de exemplu. Fie tipul de dat FM sinonim pentru un pointer la funcie care primete un double i returneaz un double:
typedef double (*FM)(double x);

O structur care are n componen acest pointer se declar astfel:


7

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} };

Definiia funciei pentru construirea meniului este:


void meniu(struct functii tab[], int nf, char *msg) { int i; clrscr(); puts(msg); for(i=0; i<nf; i++) printf("\t%d - %s\n",i+1, tab[i].nume); printf("\t0 - exit\n"); printf("\t >> "); }

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

S se calculeze urmtoarele integrale:

1
2

sin( x + 3x)dx
2
2 x

(x + 4 x + e )dx
0

Indicaie:

Se va folosi pentru calculul integralei funcia integralaTrapez prezentat la curs.


Problema nr. 3

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

- norma infinit - norma 1


2

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

Se reia Problema nr. 4 folosind funcia de bibliotec qsort.

10

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