Dernière mise à jour :2008-07-24

informatique

Compilation d'un programme en C

Qu'il soit écrit en un langage ou un autre, un programme doit passer par plusieurs phases de traitement avant d'être convertit en fichier exécutable. Le compilateur transforme d'abord le code en fichier objet. Celui-ci est ensuite traité à nouveau avec l'éditeur de liens qui modifit le fichier en sa version exécutable. En langage c, on compte une étape de plus qui vient se loger entre le programme source et l'étape de la compilation dans ligne des différents traitements. Cette étape, c'est celle exécuté par le préprocesseur.

Le préprocesseur sert, comme son nom l'indique, à préparer le passage du code source dans le procédé de compilation. Il transforme le fichier, lui ajoute du code ou en enlève en se basant sur une série d'option décrite à l'intérieur des lignes du source. La liste suivante énumère les différentes options pouvant être exécuté par le préprocesseur:

  • Remplacement de code
  • Compilation conditionnelle
  • Inclusion de fichier
  • Numérotation des lignes
  • Affichage d'erreur

Les options citées ci-haut sont représentés dans le code source par des instructions nommées 'directives'. Il existe deux différence au niveau de la syntaxe des directives par rapport aux autres instructions. Tout d'abord, les directives sont toujours précédées du symbole '#' et en second lieu, elles ne se termine jamais par un caractère ';'.

1. Remplacement de code

#define

Dans un programme en langage c, n'importe quel bout de code peut être remplacé par un nom quelconque. Ensuite, ce nom peut être utilisé dans le programme en guise de remplacement de la portion de code en question. chaque fois que le préprocesseur tombe sur l'un de ces noms, il le convertit par le code. On peu distinguer deux types de portions de code. Le premier est appelé 'constante symbolique' et il désigne une chaine de caractère ou un nombre bien préci. Le second est nommé 'macro' et représente une instruction seule ou un regroupement d'instructions.

Les deux directives ont la syntaxe suivante:

#define

Par exemple:

#define nombreMagique 10.5

défini une constante symbolique. Partout ou le compilateur trouvera le nom 'nombreMagique', il le remplacera par 10.5.

#define effacer printf("\033[2j");

défini une macro contenant une instruction permettant d'éviter de toujours réécrire printf("\033[2j"); chaque fois que l'on veut effacer l'écran.

On peut aussi créer des macros comportant plus d'une instructions comme dans l'exemple suivant:

#define effacer { printf("\033[2j");printf("Tout est effacé.\n"); }

Lorsque la directive est défini avec un nom mais pas de code de remplacement, le préprocesseur efface la constante symbolique partout ou il la trouve dans le code. Dans un cas pareil, la constante, bien qu'éliminée totalement du fichier source, possède tout de même la valeur 1 ce qui permet d'utiliser celle-ci dans un cas de compilation conditionnelle. On utilise alors la constante en tant que représentation d'un état quelconque.

#undef

Pour signifier au préprocesseur d'arrêter de remplacer une constante symbolique ou une macro, il suffit d'utiliser la directive #undef dont la syntaxe est la suivante:

#undef <nom>

Comme par exemple dans le listing suivant:

Listing 1.0 Directive #undef
1. #include <stdio.h>
2. #define ecrire printf
3. 
4. main()
5. {
6.  ecrire("Bonjour.\n");
7. #undef ecrire
8.  ecrire("Aurevoir.\n");
9. }

En compilant ce programme, le préprocesseur ne remplacera pas le second nom 'ecrire' par l'instruction printf et le 'linker' génèrera donc une erreur n'étant pas en mesure de reconnaitre le nom ecrire en tant qu'instruction existante.

2. Compilation conditionnelle

Il existe un certain nombre de directives permettant de rendre conditionnel la compilation de portion de code source.

La liste ci-dessous énumère les diverses directives utilisables:

  • #if
  • #ifdef
  • #ifndef
  • #elif
  • #else
  • #endif

On utilise ces directives un peu de la même façon qu'on le fait avec les instructions 'if' et 'else' d'un code source. Le résultat d'une expression formée de constante suivant une directive #if est évalué. Si le résultat donne 0 (false) ou != 0 (true), le compilateur incorpore ou pas le bloc de code.

Si le code source contient une instruction #elif ou else, alors, si l'expression est évaluée négative, le préprocesseur passe dans l'instruction #elif ou else.

Les principales utilités des directives conditionnelles sont la recherche d'erreur ou la pratique de différents essais.

Il existe aussi la possibilité de mixer la directive define dans une instruction #if. La syntaxe est la suivante:

#if define <constante symbolique>

Dans ce cas, le préprocesseur vérifi l'existance de la définition de cette constante symbolique. Si celle-ci est définie, l'instruction prend alors la valeur 'true' sinon, elle prend la valeur 'false' et le traitement du préprocesseur s'effectu ensuite en conséquenece du résultat. Il y a aussi possibilité d'inclure l'opérateur négatif (!) devant l'instruction define pour inverser le résultat.

3. Inclusion de fichier

La majorité des programmes conçu en langage c contiennent au moins une directive d'inclusion de fichier. On reconnait facilement un fichier d'inclusion par son extention '.h'. Par exemple, la directive suivante permet d'inclure le contenu du fichier stdio.h dans le code, exactement à l'emplacement de la directive dans le fichier source:

#include <stdio.h>

Ce fichier fait partie d'un groupe de fichier 'headers' fournit avec le compilateur. Cette panopli de fichiers permettent l'utilisation de fonctions sans avoir à les recréer soit même. Par exemple, en incluant le fichier string.h dans un programme qui nécessite l'exécution de certain traitements sur des chaines de caractères, ont s'évite de recréer une multitude de fonctions de manipulation de chaines. économie de temps et dans bien des cas, d'argent.

Les fichiers headers inclus avec le compilateur sont rangés dans un répertoire précis. Le compilateur connais donc leur emplacement. Par contre, si vous développez vos propre fichiers header, vous devrez indiquer au compilateur dans quel répertoire ceux-ci sont situé mis à part, si vous les insérez dans le même répertoire que les headers du compilateur ce qui est à déconseillé. En effet, les fichier headers du compilateur ne doivent pas (de préférence) être modifié, donc en incluant vos fichier dans avec ceux du compilateur, vous risqueriez un jour de détruire ou de modifier accidentellement un fichier qui n'est pas des votres.

Il existe deux façon d'inclure un fichier. En voici la syntaxe:

#define <nomfichier.h>
ou
#define "nomfichier.h"

La différence entre ces deux méthode d'appel de fichier, c'est que la première cherchera d'abord le fichier dans le dossier des headers standards fournits avec le compilateur. Si il n'y trouve pas le fichier, il poursuivra sa recherche dans le dossier de vos headers personnels. La seconde façon exécute le traitement inverse. Dans les deux cas, si après avoir scruté les deux répertoire, le compilateur n'a pas trouvé le fichier à inclure, il retournera une erreur.

4. Numérotation des lignes

La directive #line permet d'initialiser la numérotation interne des lignes du fichier source. Lorsque le compilateur génère une erreur, ces numéros de lignes sont alors affiché. La directive permet aussi de changer le nom du fichier qui apparait dans un message d'erreur. La syntaxe de la directive est la suivante:

#line <constante> "<texte remplacant le nom du fichier>"

À noté que le texte remplacant le nom du fichier doit être écrit entre guillement.

5. Affichage d'erreur

La directive #error combiné généralement avec une condition, permet l'affichage d'un certain message d'erreur. Cette directive est souvent utilisé à des fin d'avertissement pour aider les futurs programmeur qui modifieront le programme.

Cette directive a la syntaxe suivante:

#error <texte à afficher>

Auteur : Sylvain Bilodeau

Date de mise en ligne : 2002-08-20 01:00:00

Aucun commentaire pour l'instant.