Pointe sur le code, pas sur la data
Un pointeur de fonction est une variable qui stocke l'adresse mémoire d'une fonction. Comme un pointeur classique pointe vers une donnée, un pointeur de fonction pointe vers du code exécutable.
Dans un jeu RPG, tu as un raccourci de compétence (la touche Q, W, E, R). Ce raccourci ne contient pas la compétence — il pointe vers elle. Tu peux changer quel sort est assigné à chaque touche en cours de partie. C'est exactement ce que fait un pointeur de fonction : la touche (le pointeur) peut pointer vers Fireball, puis vers Heal, selon les besoins.
TEXT (code segment) — là où le code compilé réside en mémoire virtuelle.Quand ton programme tourne, la mémoire virtuelle est découpée en segments. Voyons où se trouvent les fonctions :
fptr++) sur un pointeur de fonction. Ce n'est pas un tableau de données !Un pointeur de fonction contient l'adresse de la première instruction de la fonction en mémoire virtuelle. Il encode aussi la signature de la fonction (type de retour + types des paramètres) — c'est ce que vérifie le compilateur.
/* Une fonction normale en mémoire */
int add(int a, int b) { return a + b; }
/* → stockée à une adresse ex: 0x004006a0 dans le segment TEXT */
int main(void)
{
/* Déclaration : fptr est un pointeur vers une fonction
qui prend (int, int) et retourne int */
int (*fptr)(int, int);
fptr = &add; /* fptr vaut maintenant 0x004006a0 */
fptr = add; /* identique — le nom de fonction = son adresse */
printf("Adresse: %p\n", (void*)fptr); /* affiche l'adresse réelle */
printf("Adresse de add: %p\n", (void*)add); /* même valeur ! */
return (0);
}
Le pointeur de fonction, c'est comme l'adresse d'une salle de jeu dans une ville (la mémoire). La salle existe à une adresse fixe. Quand tu donnes cette adresse à quelqu'un, il peut s'y rendre et jouer (appeler la fonction). Lui donner une adresse différente (une autre salle) change ce qu'il va faire.
Identifie le type de retour et les types des paramètres.
int add(int a, int b);
/* retourne int, prend (int, int) */
int (*fptr)(int, int);
/* ↑ type retour ↑ nom du pointeur ↑ params */
fptr = add; /* assigner (avec ou sans &) */
fptr(10, 5); /* appeler — forme moderne */
(*fptr)(10, 5); /* appeler — forme classique, équivalent */
int (*fptr)(int, int); /* ✅ pointeur vers fonction retournant int */
int *fptr (int, int); /* ❌ FAUX : fonction retournant int* */
| Syntaxe | Ce que ça fait | Exemple |
|---|---|---|
| type (*ptr)(params) | Déclare un pointeur de fonction | int (*f)(int, int); |
| ptr = func; | Assigne une fonction au pointeur | f = add; |
| ptr = &func; | Même chose, style explicite | f = &add; |
| ptr(args) | Appelle la fonction (forme moderne) | f(10, 5); |
| (*ptr)(args) | Appelle la fonction (forme classique) | (*f)(10, 5); |
| type (*arr[])(params) | Tableau de pointeurs de fonctions | int (*ops[])(int,int); |
| func(ptr) | Passer un fptr en argument (callback) | exec(10, 5, add); |
Déclarer, assigner, et appeler un pointeur de fonction simple.
#include <stdio.h>
int multiply(int a, int b)
{
return (a * b);
}
int main(void)
{
int (*fptr)(int, int); /* déclaration */
fptr = multiply; /* assignation */
printf("%d\n", fptr(6, 7)); /* appel → 42 */
return (0);
}
Passer un pointeur de fonction en argument — le pattern callback. Utilisé partout en C.
#include <stdio.h>
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
/* La fonction accepte un pointeur de fonction comme 3ème arg */
void calc(int a, int b, int (*op)(int, int))
{
printf("Résultat : %d\n", op(a, b));
}
int main(void)
{
calc(10, 5, add); /* → 15 */
calc(10, 5, subtract); /* → 5 */
return (0);
}
qsort() de la stdlib : elle prend un pointeur de fonction de comparaison en argument !Tableau de function pointers + utilisation dans une struct — pattern proche des projets Holberton.
#include <stdio.h>
#include <string.h>
/* Tableau de pointeurs de fonctions avec leur nom */
typedef struct {
char *name;
int (*f)(int, int);
} op_t;
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
/* Cherche l'opération par son nom et l'exécute */
int exec_op(char *name, int a, int b)
{
op_t ops[] = {
{"add", add},
{"sub", sub},
{"mul", mul},
{NULL, NULL} /* sentinelle */
};
int i = 0;
while (ops[i].name)
{
if (strcmp(ops[i].name, name) == 0)
return (ops[i].f(a, b)); /* appel via le tableau */
i++;
}
return (-1);
}
int main(void)
{
printf("add: %d\n", exec_op("add", 10, 5)); /* 15 */
printf("mul: %d\n", exec_op("mul", 6, 7)); /* 42 */
return (0);
}
/* Oublier les parenthèses */
int *fptr(int, int);
/* → fonction retournant int* */
/* Mauvaise signature */
int (*fptr)(int);
fptr = add; /* add prend 2 args ! */
/* Arithmétique invalide */
fptr++; /* UNDEFINED BEHAVIOR */
/* Appel sans assignation */
int (*f)(int, int);
f(1, 2); /* f pointe vers NULL/garbage */
/* Parenthèses autour du nom */
int (*fptr)(int, int);
/* → pointeur vers fonction */
/* Signature identique */
int (*fptr)(int, int);
fptr = add; /* ✅ add(int, int) */
/* Pas d'arithmétique */
fptr = sub; /* changer = réassigner */
/* Toujours initialiser */
int (*f)(int, int) = add;
f(1, 2); /* ✅ */
Le type de retour ET tous les types de paramètres doivent être identiques entre le pointeur et la fonction cible. Le compilateur le vérifie.
fptr = add et fptr = &add sont strictement équivalents. Le nom d'une fonction se comporte comme un pointeur constant vers elle.
Contrairement aux pointeurs de données, on ne peut pas faire fptr++ ou fptr + 2. Une fonction n'est pas un tableau.
Si un pointeur de fonction est NULL et que tu l'appelles, c'est un segfault garanti. Teste avec if (fptr != NULL).
Pour les déclarations répétées ou complexes, crée un alias de type propre (voir section Tips Holberton ci-dessous).
# Compilation standard Holberton
gcc -Wall -Werror -Wextra -pedantic -std=gnu89 main.c -o prog
# Vérifier les warnings sur les signatures incompatibles
gcc -Wall -Wstrict-prototypes main.c -o prog
# Valgrind — pas de leak sur les function pointers eux-mêmes
# (ils vivent sur la stack, mais vérifie la mémoire autour)
valgrind --leak-check=full ./prog
malloc comme argument via callback, pense à free() !/* Sans typedef — dur à lire */
int (*fptr)(int, int);
int (*fptr2)(int, int);
/* Avec typedef — propre Betty-style */
typedef int (*op_func)(int, int);
op_func fptr;
op_func fptr2;
op_func ops[10];
/* ✅ Betty-friendly */
typedef int (*op_t)(int, int);
void exec(int a, int b, op_t op)
{
if (op == NULL)
return;
printf("%d\n", op(a, b));
}
Pas directement, mais les function pointers préparent le terrain pour comprendre les callbacks.
Table de fonction pointers associant chaque specifier (%d, %s, %c...) à sa fonction d'impression. Pattern classique Holberton !
typedef struct {
char *specifier;
int (*f)(va_list);
} spec_t;
Dispatch des builtins (cd, exit, env...) via un tableau de function pointers.
La fonction de tri standard prend un comparateur en callback : void qsort(void*, size_t, size_t, int(*)(const void*, const void*)).
/* MÉMOIRE VIRTUELLE FUNCTION POINTER
┌──────────────────┐
│ STACK │ int (*fptr)(int, int) ← variable sur la stack
│ [fptr] ───────────────────────────────────────┐
├──────────────────┤ │
│ HEAP │ │ contient l'adresse
├──────────────────┤ │
│ DATA / BSS │ │
├──────────────────┤ ▼
│ TEXT (code) │ 0x004006a0: int add(int a, int b) { return a+b; }
│ │ ▲
└──────────────────┘ └── fptr pointe ICI
*/
/* Déclaration */ int (*fptr)(int, int);
/* Assignation */ fptr = add;
/* Appel */ fptr(10, 5); /* → 15 */
/* NULL check */ if (fptr) fptr(10, 5);
/* Tableau */ int (*ops[3])(int, int) = {add, sub, mul};
/* Typedef propre */ typedef int (*op_t)(int, int);