/* Le langage préprocesseur est un langage de macro
analysé avant la compilation.
Une macro est l'association d'un texte de remplacement
à un identificateur.
Indépendant du C le langage préprocesseur pourrait être utilisé
avec d'autres langages de programmation.
Il ignore totalement la structure du programme en C. */
/* Les directives sont évaluées de haut en bas
elles commencent par #
suivi d'un nombre quelconque d'espaces ou tabulations
suivi du nom de la directive en minuscule.
Les directives doivent être déclarées sur une ligne dédiée.
#define définit un motif de substitution
#undef retire un motif de substitution
#include inclusion de fichier
#ifdef, #ifndef, #if, #else, #elif, #endif compilation conditionnelle
#pragma extension du compilateur
#error message d'erreur et arrêt de la compilation
*/
#include <stdio.h>
/* inclut stdio.h qui décrit
- la fonction printf */
#define TAILLE 100
int main(void)
{
printf("--------------------\n");
printf(" Déclarations de\n");
printf(" constantes\n");
printf("--------------------\n");
printf("constante TAILLE = %d\n", TAILLE);
/* sera transformé en printf("constante TAILLE = %d\n", 100);
avant la compilation */
#define PREFIXE "erreur:"
#define FORMULE (1 + 1)
printf(PREFIXE "%d\n", FORMULE);
/* sera transformé en printf("erreur:" "%d\n", (1 + 1));
avant la compilation
(rappel: en C on peut écrire une chaîne en plusieurs fois) */
#define BLOC_DEFAUT \
default: \
puts("Cas interdit."); \
break;
/* équivalent à
#define BLOC_DEFAUT default: puts("Cas interdit."); break;
mais sur plusieurs ligne grâce à \ */
int var = 3;
switch (var)
{
case 1:
puts("choix 1");
break;
case 2:
puts("choix 2");
break;
BLOC_DEFAUT
}
#define TAILLE 100
// aucun avertissement, inutile TAILLE est déjà remplacé par 100
#define TAILLE 3
/* déclenche un avertissement du compilateur
redéfinition de TAILLE */
/* à savoir !
utiliser autant que possible une énumération */
enum jours_de_la_semaine { lundi, mardi, mercredi };
enum jours_de_la_semaine variable_jour_courant;
variable_jour_courant = lundi;
// plutôt que
#define LUNDI 0
variable_jour_courant = LUNDI;
/* est Recommandé !
notamment pour le débogage */
printf("\n--------------------\n");
printf(" Déclarations\n");
printf(" automatiques\n");
printf(" de constantes\n");
printf("--------------------\n");
/* en C sont déclarés automatiquement:
__FILE__ de type (char *) nom du fichier courant
__LINE__ de type (int) numéro de la ligne en cours
__DATE__ de type (char *) jour, mois, année
__TIME__ de type (char *) heures, minutes, secondes
__STDC__ de type (int) C ANSI (sans les spécificités du compilateur)
*/
printf(__FILE__ "\n");
printf("ligne = %d\n", __LINE__);
printf(__DATE__ "\n");
printf(__TIME__ "\n");
#ifdef __STDC__
printf("le compilateur travaille en C norme ANSI\n");
#else
printf("le compilateur ne travaille PAS en C norme ANSI\n");
#endif
/* si __STDC__ a été défini, c'est à dire a une valeur quelconque
alors le compilateur travaille en C ANSI
sinon le compilateur ne travaille PAS en C ANSI */
printf("\n--------------------\n");
printf(" Extensions\n");
printf("--------------------\n");
/* des constantes supplémentaires
sont disponibles suivant le compilateur
on retrouve le plus souvent celle ci et aussi d'autres: */
/* Détection du système d'exploitation
_WIN32 ou __WIN32__ (Windows)
linux ou __linux__ (Linux)
__APPLE__ ou __MACH__ (Apple Darwin)
__FreeBSD__ (FreeBSD)
__NetBSD__ (NetBSD)
sun ou __SVR4 (Solaris)
exemple : */
#if defined(linux) || defined(__linux__)
printf("le système d'exploitation est gnu linux\n");
#endif
/* Détection des compilateurs
Visual C++ : _MSC_VER
Compilateur gcc :
Version : __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__.
Compilateurs Borland :
__TURBOC__ (version de Turbo C ou Borland C)
__BORLANDC__ (version de Borland C++ Builder)
Divers : __MINGW32__ (MinGW), __CYGWIN__ ou __CYGWIN32__ (Cygwin).
exemple : */
#ifdef __GNUC__
printf("gcc version : %d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#endif
printf("\n--------------------\n");
printf(" Déclaration\n");
printf(" de macros\n");
printf("--------------------\n");
/* Une macro est en fait une constante
qui peut prendre un certain nombre d'arguments.
Les arguments sont placés entre parenthèses
après le nom de la macro sans espaces */
#define SWAP(x,y) x ^= y; y ^= x; x ^= y
/* échange la valeur des deux arguments
(qui doivent être des variables entières) */
char a = 'A', b = 'B';
printf("a = %c, b = %c\n", a, b);
SWAP(a, b);
// échange les valeurs de a et b
printf("a = %c, b = %c\n", a, b);
int c = 2, d = 1;
if (c < d) SWAP(c, d);
// sera remplacé avant compilation par :
if (c < d) c ^= d; d ^= c; c ^= d;
/* problème !
seule l'expression c ^= d
sera dans la condition if (c < d) */
printf("c = %d, d = %d\n", c, d);
#define SWAP(x,y) { x ^= y; y ^= x; x ^= y; }
// voilà comment on corrige SWAP
c = 2, d = 1;
if (c < d) SWAP(c, d);
printf("c = %d, d = %d\n", c, d);
#define MAX(x,y) x > y ? x : y
// retourne le maximum
printf("\nmaximum = %d\n", MAX(4, 6));
// sera remplacé avant compilation par :
printf("maximum = %d\n", 4 > 6 ? 4 : 6);
int i;
i = 2 * MAX(4,6);
// sera remplacé avant compilation par :
i = 2 * 4 > 6 ? 4 : 6;
printf("i = %d\n", i);
/* résultat :
i devrait etre égal à 2 * 6 soit 12
pourtant ce qui s’exécute est
8 > 6 ? 4 : 6
et on obtient 4 ! */
#define MAX(x,y) ((x) > (y) ? (x) : (y))
// voilà comment on corrige MAX
i = 2 * MAX(4, 6);
printf("i = %d\n", i);
int e, f = 2, g = 1;
e = MAX(++f,g);
printf("e = %d\n", e);
f = 2; g = 1;
// lors de la compilation on a :
e = ((++f) > (g) ? (++f) : (g));
printf("e = %d\n", e);
/* on s'attend à avoir 3 comme résultat
mais c'est 4 !
++f est exécuté 2 fois */
/* Dans ce cas on ne peut pas utiliser de macro
il est alors préférable d'utiliser une fonction inline */
inline int maxi(int a, int b)
{
return a > b ? a : b;
}
f = 2; g = 1;
e = maxi(++f, g);
printf("e = %d\n", e);
printf("\n--------------------\n");
printf(" Suppression\n");
printf(" d'une définition\n");
printf("--------------------\n");
#undef TAILLE
#undef MAX
#undef SWAP
printf("\n--------------------\n");
printf(" Transformation\n");
printf(" en chaîne de\n");
printf(" caractères\n");
printf("--------------------\n");
#define assert(condition) \
if( (condition) == 0 ) \
{ \
puts( "La condition " #condition " a échoué" ); \
exit( 1 ); \
}
/* avec une macro qui prend au moins 1 argument
pour utiliser l'argument sous forme de texte
on peut utiliser #argument */
//--------------------------------------------------------------------------------
printf("\n--------------------\n");
printf(" Concaténation\n");
printf(" d'arguments\n");
printf("--------------------\n");
#define version(symbole) symbole ## _v123
int version(variable);
// Déclare int variable_v123;
printf("\n--------------------\n");
printf(" Macros\n");
printf(" à nombre variable\n");
printf(" d'arguments\n");
printf("--------------------\n");
#define debug(format, ...) fprintf( stderr, "Dans " __FILE__ " ligne %d " format "\n", __LINE__, __VA_ARGS__ )
debug("erreur %s %s %s", "argument 1", "argument 2", "etc.");
/* valide en C version C99 uniquement
... doivent être remplacés par un au minimum ou plusieurs arguments */
#define debug(format, ...) fprintf( stderr, "Dans " __FILE__ " ligne %d " format "\n", __LINE__, ##__VA_ARGS__ )
debug("erreur");
/* valide avec le compilateur gcc
##__VA_ARGS__ 0 ou plusieurs arguments */
printf("\n--------------------\n");
printf(" Autres exemples\n");
printf("--------------------\n");
void printf_sizeof(char * type, size_t taille)
{
printf("sizeof(%s) = %zd.\n", type, taille);
}
printf_sizeof("char", sizeof(char));
#define PRINTF_SIZEOF(type) printf("sizeof(" #type ") = %zd.\n", sizeof(type))
PRINTF_SIZEOF(char);
// on préfère la macro !
enum etat_t { Arret, Demarrage, Marche, ArretEnCours } etat = Marche;
#define CASE_ETAT(etat) case etat: printf("Dans l'état " #etat "\n"); break;
switch (etat)
{
CASE_ETAT(Arret)
CASE_ETAT(Demarrage)
CASE_ETAT(Marche)
CASE_ETAT(ArretEnCours)
default:
printf("Etat inconnu (%d).\n", etat);
break;
}
printf("\n--------------------\n");
printf(" Compilation\n");
printf(" conditionnelle\n");
printf("--------------------\n");
#ifdef DEBUG
// S'utilise : debug( ("Variable x = %d\n", x) ); (double parenthésage)
#define debug(x) printf x
#else
#define debug(x)
#endif
/* si DEBUG a une valeur quelconque
alors debug(x) sera remplacé avant compilation par printf x */
int varX = 12345;
debug( ("1) Variable varX = %d\n", varX) );
#define DEBUG 1
#ifdef DEBUG
// S'utilise : debug( ("Variable x = %d\n", x) ); (double parenthésage)
#define debug(x) printf x
#else
#define debug(x)
#endif
debug( ("2) Variable varX = %d\n", varX) );
#if defined(DEBUG) && defined(PAS_DE_DEBUG)
#error DEBUG et PAS_DE_DEBUG sont définis !
#endif
/* voilà comment tester si plusieurs constantes ont une valeur quelconque
(tester que les constantes sont définies) */
#if 0
// 0 vaut faux donc les lignes entre #if et #endif sont ignorées
// Vous pouvez mettre ce que vous voulez ici, tout sera ignoré, même du code invalide
:-)
#endif
#if 0
// ce qui suit est ignoré
#include "fichier.h"
/* inclut un fichier qui se trouve
d'abord dans le même répertoire que notre fichier de travail
ou sinon dans les répertoires préconfigurés */
#include <fichier.h>
/* inclut un fichier qui se trouve dans les répertoire préconfigurés
c'est à dire /usr/include sous linux
et ceux passés explicitement en paramètre au compilateur. */
#define FICHIER "un_fichier.h"
#include FICHIER
// fonctionne aussi
/* un fichier .h contient normalement
des déclarations de type, macros, prototypes de fonctions
relatif à un module
et pas du code
parce que ces fichiers sont destinés à être inclus
dans plusieurs endroits du programme
et qu'il pourrait y avoir redéfinition (définition en double)
et donc erreurs */
#endif
printf("\n--------------------\n");
printf(" Protection contre\n");
printf(" les inclusions\n");
printf(" multiples\n");
printf("--------------------\n");
#ifndef H_MON_FICHIER
#define H_MON_FICHIER
// Mettre toutes les déclarations ici
#endif
/* explication : si H_MON_FICHIER n'est pas défini
définir H_MON_FICHIER
continuer avec la suite
la prochaine fois tout sera ignoré car H_MON_FICHIER sera déjà défini */
printf("\n--------------------\n");
printf(" Avertissement et\n");
printf(" message d'erreur\n");
printf(" personnalisés\n");
printf("--------------------\n");
#if 0
#error message
// affiche le message d'erreur et arrête la compilation
#endif
#warning message
// fonctionne avec gcc
return 0;
}