Analyse et programmation 2
Le préprocesseur
Thèmes abordés
• Le préprocesseur
– Rôle et fonctionnement – Rôle et fonctionnement
• L’inclusion de fichiers
– Les directives include
• Comment compiler différentes variantes d’un programme
– La compilation conditionnelle
• Automatiser des tâches de programmation
– Les macros
• Les techniques avancées
– autres directives
• Comment utiliser judicieusement le préprocesseur
– Les limites pratiques
Le préprocesseur
Rôle et position dans la chaîne de compilation - rappel
main.c stdlib.h stdio.h
2. Compilateur Code source complet 1. Préprocesseur de texte
Processus de compilation
Analyse et programmation 2 - Le préprocesseur 2
Exécutable complet main.o
3. Editeur de liens
crt.lib
Le préprocesseur
Présentation
• Effets
Procède principalement à d remplacement de te te po r – Procède principalement à du remplacement de texte pour
produire un fichier source compilable complet.
– Le fichier transformé est ensuite transmis au compilateur.
• Directives préprocesseur
– Elles commencent par le caractère spécial # – Les directives standards
#define #error
#define #error
#elif #else #endif #if #ifdef #ifndef
#include #line #pragma #undef
Inclusion de fichiers
Syntaxe et effet
• Syntaxes
#include <stdio h>
#include <stdio.h>
#include "monfichier.h"
• Fonction
– Le préprocesseur remplace cette directive par le fichier spécifié.
– La forme #include <stdio.h>
• ne recherche le fichier mentionné que dans les répertoires de bibliothèque par défaut.
• Ces répertoires peuvent en général être configurés
Analyse et programmation 2 - Le préprocesseur 4
• Ces répertoires peuvent en général être configurés – dans l’environnement de développement
– par une variable d’environnement du système d’exploitation.
– La forme #include "monfichier.h"
• Recherche d’abord dans le répertoire du projet, puis comme l’autre forme.
Définition de symboles
La directive #define
• Syntaxe
Symbole Texte de remplacement
#define
Pas de point virgule final.
Si présent, fait partie du texte de remplacement
• Effet
– Agit sur le code source situé après sa définition.
– Toutes les occurrences de « Symbole » sont remplacées par le texte de remplacement.
– Symbole doit être un identificateur valide.
– Texte de remplacement peut être vide. La symbole est quand même défini.
Exemple
Symbole Texte de remplacement
#define
• Exemple
#define afficher printf int main()
{
afficher("Hello World !\n");
system("PAUSE");
}
Définition de symboles
La directive #define - pièges
• Effectue un remplacement de symboles
Donc pas de remplacement dans les chaînes de caractère – Donc pas de remplacement dans les chaînes de caractère
littérales.
• Attention, il s’agit d’un remplacement textuel
• Effets inattendus possibles
• Exemple
#define TEMPERATURE_MAX 80 + 20
Analyse et programmation 2 - Le préprocesseur 6
float temperature;
temperature = TEMPERATURE_MAX * 0.8; // 80 % // résultat obtenu :
temperatureMax = 80 + 20 * 0.8; // 96 !
Définition de symboles
La directive #define - recommandations
• Toujours tout mettre entre parenthèses !
#define TEMPERATURE MAX (80 + 20)
#define TEMPERATURE_MAX (80 + 20)
• Cette directive permet de créer un langage synonyme
#define POUR for
#define TANT_QUE while
#define FAIRE do
– Cette utilisation n’apporte rien en fonctionnalité.
– Elle a un impact catastrophique sur la lisibilité.p p q – Elle est donc hautement déconseillée.
• Utilisation habituelle
#define TEMPERATURE_MAX 100
Définition de symboles
La directive #define – définitions multilignes
• Les textes de remplacement
– Peuvent être très longs – Peuvent être très longs.
– Deviennent alors peu lisibles.
• Pour améliorer la lisibilité
– Il est possible de le répartir sur plusieurs lignes.
– On indique que texte se poursuit sur la ligne suivante en terminant une ligne avec le caractère ‘\’
• Exemple
Analyse et programmation 2 - Le préprocesseur 8
– Symbole pour afficher une trace du passage du programme.
#define TRACE printf("Le programme est passe " \
" dans le fichier %s" \
" ligne %d\n", \ __FILE__, __LINE__);
Définition de symboles
Symboles créés automatiquement
• Lors du traitement par le préprocesseur
– Un certain nombre de symboles sont définis automatiquementUn certain nombre de symboles sont définis automatiquement.
– Certains symboles font partie de la norme ANSI.
– En général, les compilateurs ajoutent de nombreux symboles spécifiques.
• Symboles standardisés
__DATE__ Chaîne littérale contenant la date de compilation du fichier.
__FILE__ Chaîne littérale contenant le nom du fichier source.
__LINE__ Numéro de la ligne en cours de compilation.
__STDC__ Définie à 1 si le compilateur respecte le standard C ANSI.
__TIME__ Chaîne littérale contenant l’heure de compilation du fichier.
__TIMESTAMP__ Chaîne littérale contenant la date/heure de dernière modification du fichier source
Définition de symboles
Définir des symboles globalement
• Au niveau de l’IDE
De nombre IDE permettent de définir des s mboles globalement – De nombreux IDE permettent de définir des symboles globalement.
– En général, dans les propriétés du projet.
– Cela permet de modifier globalement les caractéristiques d’un programme.
• Dans un fichier à inclure
– On peut créer un fichier .h contenant des définitions de symbole permettant de transformer le code.
Analyse et programmation 2 - Le préprocesseur 10
p
• Application
– On peut ainsi garder un même code source qui peut basculer très facilement entre environnement de simulation et réel.
Définition de symboles
Annuler une définition de symbole
• Syntaxe
# d f S b l
#undef Symbole
• Effet
– Après cette directive, le symbole n’est plus défini.
– Fonctionne même si Symbole n’était pas encore défini.
– Il est alors possible de donner une nouvelle définition à ce symbole.
– Attention : certains compilateurs acceptent une redéfinition d’un Attention : certains compilateurs acceptent une redéfinition d un symbole déjà défini
Les macros
Présentation
• Définition
Une macro correspond à ne définition paramétrée – Une macro correspond à une définition paramétrée.
– Elle est créée avec #define comme un symbole sans paramètres.
• Syntaxe
• Effet
– La macro est expansée
Nom(param1, param2) Texte de remplacement
#define
Analyse et programmation 2 - Le préprocesseur 12
• Les paramètres sont reportés dans le texte de remplacement.
– Le texte de remplacement obtenu remplace la macro dans le code source.
– Une macro peut avoir 0 paramètres. Elle doit alors être utilisée en mentionnant ().
Les macros
Exemple 1
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
int main() {
printf("%d\n", MAX(10, 15));
// après expansion :
printf("%d\n", ((10) > (15) ? (10) : (15)) );
system("PAUSE");
} }
Les macros
Exemple 2
#define TRACE_INT(nom, valeur) \ printf( \
"TRACE a passage dans le fichier %s " \
"TRACE au passage dans le fichier %s " \
"a la ligne %d, la variable %s vaut %d\n", \ __FILE__, __LINE__, nom, valeur);
int main() {
int i;
int somme;
somme = 0;
for (i = 0; i < 10; i++)
Analyse et programmation 2 - Le préprocesseur 14
o ( 0; 0; )
{
somme += i;
TRACE_INT("i", i) }
system("PAUSE");
}
Les macros
Les pièges
• Toujours penser qu’il s’agit d’un remplacement de texte.
– Et non pas d’une fonction – Et non pas d une fonction.
– L’évaluation des paramètres est gérée différemment !
• Exemple
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
int main() {
int i, k;
int i, k;
i = 8;
k = 9;
printf ( "%d\n", MAX(++i, --k));
system("PAUSE");
}
// Affiche 10 !
Les macros
Opérateurs spéciaux
• Opérateur # (stringizing operator)
Transforme le paramètre placé après # en chaîne littérale – Transforme le paramètre placé après # en chaîne littérale
• Exemple
#define TRACE_INT(variable) \ printf( \
"TRACE au passage dans le fichier %s " \
"a la ligne %d, la variable %s vaut %d\n", \ __FILE__, __LINE__, #variable, variable);
Analyse et programmation 2 - Le préprocesseur 16
"i" pour TRACE_INT(i)
Les macros
Opérateurs spéciaux
• Opérateur ## (token pasting operator)
– Concatène le paramètre placé avant ou après avec le texte de la macro.
– Permet de construire des noms de variables à partir du texte passé en paramètre à une macro.
• Exemple
#define demo_token_paster(n) printf("i" #n " = %d", i##n) int main()
{
int i1 = 10;
int i2 = 20;
int i2 20;
int i3 = 30;
demo_token_paster(1);
demo_token_paster(2);
demo_token_paster(3);
system("PAUSE");
}
La compilation conditionnelle
Directives #if
• Syntaxe
#if defined(DEBUG)( )
printf("Mode debug\n");
#endif
• Effet
– Si le symbole DEBUG est défini, le code entre if et endif fera partie du code source compilé.
– Dans le cas contraire, tout ce code sera supprimé.
– Les erreurs de syntaxe ne sont pas détectées.
Penser à tester toutes les variantes du code
Analyse et programmation 2 - Le préprocesseur 18
– Penser à tester toutes les variantes du code.
• Forme équivalente
#ifdef DEBUG
printf("Mode debug\n");
#endif
La compilation conditionnelle
Directives #if
• Forme générale
#if condition1
// zone compilée si condition1 est vraie
#elif condition2
// zone compilée si condition2 est vraie
#elif condition3
// zone compilée si condition3 est vraie
#else
// zone compilée si aucune condition vraie
#endif
#endif
• Les directives #elifet #elsesont optionnelles.
• Formes contractées
#ifdef symbol équivalent à #if defined(symbol)
#ifndef symbol équivalent à #if !defined(symbol)
La compilation conditionnelle
Directives #if – les conditions utilisables
• Existence d’un symbole
defined(SYMBOLE) defined(SYMBOLE)
• Egalité d’un symbole
SYMBOLE == 2
• Expressions complexes, selon la syntaxe du C
– Seulement des expressions statiques (SYMBOLE == 2) && defined(DEBUG)
Analyse et programmation 2 - Le préprocesseur 20
Applications
Internationalisation d’une application
• En fonction d’un symbole de langue, inclure un fichier qui définit les chaînes de caractères dans cette langue qui définit les chaînes de caractères dans cette langue
#ifdef FRANCAIS
#include "francais.h"
#else
#include "anglais.h"
#endif
• francais.h
#define TEXTE_CHOIX "Choix :"
#define TEXTE_CHOIX1 "1- Rechercher un titre"
• anglais.h
#define TEXTE_CHOIX "Choice :"
#define TEXTE_CHOIX1 "1- Find a book"
Applications
Génération de code en fonction de l’environnement
• Passage entre un environnement de simulation et un environnement réel en modifiant une seule définition.
• Exemple
void SetMotor(int On) {
#ifdef MODE_REEL
SetOutputBit(0x10FA, On ? 2 : 0)
#endif }
Analyse et programmation 2 - Le préprocesseur 22
void SetHeater(int On) {
#ifdef MODE_REEL
SetOutputBit(0x10FD, On ? 1 : 0)
#endif }
Applications
Basculement entre version DEBUG et RELEASE d’une application
• Passage mode debug à release
En mode release on ne e t pl s les traces d’e éc tion – En mode release, on ne veut plus les traces d’exécution.
• Exemple
#ifdef DEBUG
#define TRACE_INT(variable) \ printf( \
"TRACE au passage dans le fichier %s " \
"a la ligne %d, la variable %s vaut %d\n", \ __FILE__, __LINE__, #variable, variable);
#else
#define TRACE_INT(variable)
#endif
Le préprocesseur
Autres directives standard – générer une erreur de compilation
• Utiliser la directive #error C tt di ti d it êt i i
• Cette directive doit être suivie
– par le message d’erreur en clair.
– tel qu’il apparaitra dans le log du compilateur.
• Exemple
#if MODE == 1
#define Kp 10
#elif MODE == 2
Analyse et programmation 2 - Le préprocesseur 24
#elif MODE == 2
#define Kp 12.5
#else
#error Valeur de MODE invalide
#endif
Le préprocesseur
Autres directives standard - #pragma
• Chaque compilateur et chaque plateforme a des options spécifiques
spécifiques.
• Ces options peuvent être activées ou désactivées lors de la compilation en utilisant la directive pragma.
• Le texte qui suit pragma est spécifique au compilateur.
• Exemple – Visual Studio 2005
#pragma pack(push, 1) - packing is now 1 // ...
#pragma pack(pop) - packing is 8
Qu’avons-nous appris ?
• Le préprocesseur permet
– D’inclure des fichiers d’en tête ou de code – D inclure des fichiers d en tête ou de code.
– De redéfinir des symboles, et les pièges associés.
– De générer du code avec des macros.
– De compiler conditionnellement du code.
• Applications
– Basculement entre mode avec et sans trace
• Tout en conservant le code d’écriture des traces.
Ad t ti id d d à diffé t t t
Analyse et programmation 2 - Le préprocesseur 26
– Adaptation rapide du code à différents contextes.
• Environnement réel, simulation.
– Génération de programmes différents en fonction d’une définition
• Version anglaise, française, …
• Ne pas en abuser.
Vos questions
Analyse et programmation 2 - Le préprocesseur 28