Historiquement, la modularité est apparue comme un moyen de séparer les grands projets en parties logiques indépendantes afin d'améliorer la lisibilité, la réutilisation du code et la séparation des responsabilités entre les développeurs. En C, la modularité est réalisée au niveau des fichiers — fichiers source (.c) et fichiers d'en-tête (.h).
Le problème auquel sont confrontés les programmeurs : comment organiser l'interaction entre les parties du code, éviter la duplication des définitions, ne pas nuire à l'encapsulation et simplifier la compilation.
La solution consiste à utiliser la séparation de l'interface et de l'implémentation :
Exemple de structure de code modulaire :
// mymath.h #ifndef MYMATH_H #define MYMATH_H int add(int, int); #endif // mymath.c #include "mymath.h" int add(int a, int b) { return a + b; } // main.c #include "mymath.h" #include <stdio.h> int main() { printf("%d\n", add(3, 4)); return 0; }
Caractéristiques clés :
Peut-on définir une variable avec extern dans un fichier d'en-tête et l'utiliser en toute sécurité dans plusieurs modules ?
Non ! Les variables globales doivent être définies dans un seul fichier .c, et uniquement déclarées dans les en-têtes via extern. Sinon, des erreurs de liaison se produiront à cause de la "définition multiple".
Est-il nécessaire d'inclure chaque fichier d'en-tête via #include seulement une fois ?
Il est nécessaire d'encadrer chaque fichier .h avec des macros de protection (#ifndef/#define/#endif), sinon, lors d'une inclusion multiple, il y aura des conflits de déclarations et des erreurs de compilation.
Est-il possible de réaliser une encapsulation pure des données privées d'une structure (opaque pointer) en C ?
Oui. Ce qu'on appelle "opaque pointer" permet de masquer les détails de la structure à l'utilisateur :
// mystruct.h typedef struct MyStruct MyStruct; MyStruct* create(void); void destroy(MyStruct*); // mystruct.c struct MyStruct { int a; };
Toute la logique est réalisée dans un long fichier .c, le code se répète, les variables globales se chevauchent, des erreurs de liaison se produisent.
Avantages :
Inconvénients :
Le code est décomposé par modules, des guards d'inclusion sont utilisés, les données privées sont cachées via un opaque pointer.
Avantages :
Inconvénients :