Menu
Coddy logo textTech

Définir des structures opaques

Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 18 sur 61.

Maintenant que vous comprenez le concept de pointeurs opaques, voyons comment compléter le modèle. L'en-tête déclare qu'un type existe, mais le fichier source est l'endroit où vous définissez ce qu'il contient réellement.

Voici la configuration complète. Tout d'abord, l'en-tête avec seulement la déclaration anticipée :

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Type incomplet

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

Ensuite, le fichier source contient la définition réelle de la structure :

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // Masqué de l'extérieur !
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

Le point clé : struct Counter { ... } apparaît uniquement dans le fichier .c. Tout fichier qui inclut counter.h sait que Counter existe mais ne peut pas voir value ou step. Tenter d'écrire c->value dans main.c provoquerait une erreur de compilation — les membres sont véritablement invisibles.

Cette séparation est le fondement de l'encapsulation en C. L'étape suivante consiste à fournir un accès contrôlé via des fonctions getter et setter.

challenge icon

Défi

Facile

Construisons un module Timer qui illustre le modèle du pointeur opaque (opaque pointer pattern). Vous allez vous entraîner à masquer la définition de la structure à l'intérieur du fichier source, rendant ses membres internes complètement invisibles pour tout code utilisant votre module.

Vous allez créer trois fichiers :

  • timer.h : C'est votre interface publique. Déclarez un type Timer opaque en utilisant uniquement une déclaration anticipée (forward declaration) — pas de corps de structure ici ! Déclarez également deux fonctions : create_timer (prend un entier pour les secondes et retourne un pointeur Timer) et free_timer (prend un pointeur Timer et libère les ressources). Utilisez des gardes d'inclusion avec le symbole TIMER_H.
  • timer.c : C'est ici que réside le secret. Définissez la structure réelle struct Timer avec deux membres cachés : int seconds et int running (utilisez 1 pour en cours d'exécution, 0 pour arrêté). Implémentez votre constructeur pour allouer la structure, initialiser seconds avec la valeur fournie, et définir running à 1. Implémentez le destructeur pour libérer la mémoire.
  • main.c : Utilisez votre module Timer pour créer un minuteur et confirmer qu'il a été créé avec succès. Puisque les membres de la structure sont cachés, vous ne pouvez pas y accéder directement — c'est tout l'intérêt ! Affichez simplement un message confirmant la création, puis libérez le minuteur et affichez un message de nettoyage.

Vous recevrez une entrée : le nombre de secondes (un entier) pour initialiser le minuteur.

Dans votre fichier principal, créez un minuteur avec la valeur de secondes fournie. Affichez un message de création, puis libérez le minuteur et affichez une confirmation.

Affichez la sortie dans ce format :

Timer created
Timer freed

L'idée clé ici est que main.c peut manipuler des pointeurs Timer mais n'a aucun moyen d'accéder directement à seconds ou running. Tenter d'écrire t->seconds dans main.c provoquerait une erreur de compilation car le corps de la structure n'est défini que dans timer.c. C'est une véritable encapsulation — les détails d'implémentation sont complètement cachés aux utilisateurs de votre module.

Aide-mémoire

Le motif du pointeur opaque sépare la déclaration d'un type de sa définition, masquant ainsi les détails d'implémentation.

Le fichier d'en-tête contient uniquement une déclaration anticipée :

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // Type incomplet

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

Le fichier source contient la définition réelle de la structure :

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // Masqué de l'extérieur
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

La définition de la structure n'apparaît que dans le fichier .c. Les fichiers qui incluent l'en-tête savent que le type existe mais ne peuvent pas accéder directement à ses membres. Cela offre une véritable encapsulation en C.

Essayez vous-même

#include <stdio.h>
#include "timer.h"

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: Créer un minuteur avec la valeur de secondes fournie
    
    // TODO: Afficher "Timer created"
    
    // TODO: Libérer le minuteur
    
    // TODO: Afficher "Timer freed"
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Object Oriented Programming