• Aucun résultat trouvé

Le préprocesseur

N/A
N/A
Protected

Academic year: 2022

Partager "Le préprocesseur"

Copied!
15
0
0

Texte intégral

(1)

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

(2)

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

(3)

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");

}

(4)

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

(5)

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

(6)

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

(7)

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");

} }

(8)

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 !

(9)

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");

}

(10)

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)

(11)

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"

(12)

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

(13)

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

(14)

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

(15)

Analyse et programmation 2 - Le préprocesseur 28

Références

Documents relatifs

8-10 : la méthode .extend() ajoute en bout de liste tous les éléments de la liste passée en argument ; liste a alors trois éléments?. Dans les exemples qui suivent, on crée de

On perd une partie

• Le traitement de liste est souvent réalisé à l’aide de fonctions récursives (il n’existe pas d’équivalent fonctionnel direct des boucles). • Calcul de la longueur

• En tapant entre guillemets la chaîne que vous voulez mettre dans votre tableau, le compilateur C calcule automatiquement la taille nécessaire... Création et initialisation de

Écrire une fonction est_Palindrome prenant pour paramètre une chaîne de caractères et renvoyant 1 s'il s'agit d'un palindrome non strict et 0

chaîne de caractères = suite d’octets Lecture fichier texte : lignes stockées dans une chaîne de caractères.. Lecture fichier binaire : données stockées dans une chaîne

chaîne de caractères = suite d’octets Lecture fichier texte : lignes stockées dans une chaîne de caractères.. Lecture fichier binaire : données stockées dans une chaîne

Lecture fichier binaire : données stockées dans une chaîne de caractères... 3.2 str : chaîne d’octets (pas