Utilisation de plusieurs fichiers
Pour qu'un ensemble de fonctions cohérentes puisse être
réutilisé facilement, il convient de les regrouper dans
un même fichier. Le fichier est en C à la base de ce qu'en
programmation on nomme la
modularité.
Cela permettra en outre de leur faire partager des variables sans que
ces variables soient utilisées par des fonctions
extérieures, ainsi que des fonctions auxilliaires. C'est ce
qu'on appelle l'encapsulation.
Modularité et encapsulation
sont deux des concepts les plus importants de la programmation, quelque
soit le type de langage utilisé.
Exemple
Voici le fichier base2.c qui contient les
calculs des puissances de 2 et de la partie entière
supérieure du logarithme en base 2 d'un entier. base2.c contient
également une fonction main
destinée au test des fonctions présentes, ainsi qu'une
fonction précisant l'usage de la commande ainsi construite.
La commande affiche les valeurs des fonctions puissance_de_2 et ilog pour des valeurs allant de
0 à l'entier passé en paramètre. Pour
vérifier l'exactitude du résultat, on calcule
également la partie entière supérieure du
logarithme en base 2 à l'aide des fonctions de la
bibliothèque mathématique log2 et ceil. La fonction ceil renvoyant un double, il
est nécessaire de faire un cast (int) pour éviter un
avertissement car la valeur affichée est un entier (%d).
Le fichier base2.c est
compilé à l'aide de la commande
gcc -Wall -std=c99 base2.c -lm -o
base2
L'option -l permet
de lier une bibliothèque, ici la bibliothèque libm. Les bibliothèques
ayant toutes le préfixe lib,
celui-ci est omis dans l'option -l.
Voici un exemple d'exécution :
$ base2 10
0
1
0 -2147483648
1
2
0 0
2
4
1 1
3
8
2 2
4
16 2 2
5
32 3 3
6
64 3 3
7
128 3 3
8
256 3 3
9
512 4 4
10
1024 4 4
On souhaite séparer les fonctions réutilisables de base2
de la partie liée à leur test. Pour cela, on
répartit les fonctions existantes dans deux fichiers : base2.c
et test_base2.c. Il faut
également créer un fichier d'entête base2.h
contenant les déclarations des fonctions de base2.c.
On remarquera :
- les commentaires sur les fonctions de base2 sont dans le fichier
d'entête. Ce fichier doit être lu par le "client" du module
base2. Par contre, il n'a pas à regarder les fichiers .c
contenant les implémentations.
- les directives du pré-processeur contenues dans base2.h ont pour
objectif d'éviter les inclusions multiples qui entraînent
des erreurs de compilation.
- Le fichier d'entête est inclus dans test_base2.c,
mais aussi dans base2.c pour assurer la
cohérence entre les déclarations et les
définitions des fonctions.
- On a déclaré la fonction usage dans test_base2.c
même si ce n'est pas indispensable.
La commande pour compiler l'ensemble est la suivante :
gcc -Wall -std=c99 base2.c
test_base2.c -lm -o base2
On peut également compiler séparément chacun des
fichiers sources et ensuite réaliser l'édition de lien.
Cela permet lorsque beaucoup de fichiers sont utilisés de ne
recompiler que ceux ayant été modifiés.
$ ls
base2.c
base2.h test_base2.c
$ gcc -Wall -std=c99 -c base2.c
$ ls
base2.c
base2.h
base2.o test_base2.c
$ gcc -Wall -std=c99 -c
test_base2.c
$ ls
base2.c
base2.h
base2.o
test_base2.c test_base2.o
$ gcc base2.o test_base2.o -lm -o
base2
$ ls
base2
base2.h test_base2.c
base2.c
base2.o test_base2.o
$ base2 5
0
1
0 -2147483648
1
2
0 0
2
4
1 1
3
8
2 2
4 16
2 2
5 32
3 3
Afin d'automatiser ce processus, le système Unix propose l'outil
make associé aux
fichiers Makefile. Le
fichier Makefile suivant,
situé dans le même répertoire que les fichiers base2.h, base2.c,
et test_base2.c permet ainsi
d'obtenir le même résultat que précédemment
en utilisant uniquement la commande make.
$ ls
Makefile
Makefile.bak
base2.c
base2.h test_base2.c
$ make
gcc -c -Wall -std=c99 base2.c
gcc -c -Wall -std=c99 test_base2.c
gcc -o base2 base2.o test_base2.o -lm
$ ls
Makefile
base2
base2.h test_base2.c
Makefile.bak
base2.c
base2.o test_base2.o
$ make clean
rm -f *.o core base2
$ ls
Makefile
Makefile.bak
base2.c
base2.h test_base2.c
Utilisation de fonctions et variables statiques
Une variable qui est déclarée en dehors de toute fonction
est dite globale.
Dans un fichier .c, on trouve deux sortes de fonctions et de variables
globales: les externes et les
statiques. Par défaut,
ou si elle est déclarée explicitement externe à
l'aide du mot clé extern,
un fonction ou un variable est externe, c'est à dire qu'elle
peut être utilisée dans tout le programme. Au contraire,
un fonction ou une variable globale statique, (mot clé static), ne peut être
utilisée qu'à l'intérieur du fichier ou elle est
déclarée.
Par exemple, supposons que l'on veuille rajouter à base2.c une fonction qui
affiche le mot binaire correspondant à un entier n. Cette
fonction est en deux parties pour traiter le cas particulier du 0 : afficher_binaire(unsigned) et afficher_binaire_aux(unsigned).
La
deuxième fonction n'ayant d'utilité que dans le cadre de
la première, elle sera déclarée static.
Les trois fichiers base2.c, base2.h
et test_base2.c
sont modifiés.
$ base2 5
0
1
0
-2147483648 0
1
2
0
0 1
2
4
1
1 10
3
8
2
2 11
4
16 2
2 100
5
32 3
3 101