Organisation programme C
ATTENTION : cette fiche ne constitue qu'une initiation à la structuration d'un projet en C. Ne la voyez pas comme une finalité mais comme une introduction. Certaines notions sont parfois présentées de façon simplifiées de façon à vous permettre de prendre assez facilement la main. Vous verrez plus tard comme faire cela de façon encore plus ordonnée et structurée.
Cette page vous explique comment organiser votre projet en répartissant vos codes entre plusieurs fichiers puis comment compiler l'ensemble.
Vous allez répartir votre code dans différents modules, chaque module étant défini à travers deux fichiers :
- Les fichiers d'en-tête (header) d'extension .h qui contiendront les informations publiques : ce qui doit être visible de l'exérieur pour parvenir à utiliser votre module :
- Les prototypes et documentations des fonctions (publiques)
- Les déclarations de structures (publiques)
- Les typedef
- Les constantes / macros
- Les fichiers sources d'extension .c qui contiendront la grande majorité de votre code dont vos fonctions privées : ces fichiers contiennent les codes effectifs de vos fonctions.
- Le code des fonctions publiques et privées
- Les fonctions privées (avec static)
- Les détails d’implémentation
- Les variables internes
1 - Exemple de programme avec module
Lorsque vous voulez créer du code qui soit réutilisable dans un autre cadre que votre projet, il ne faut pas le placer dans votre main.c mais dans deux autres fichiers d'extension .c et .h, disons personne.c. Prenons un exemple simple avec un module contenant de quoi définir une Personne :
MONPROJET/ (répertoire / dossier)
├── personne.h
├── personne.c
└── main.c
Cliquer pour obtenir le détail des différents fichiers.
Fichier personne.h (avec et sans documentation)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | |
Traduction en français des différentes lignes :
- Ligne 2 : SI la macro PERSONNE_H n'a pas encore été définie alors réalise les lignes 3 à 38.
- Ligne 38 : Fin du SI
- Ligne 3 : Directive signifiant "crée une macro PERSONNE_H".
- Lignes 10-13 : Création du type Personne qui est une STRUCTURE PUBLIQUE puisqu'on fournit sa structure exacte.
- Lignes 15-.. : On fournit PROTOTYPES et DOCUMENTATIONS de fonctions.
Cela permet d'utiliser include sans se soucier de savoir si son code a déjà été ramené dans le code source : si ce n'est pas le cas, on le ramène et on crée la macro pour s'en souvenir, sinon on ne fait rien puisqu'il n'y a rien sous le endif.
Notez que j'intégre ici des versions d'accesseurs qui gèrent les éventuels envois de personnes mal configurés.
Autre technique possible : on peut choisir de ne rien tester MAIS de préciser comme PRECONDITIONS : personne valide. C'est alors à la personne qui appelle votre fonction de faire le test AVANT l'appel.
Fichier personne.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| |
Notez la différence de syntaxe pour la directive include entre les modules personnels et les modules natifs.
La directive demandant de recopier personne.h permet de récupérer la structure ET de vérifier que les déclarations anticipées (prototypes) correspondent bien aux prototypes des déclarations.
Fichier main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | |
Remarquez que le compilateur peut vérifier que la syntaxe utilisée dans main.c à l'aide du fichier .h.
Par contre, pour le moment, on ne sait pas réellement exécuter ces fonctions car elles sont dans deux fichiers sources différents. C'est l'éditeur de lien qui va faire le lien entre les différents fichiers .c.
La compilation est ensuite réalisée de cette façon :
1 | |
- On lance un appel au programme gcc
- en lui donnant en arguments d'appel les différents fichiers .c à lier,
- l'option -o permet d'indiquer le nom qu'on veut donner à l'exécutable.
Il ne reste alors plus qu'à lancer votre exécutable (attention, sous Windows, le nom programme aura certainement été transformé en programme.exe) :
1
2
3
4 | |
RAPPEL sur les commandes en ligne de code :
- Trouver le répertoire actuel : .
- Trouver le répertoire parent : ..
- Se déplacer : cd chemin_a_suivre (ainsi cd ../.. veut dire de remonter deux fois dans l'arborescence.
- Voir le contenu du répertoire courant : ls ou ls -al.
2 - Dans votre projet
Lors d'un projet, on doit classifier les fonctionnalités en grande thématique et on pourra alors créer un fichier .h et un fichier .c pour chacune de ces grandes familles. De façon naïve, on pourrait faire ceci (mais on verra comment faire mieux plus bas) :
MONPROJET (répertoire / dossier)
|
├── FICHIERCSV (repertoire)
| ├── fichiercsv.h
| └── fichiercsv.c
|
├── DONNEES (repertoire)
| ├── donnees.h
| └── donnees.c
|
├── AFFICHAGE (repertoire)
| ├── affichage.h
| └── affichage.c
|
└── main.c
Vous pourrez inclure vos fichiers comme précédemment mais La compilation sera un peu différente car il faudra :
- Donner clairement la position des différences fichiers-sources
- Donner au compilateur la liste des dossiers à fouiller lorsqu'on utilise une directive #include.
1 | |
Comme vous le voyez, la commande de compilation peut rapidement devenir assez grande sur de vrais projets comportant beaucoup de modules. Il existe un programme nommé make qui permet de générer automatiquement cette compilation en allant lire un fichier de configuration nommé le Makefile.
Néanmoins, pour bien savoir utiliser ces configurations automatiques, il faut savoir... compiler à la main. Je ne vous donne donc ici aucune indication sur la création d'un Makefile. C'est volontaire. Commencez pour compiler manuellement. La flèche UP est votre amie !
En réalité, la plupart du temps, on organise un projet en plaçant les fichiers-source .c dans un répertoire nommé src et les fichiers d'en-tête dans un répertoire nommé include.
MONPROJET (répertoire / dossier)
|
├── src
| ├── fichiercsv.c
| ├── donnees.c
| └── affichage.c
|
├── include
| ├── donnees.h
| ├── affichage.h
| └─── fichiercsv.h
|
├── build
| ├── projetv1
| └── projetv2
|
└── main.c
Avec cette structure, il suffira de dire d'inclure le répertoire include avec -I include. Le compilateur va donc chercher automatiquement les .h dans ce répertoire.
Pour indiquer les fichiers .c à l'éditeur de liens, on peut utiliser src/*.c
Enfin, pour indiquer de placer les exécutables dans le répertoire build, on utilisera -o version3.
1 | |
Nous n'imposerons aucune structure particulière ici. Savoir construire une belle structure de projet est un module en soi et vous aurez l'occasion d'avoir des modules de conception logicielle.
Quelques mini-remarques en pasant :
- Vous pourriez partir sur une structure MVC : M pour Modèle (ce module gère les données de votre programme), V pour Vue (ce module gère l'interface offerte à l'utilisateur) et C pour Controleur (ce module fait la liaison entre le Modèle, la Vue et l'utilisateur). C'est le modèle usuel sur les sites Web dynamiques.
- Vous pourriez partir sur une structure en Couche : chaque module ne communique qu'avec son module parent et son module enfant. C'est le modèle d'Internet.
- Vous pourriez stocker vos fichiers-source (.c) dans un répertoire src et vos fichiers-en-tête (.h) dasn un répertoire include.
Aucune structure n'est imposée lors de ces premiers projets mais il faudra juste expliquer pourquoi vous avez structurer le votre de telle ou telle façon.
3 - Optionnel ? Avec malloc et free
Cette partie est purement optionnelle : elle n'est à utiliser que si vous êtes à l'aise avec la programmation en C.
Nous allons reprendre l'exemple précédent mais en cachant à l'utilisateur l'implémentation de nos personnes. Il ne pourra les créer qu'en passant par nos fonctions d'interface.
Il va donc falloir créer les structures permanentes en dehors du programme principal lui-même.
Pour cela, nous allons utiliser la fonction malloc() qui permet de demander l'attribution d'une zone-mémoire permaente (située dans le tas) et la fonction free() qui permet de libérer cette zone-mémoire une fois qu'on n'en a plus besoin.
Dans cette partie (avancée), nous utiliserons également la gestion mémoire pour stocker nos noms plutôt que de nous limiter à une taille fixe maximale (100 dans l'exemple de la partie 1).
La structure du code reste la même (si ce n'est que je vous montre comment placer les fichiers du module dans un répertoire :
MONPROJET (répertoire / dossier)
├── PERSONNE (repertoire)
| ├── personne.h
| └── personne.c
|
└── main.c
Cliquer pour obtenir le détail des différents fichiers.
Fichier personne.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | |
Traduction en français des différentes lignes :
- Ligne 10 : on définit un type Personne qui est un alias vers une structure qui n'est pas définie directement dans ce fichier : il s'agit donc d'une struture privée que l'utilisateur ne devra pas manipuler directement. Il doit se contenter d'utiliser les fonctions d'interface qu'on lui fournit.
- Ligne 21 : on lui permet de créer une personne.
- Ligne 27 : on lui permet de détruire une personne.
Fichier personne.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 | |
Notez qu'on y donne la défintion exacte de la structure interne privée.
On la manipule à travers le type Personne qui est un alias de struct PersonneInterne.
MONPROJET (répertoire / dossier)
├── PERSONNE (repertoire)
| ├── personne.h
| └── personne.c
|
└── main.c
Fichier main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | |
La compilation est ensuite réalisé de cette façon :
1 | |
4 - FAQ
Qu'est-ce qui est imposé lors du projet alors ?
- Votre projet devra comporter au moins trois modules dont un module contenant un jeu de tests : un ensemble d'appels et vérification de fonctions de façon à vérifier qu'elles répondent correctement. Cela permet de vérifier en temps réel qu'une modification faite pour rajouter quelque chose n'est casse pas une autre.
- La documentation de vos fonctions.
- Des assertions sur les fonctions pour la phase de conception. Lors de la réalisation de l'exécutable final, on désactivera les assert.
Article publié le 01 03 2026
Dernière modification : 03 03 2026
Auteur : ows. h.