A) Introduction
On va commencer avec juste un fichier pour commencer.
1) On va écrire un fichier de sources (fichier .c). C'est à partir de fichier source qu'on génére un binaire (module, librairie, exécutable... quelque chose que l'ordinateur peut manipuler directement). Ce premier fichier de source (par exemple main .c) doit impérativement comporter un main sinon il n'est pas possible d'en faire un exécutable. En effet, la fonction main est chargée au lancement du programme. Il est donc impératif d'en avoir une (et une seule !). Exemple
#include <stdio.h> // <--- Ceci est une inclusion de header. Il permet d'utiliser diverses fonctions, par exemple printf
int main(){
printf("plop !\n"); // j'écris plop !, le \n signifie retour à la ligne
return 0; // la fonction main est sensée retournée 0 quand tout s'est bien passé
}
Remarque :
Sous linux il est possible d'avoir la documentation de chaque fonction standard du langage C avec la commande man 3. Pour quitter appuies sur q. Exemple :
(mando@aldur) (~) $ man 3 printf
Dans cet exemple on voit en particulier qu'il faut inclure stdio.h pour pouvoir utiliser printf :
PRINTF(3) Manuel du programmeur Linux PRINTF(3)
NOM
printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf,
vsnprintf - Formatage des sorties
SYNOPSIS
#include <stdio.h>
...
A noter que tu peux chercher directement les pages de man sur google.
2) A présent je peux compiler ce fichier dans une console avec gcc. Compiler signifie convertir un fichier source (ici main.c) en exécutable (par exemple plop.exe). Les options -W -Wall permettent d'afficher des warnings afin d'être sûr que le programme est plop. Un bon programme compile sans warning et doit impérativement compiler sans erreur (sinon l'exécutable n'est pas généré). On peut aussi ajouter l'option -o suivie du nom de l'exécutable, par exemple plop.exe :
gcc -W -Wall -o plop.exe main.c
3) A présent je peux exécuter mon programme :
(mando@aldur) (~) $ gcc -W -Wall -o plop.exe plop.c
(mando@aldur) (~) $ ./plop.exe
plop
4) Effectivement il a bien écrit plop :)
B) Les modules
Il est possible de travailler sur plusieurs fichiers. Chaque grande "fonctionnalité" correspond à un module. Par exemple si je décide de coder mes propres listes il peut être valable de faire un module liste en vue de le réutiliser dans d'autres programmes. De plus ça ne nuit pas à la clarté du code. Un module de comporte pas de main, il ne fait qu'implémenter des fonctions utilisées soit par un autre module, soit par un fichier comportant un main. Un module est généralement composé de deux fichiers :
- un fichier .h qui déclare les fonctions disponibles dans le fichier .c. Ces déclarations sont appelées "prototypes" et sont généralement accompagnés de commentaires permettant d'expliquer le rôle de la fonction et à quoi correspondent chaque paramètre
- le fichier .c implémente les fonctions déclarées dans le .h
Exemple :
1) Je crée un fichier mando_a_dit.h. Au cas ou ce header serait inclu en plusieurs endroit, il est nécessaire de mettre un verrou à l'aide de #ifndef / #define. En effet un programme ne peut comporter deux fonctions portant le même nom. Ainsi avec ce verrou, le fichier ne peut être qu'inclu qu'une fois.
#ifndef MANDO_A_DIT
#define MANDO_A_DIT
/**
* \brief Ecrit "mando a dit de ..." suivi du message
* \param msg Le message
*/
void mando_a_dit(const char *msg);
#endif
2) Il est à présent temps d'implémenter le code de la fonction "mando_a_dit". Pour celà je crée le fichier mando_a_dit.c :
#include "mando_a_dit.h" // attention à mettre des " " et pas des < >
#include <stdio.h> // par contre pour les headers standards on mets bien < >
void mando_a_dit(const char *msg){
printf("mando a dit de %s",msg);
}
3) A présent je peux utiliser ma fonction mando_a_dit dans main.c, à condition d'inclure le header correspondant. main.c devient :
#include <stdio.h>
#include "mando_a_dit.h"
int main(){
printf("plop\n");
mando_a_dit("se coucher !\n");
return 0;
}
4) Ok maintenant il ne reste plus qu'à compiler notre programme, composé des fichiers main.c, mando_a_dit.h et mando_a_dit.c. Pour compiler le module mando_a_dit il faut utiliser l'option -c, qui va générer un mando_a_dit.o.
(mando@aldur) (~) $ gcc -W -Wall mando_a_dit.o plop.c
Tu peux vérifier avec la commande ls que mando_a_dit.o est effectivement apparu. Ce fichier contient en fait le binaire associé aux fonctions de ce module. Il est nécessaire de faire ainsi pour chaque module. Pour compiler un fichier source, il faut l'assembler avec tous les .o qu'il utilise. En l'occurrence main.c utilise le module mando_a_dit.o. Le principe est exactement le même avec un module utilisant un autre module. Pour compiler main.c je dois donc taper ;
gcc -W -Wall -o plop.exe mando_a_dit.o main.c
Ce qui donne :
(mando@aldur) (~) $ gcc -W -Wall -o plop.exe mando_a_dit.o main.c
(mando@aldur) (~) $ ./plop.exe
plop
mando a dit de se coucher !
Remarque :
On a utilisé le header stdio.h, mais qu'a aucun moment on a utilisé un module stdio.o. C'est normal, car quand tu compiles tu utilises implicitement la lib C qui implémente les fonctions standards du C (par exemple dans /usr/lib/gcc/i486-linux-gnu/4.1.3/)/ De plus le headers est inclu avec des < > car le fichier stdio.h est dans un répertoire "standard" (par exemple /usr/include/stdio.h)
C) Les makefile
Tu l'auras compris dès qu'on commence à avoir plein de modules, cela devient assez fastidieux de compiler un programme. Heureusement il est possible de créer un fichier makefile permettant de faire toute la compilation sans se préoccuper des dépendances. Afin de ne pas te noyer je ne vais pas aborder cette notion tout de suite, mais dis toi juste que de manière générale en programmation, on a intérêt à découper au maximum.
Si tu as besoin d'éclaircissements, n'hésite pas à poser tes questions
En espérant t'avoir été utile, bonne chance !
Configuration: Linux Debian
Mozilla 1.8.1.5