Pointeri la funcții în C

Funcții C89

O variabilă care ține adresa unei funcții: o poți schimba, pune în tablouri sau trimite altor funcții (callback).

Sintaxă
tip_returnat (*nume)(tipuri_parametri);
int (*op)(int, int) = aduna;
int r = op(2, 3);
typedef int (*Operatie)(int, int);

Ce face Pointeri la funcții

Numele unei funcții, folosit fără paranteze, este adresa ei. Un pointer la funcție ține o astfel de adresă și poate apela funcția: op(2, 3) sau (*op)(2, 3), ambele forme sunt corecte.

Sintaxa declarației pune numele între paranteze cu steluță: int (*op)(int, int). Fără paranteze, int *op(int, int) ar fi o funcție care returnează int *. Cu typedef, tipul primește un nume scurt și codul devine mai ușor de citit.

Pointerii la funcții permit codul generic: qsort primește funcția de comparare, un meniu poate ține acțiunile într-un tablou, o bibliotecă poate apela funcția ta când se întâmplă ceva (callback).

Exemple

Exemplul 1: Un calculator cu tablou de funcții

#include <stdio.h>

int aduna(int a, int b) { return a + b; }
int scade(int a, int b) { return a - b; }
int inmulteste(int a, int b) { return a * b; }

int main(void) {
    int (*operatii[])(int, int) = {aduna, scade, inmulteste};
    const char *simboluri = "+-*";
    for (int i = 0; i < 3; i++) {
        printf("12 %c 4 = %d\n", simboluri[i], operatii[i](12, 4));
    }
    return 0;
}

Rezultat

12 + 4 = 16
12 - 4 = 8
12 * 4 = 48

Exemplul 2: Callback cu typedef

aplica nu știe ce transformare face; primește funcția ca parametru și o aplică fiecărui preț.

#include <stdio.h>

typedef double (*Transformare)(double);

double cu_tva(double p) { return p * 1.21; }
double reducere_10(double p) { return p * 0.9; }

void aplica(double *preturi, int n, Transformare f) {
    for (int i = 0; i < n; i++) {
        preturi[i] = f(preturi[i]);
    }
}

int main(void) {
    double preturi[] = {100, 250, 40};
    aplica(preturi, 3, cu_tva);
    aplica(preturi, 3, reducere_10);
    for (int i = 0; i < 3; i++) {
        printf("%.2f lei\n", preturi[i]);
    }
    return 0;
}

Rezultat

108.90 lei
272.25 lei
43.56 lei

Exemplul 3: Alegerea funcției la rulare

Pointerul primește o funcție sau alta după o condiție; apelul rămâne același.

#include <stdio.h>

void salut_ro(const char *nume) { printf("Bună, %s!\n", nume); }
void salut_en(const char *nume) { printf("Hello, %s!\n", nume); }

int main(void) {
    const char *limba = "ro";
    void (*salut)(const char *) = (limba[0] == 'r') ? salut_ro : salut_en;
    salut("Ana");
    salut = salut_en;
    salut("Ana");
    return 0;
}

Rezultat

Bună, Ana!
Hello, Ana!

Greșeli frecvente

  • Scrii int *op(int, int); când voiai un pointer la funcție. Asta declară o funcție care returnează int *; corect: int (*op)(int, int);.
  • Apelezi un pointer la funcție neinițializat sau nul. Programul se oprește; verifică if (op) înainte, dacă pointerul poate lipsi.
  • Faci cast între pointeri la funcții cu semnături diferite și apelezi prin ei. Apelul printr-un tip nepotrivit este comportament nedefinit.

Comenzi înrudite

  • qsort: Sortează un tablou de orice tip, folosind o funcție de comparare scrisă de tine.
  • bsearch: Caută rapid un element într-un tablou sortat (căutare binară) și întoarce un pointer la el sau NULL.
  • typedef: Dă un nume nou, mai scurt sau mai clar, unui tip existent: o structură, un pointer sau un tip de funcție.
  • Funcții și prototipuri: Cum definești și declari funcții în C: tipul rezultatului, parametrii, prototipul și transmiterea argumentelor prin valoare.
  • exit, atexit și EXIT_SUCCESS: Oprește programul din orice funcție, cu un cod de ieșire; atexit înregistrează funcții de rulat la final.