← HUB ✦ Holberton School — C Programming ✦

FUNCTION
POINTERS

Pointe sur le code, pas sur la data

C'EST QUOI UN POINTEUR DE FONCTION ?
🎯 DÉFINITION

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.

🕹️ ANALOGIE GAMING

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.

Un pointeur de fonction contient une adresse dans le segment TEXT (code segment) — là où le code compilé réside en mémoire virtuelle.
OÙ ÇA POINTE DANS LA MÉMOIRE VIRTUELLE ?
🗺️ LA CARTE DE LA MÉMOIRE VIRTUELLE

Quand ton programme tourne, la mémoire virtuelle est découpée en segments. Voyons où se trouvent les fonctions :

0xFFFFFFFF
Kernel Space (inaccessible)
↓ haute
STACK Variables locales, paramètres, adresses de retour
↕ variable
HEAP malloc() / free() — mémoire dynamique
↕ fixe
DATA / BSS Variables globales et statiques
↓ basse
TEXT / CODE ⬅ Les fonctions sont ici ! Le pointeur de fonction pointe vers ce segment.
0x00000000
NULL (adresse invalide)
Un pointeur de fonction stocke une adresse dans le segment TEXT. Ce segment est en lecture seule et non exécutable depuis le heap/stack — c'est une protection système.
Tu ne peux PAS faire d'arithmétique (fptr++) sur un pointeur de fonction. Ce n'est pas un tableau de données !
CE QUE CONTIENT EXACTEMENT UN POINTEUR DE FONCTION
🔬 ANATOMIE DU POINTEUR

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);
}
🕹️ ANALOGIE GAMING

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.

LA SYNTAXE — LE GUIDE DE SURVIE
📐 DÉCLARER UN POINTEUR DE FONCTION
Étape 1 — Regarder la signature de la fonction cible

Identifie le type de retour et les types des paramètres.

int add(int a, int b);
/* retourne int, prend (int, int) */
Étape 2 — Copier la signature, remplacer le nom par (*nom)
int (*fptr)(int, int);
/* ↑ type retour  ↑ nom du pointeur  ↑ params */
Étape 3 — Assigner et appeler
fptr = add;          /* assigner (avec ou sans &) */
fptr(10, 5);         /* appeler — forme moderne */
(*fptr)(10, 5);      /* appeler — forme classique, équivalent */
Piège : les parenthèses sont OBLIGATOIRES
int  (*fptr)(int, int); /* ✅ pointeur vers fonction retournant int */
int  *fptr (int, int);  /* ❌ FAUX : fonction retournant int* */
TABLEAU RÉCAP — SYNTAXES CLÉS
📋 CHEAT TABLE COMPLÈTE
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);
LES 3 BOSS — EXEMPLES PROGRESSIFS
🐣 BOSS 1 — APPEL BASIQUE
⚔️ LVL 1 — FACILE

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);
}
⚔️ BOSS 2 — CALLBACK FUNCTION
🔥 LVL 2 — INTERMÉDIAIRE

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);
}
C'est le cœur de qsort() de la stdlib : elle prend un pointeur de fonction de comparaison en argument !
💀 BOSS FINAL — TABLEAU DE POINTEURS + STRUCT
💀 LVL 3 — HOLBERTON STYLE

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);
}
ERREURS CLASSIQUES — ❌ vs ✅
❌ MAUVAIS
/* 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 */
✅ CORRECT
/* 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); /* ✅ */
RÈGLES CRITIQUES — NE JAMAIS OUBLIER
⚡ LES 5 COMMANDEMENTS
1

La signature DOIT correspondre exactement

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.

2

Le nom de fonction = son adresse

fptr = add et fptr = &add sont strictement équivalents. Le nom d'une fonction se comporte comme un pointeur constant vers elle.

3

Pas d'arithmétique sur les function pointers

Contrairement aux pointeurs de données, on ne peut pas faire fptr++ ou fptr + 2. Une fonction n'est pas un tableau.

4

Toujours vérifier NULL avant d'appeler

Si un pointeur de fonction est NULL et que tu l'appelles, c'est un segfault garanti. Teste avec if (fptr != NULL).

5

Utilise typedef pour simplifier les déclarations complexes

Pour les déclarations répétées ou complexes, crée un alias de type propre (voir section Tips Holberton ci-dessous).

COMMANDES DE COMPILATION
⚙️ GCC FLAGS HOLBERTON
# 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
Les function pointers eux-mêmes ne créent pas de leaks (ils sont sur la stack). Mais si tu passes un malloc comme argument via callback, pense à free() !
TIPS SPÉCIAUX HOLBERTON
🎓 TYPEDEF — SIMPLIFIE LA VIE
/* 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 STYLE
/* ✅ 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));
}
Betty aime les typedef pour les fptrs. Ça améliore la lisibilité et évite les erreurs de syntaxe.
🌳 USE CASES DANS LES PROJETS HOLBERTON
Projet "Variadic Functions"

Pas directement, mais les function pointers préparent le terrain pour comprendre les callbacks.

Projet "Printf" — _printf

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;
Projet "Simple Shell"

Dispatch des builtins (cd, exit, env...) via un tableau de function pointers.

qsort() de la stdlib

La fonction de tri standard prend un comparateur en callback : void qsort(void*, size_t, size_t, int(*)(const void*, const void*)).

RÉSUMÉ — LA CARTE MENTALE
🗺️ TOUT EN UN COUP D'ŒIL
/*  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);