Programmation C source/préprocesseur
/* 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
/* inclut stdio.h qui décrit
 - la fonction printf */
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);
 // 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;