Dans les projets d'ingénierie logicielle d'entreprise, un programme ne réside jamais dans un fichier unique. Le noyau Linux, par exemple, compte plus de 30 millions de lignes réparties dans des dizaines de milliers de fichiers sources !
La programmation modulaire consiste à diviser le projet en plusieurs composants autonomes et réutilisables appelés modules. En langage C, un module se compose d'un binôme indissociable : un fichier d'en-tête .h (Header) et un fichier source d'implémentation .c.
1. Anatomie d'un module C : .h et .c#
Un module C dissocie clairement l'interface publique (ce que le module propose au monde extérieur) de son implémentation privée (la façon dont les calculs sont concrètement exécutés) :
flowchart LR
subgraph Module [Module : geometrie]
H["geometrie.h\nInterface Publique\n- Prototypes des fonctions\n- Constantes et macros\n- Types de données"]
C["geometrie.c\nImplémentation Privée\n- Corps des fonctions\n- Variables internes"]
end
Main["main.c\nProgramme Principal\n#include \"geometrie.h\""] -->|Lit l'interface| H
C -->|Implémente| H
- Le fichier d'en-tête (
.h) : C'est le mode d'emploi du module. Il ne contient aucun code exécutable (pas de corps de fonction{ ... }), mais uniquement les prototypes des fonctions, les constantes et les déclarations de types. - Le fichier source (
.c) : Il contient le code exécutable des fonctions déclarées dans le.h. - La directive
#include: #include <stdio.h>: Les chevrons< >recherchent le fichier dans les répertoires système des bibliothèques C standard.#include "geometrie.h": Les guillemets" "recherchent en priorité le fichier dans le dossier local du projet.
2. La protection obligatoire : Les gardes d'inclusion (Include Guards)#
Lorsque plusieurs fichiers d'un projet incluent le même fichier d'en-tête, le compilateur risque de lire plusieurs fois les mêmes déclarations, provoquant des erreurs de redéfinition (redefinition error).
Pour éviter cela, tout fichier .h doit être protégé par un garde d'inclusion :
#ifndef NOM_DU_MODULE_H
#define NOM_DU_MODULE_H
/* Déclarations de prototypes, constantes et types ici */
#endif /* NOM_DU_MODULE_H */
Au premier passage, la macro NOM_DU_MODULE_H n'est pas définie : le préprocesseur la définit et lit le fichier. Si le fichier est inclus une seconde fois ailleurs, la macro existe déjà et le préprocesseur ignore l'intégralité du contenu !
3. Exemple complet pas à pas : Le module geometrie#
Construisons un module de calcul géométrique composé de trois fichiers :
1. L'interface : geometrie.h#
#ifndef GEOMETRIE_H
#define GEOMETRIE_H
/* Constantes du module */
#define PI 3.141592653589793
/* Prototypes des fonctions exportées */
double CalculerAireCercle(double rayon);
double CalculerPerimetreCercle(double rayon);
double CalculerAireRectangle(double longueur, double largeur);
#endif /* GEOMETRIE_H */
2. L'implémentation : geometrie.c#
#include "geometrie.h"
double CalculerAireCercle(double rayon)
{
return PI * rayon * rayon;
}
double CalculerPerimetreCercle(double rayon)
{
return 2.0 * PI * rayon;
}
double CalculerAireRectangle(double longueur, double largeur)
{
return longueur * largeur;
}
3. Le programme consommateur : main.c#
#include <stdio.h>
#include "geometrie.h"
int main(void)
{
double r = 5.0;
double aire = CalculerAireCercle(r);
double perimetre = CalculerPerimetreCercle(r);
printf("--- CALCULS GEOMETRIQUES ---\n");
printf("Rayon : %.2f cm\n", r);
printf("Perimetre : %.2f cm\n", perimetre);
printf("Aire : %.2f cm²\n", aire);
return 0;
}
4. Compiler un projet multifichier avec GCC#
Pour compiler ce projet en ligne de commande avec le compilateur GCC, vous devez fournir tous les fichiers .c (les fichiers .h sont inclus automatiquement par le préprocesseur) :
Que se passe-t-il sous le capot ?#
Dans les grands projets, on compile chaque fichier séparément en un fichier objet binaire (.o), puis on les lie ensemble :
# 1. Compilation séparée en fichiers objets
gcc -Wall -Wextra -c geometrie.c -o geometrie.o
gcc -Wall -Wextra -c main.c -o main.o
# 2. Édition des liens pour produire l'exécutable
gcc main.o geometrie.o -o programme_geometrie.exe
5. Fonctions privées d'un module avec le mot-clé static#
Si votre module a besoin d'une fonction d'aide interne qui ne doit pas être visible ou appelée par d'autres fichiers extérieurs, déclarez-la avec le mot-clé static dans le fichier .c (et ne la déclarez pas dans le .h) :
// Cette fonction ne peut être appelée QUE depuis geometrie.c
static double ValiderRayon(double r)
{
return (r < 0.0) ? 0.0 : r;
}
Cela garantit une excellente encapsulation et protège votre code contre les collisions de noms dans les grands projets.
Vous savez désormais structurer un projet C selon les règles de l'art. Validons l'ensemble des notions de cette Partie 2 à travers des problèmes d'application complets.
Commentaires