IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo

FAQ CConsultez toutes les FAQ

Nombre d'auteurs : 35, nombre de questions : 194, dernière mise à jour : 18 février 2018  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de www.developpez.com et de l'expérience personnelle des auteurs.

Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez devenir rédacteur, lisez ceci .

Sur ce, je vous souhaite une bonne lecture.

SommaireCompilation et édition des liensLe préprocesseur (13)
précédent sommaire suivant
 

Avant d'être effectivement compilés, les fichiers source sont tout d'abord traités par un programme appelé préprocesseur qui se chargera d'exécuter toutes les commandes commençant par # (#include, #define, etc.) jusqu'à ce qu'il n'y en ait plus une seule. C'est à ce moment et à ce moment seulement, c'est-à-dire après passage du préprocesseur si besoin était, que la compilation proprement dite (c'est-à-dire traduction du fichier source en fichier objet) peut enfin être lancée.

Mis à jour le 20 septembre 2004 gl Melem

#define N 10 définit une macro N que le préprocesseur devra remplacer par 10 après son passage. Cela signifie que :

Code c : Sélectionner tout
1
2
3
4
5
6
#define N 10 
  
int f(void) 
{ 
    return N + 1; 
}
Donnera après passage du préprocesseur :

Code c : Sélectionner tout
1
2
3
4
int f(void) 
{ 
    return 10 + 1; 
}

Mis à jour le 20 septembre 2004 gl

Les macros peuvent accepter des paramètres, un peu comme les fonctions. Le nom de la macro doit être immédiatement suivi (sans même un espace) de la parenthèse ouvrante, des paramètres puis de la parenthèse fermante. Vient ensuite le corps de la macro qui peut s'étaler sur plusieurs lignes, chacune sauf la dernière se terminant par un anti-slash : \. Par exemple :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
#define PRINT(x) printf("MESSAGE : %s\n", x) 
  
int main(void) 
{ 
    PRINT("Hello, world !"); 
  
    return 0; 
}
Donnera après passage du préprocesseur :

Code C : Sélectionner tout
1
2
3
4
5
6
int main(void) 
{ 
    printf("MESSAGE : %s\n", "Hello, world !"); 
  
    return 0; 
}

Mis à jour le 20 septembre 2004 gl

#define MYMACRO définit une macro MYMACRO qui sera remplacée, après passage du préprocesseur, par… rien ! Par exemple :

Code c : Sélectionner tout
1
2
3
4
5
6
7
8
9
#define IN 
#define OUT 
  
int f(IN int n, OUT int * p1, OUT int * p2) 
{ 
    *p1 = n - 1; 
    *p2 = n + 1; 
    return 2 * n; 
}
Donnera après passage du préprocesseur :

Code C : Sélectionner tout
1
2
3
4
5
6
int f( int n,  int * p1,  int * p2) 
{ 
    *p1 = n - 1; 
    *p2 = n + 1; 
    return 2 * n; 
}

Mis à jour le 20 septembre 2004 gl Melem

Le test if defined(NOM_MACRO) ou ifdef NOM_MACRO permet de savoir si une macro appelée NOM_MACRO est définie. Voici quelques exemples d'utilisation.

Code c : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h> 
  
#if defined(UPPERCASE) 
#   define MESSAGE "HELLO, WORLD !" 
#else 
#   define MESSAGE "Hello, world !" 
#endif 
  
int main(void) 
{ 
    printf("%s\n", MESSAGE); 
    return 0; 
}
Dans cet exemple, puisque la macro UPPERCASE n'a pas été définie, tout ce qui est situé entre le test et le else sera ignoré (supprimé !) par le préprocesseur. Plus précisément, du bloc if ... else ... endif, il ne restera plus que ce qui se trouvait entre le else et le endif. Ici, MESSAGE sera donc remplacé par le préprocesseur par "Hello, world !". Si UPPERCASE avait cependant été définie, MESSAGE aurait alors été remplacé par "HELLO, WORLD !".

Mis à jour le 23 mars 2009 Melem

Il y en a plusieurs ! Voici quelques points auxquels il faut faire très bien attention lorsqu'on fait usage des macros.

Parenthèses
Soit la macro CARRE dont le rôle est de fournir le carré de la valeur passée en paramètre. Si elle est définie de la sorte : #define CARRE(x) x * x, Son utilisation pour un paramètre tel que 9 + 1 sera erronée. En effet CARRE(9 + 1) sera remplacé par 9 + 1 * 9 + 1 qui en C est équivalent à 9 + (1 * 9) + 1, la multiplication étant prioritaire par rapport à l'addition, ce qui n'est bien entendu pas le carré de 9 + 1. Il convient donc de toujours parenthéser à l'extrême les macros, la nôtre devant s'écrire alors : #define CARRE(x) ((x) * (x)).

Effets de bord :
Soit la macro MAX fournissant le maximum de deux nombres : #define MAX(x, y) ((x) > (y) ? (x) : (y)). Appelée de la manière suivante : k = MAX(3, 2), k aura évidemment la valeur 3. Soient maintenant i et j, deux variables de type int valant toutes 2. On pense alors qu'avec k = MAX(++i, j), k aura encore la valeur 3 comme dans l'exemple précédent alors que cette fois-ci elle aura la valeur 4 ! En effet, k = MAX(++i, j) est remplacé par le préprocesseur par k = ((++i) > (j) ? (++i) : (j)). Comme i a été incrémenté deux fois, on aura k = 4 et non 3 comme il était attendu.

Nom des paramètres :
Une erreur de ce genre peut également arriver : avec #define ERR_PRINT_INT(n) fprintf(stderr, "%d\n", n), ERR_PRINT_INT(10) sera remplacé par fprintf(stderr, "%d\10", 10) !

Mis à jour le 20 septembre 2004 gl

Dans la définition d'une macro, l'opérateur # permet de transformer un argument en chaîne de caractères, quel que soit l'argument. Par exemple :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h> 
  
#define TOSTR(x) #x 
#define NNNNN 99999  
  
int main(void) 
{ 
    printf("%s\n", TOSTR(10000)); /* --> "10000" */ 
    printf("%s\n", TOSTR(1 + 1)); /* --> "1 + 1" */ 
    printf("%s\n", TOSTR(n + 1)); /* --> "n + 1" */ 
    printf("%s\n", TOSTR(float)); /* --> "float" */ 
    printf("%s\n", TOSTR(NNNNN)); /* --> "NNNNN" */ 
  
    return 0; 
}
Comme on peut le constater, TOSTR(NNNNN) est traité par le préprocesseur comme #NNNNN et, sachant qu'il est en train de développer une macro, va remplacer cette séquence en "NNNNN" et non en "99999" ! Pour avoir "99999" au lieu de "NNNNN", on pourra utiliser l'astuce suivante :

Code C : Sélectionner tout
1
2
#define TOSTR(x) __STR(x) 
#define __STR(x) #x
Dans ce cas, au passage du préprocesseur, TOSTR(NNNNN) sera dans un premier temps remplacé par __STR(99999) qui sera à son tour (dans une deuxième passe), remplacé par "99999".

Mis à jour le 29 novembre 2004 gl Melem

Dans la définition d'une macro, l'opérateur ## permet de concaténer deux arguments. Ainsi avec : #define CAT(x, y) x##y, CAT(C, 90) sera remplacé par le préprocesseur par C90. Voici un autre exemple avec programme complet :

Code C : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h> 
#include <wchar.h> 
  
#define WIDESTR(x) L##x 
#define AU_REVOIR "Au revoir" 
  
int main(void) 
{ 
    wprintf(WIDESTR("%s\n"), WIDESTR("Bonjour")); /* --> wprintf(L"%s\n", L"Bonjour"); */ 
  
    return 0; 
}
Par contre, WIDESTR(AU_REVOIR) sera traité par le préprocesseur comme L##AU_REVOIR et, sachant qu'il est en train de développer une macro, va remplacer cette séquence en LAU_REVOIR et non en L"Au revoir" ! Pour avoir L"Au revoir" au lieu de LAU_REVOIR, on pourra utiliser l'astuce suivante :

Code C : Sélectionner tout
1
2
#define WIDESTR(x) ___WSTR(x) 
#define ___WSTR(x) L##x
Dans ce cas, au passage du préprocesseur, WIDESTR(AU_REVOIR) sera dans un premier temps remplacé par ___WSTR("Au revoir") qui sera à son tour (dans une deuxième passe), remplacé par L"Au revoir".

Mis à jour le 29 novembre 2004 Laurent Gomila Melem

La directive pragma permet d'envoyer une commande (par exemple une option de compilation, d'édition des liens, etc.) au compilateur. Ces commandes sont toutefois spécifiques au compilateur. La norme requiert que lorsque le compilateur ne reconnaît pas une commande, il doit tout simplement l'ignorer (les compilateurs émettent généralement un warning pour informer l'utilisateur de ce fait).

Mis à jour le 27 juillet 2008 Melem

La directive error permet d'interrompre la compilation, comme si une erreur s'était produite. Par exemple, pour faire une source qui ne puisse compiler que si la taille d'un char sur la cible est de 8 bits, on peut faire :

Code C : Sélectionner tout
1
2
3
4
5
6
#include <limits.h> 
... 
#if (CHAR_BIT != 8) 
#   error Ce programme requiert que la taille d'un char soit de 8 bits. 
#endif 
...

Mis à jour le 27 juillet 2008 Melem

Non car sizeof (...) est évaluée pendant la compilation, pas avant. L'astuce suivante permet de générer une véritable erreur de compilation lorsqu'un test échoue (ici, on va générer une erreur lorsque sizeof(int) est différent de sizeof(long)).

Code C : Sélectionner tout
1
2
3
/* Si sizeof(int) != sizeof(long), le tableau assert_int_long aura une taille négative, */ 
/* ce qui ne sera pas apprécié par le compilateur ...                                   */ 
static int assert_int_long[sizeof(int) == sizeof(long) ? 1 : -1];

Mis à jour le 27 juillet 2008 gege2061 Melem

__DATE__ est une macro, elle est remplacée par le préprocesseur par la date de compilation du fichier. Elle est donc traitée par le préprocesseur, pas évaluée à l'exécution.

Mis à jour le 20 septembre 2004 gl

Il signifie la plupart du temps que la fonction ou la macro en question n'est pas standard (donc pas très portable…). Plus précisément, qu'elle fait partie de la version de la bibliothèque standard proposée par l'environnement que vous utilisez, mais qu'elle n'est pourtant pas reconnue par la norme. Par exemple, les fonctions et macros DOS/Windows _getch, _kbhit, _spawn*, _P_WAIT, etc. font partie de la bibliothèque du C (appelée CRT sous Windows pour C Run-Time Library) alors qu'elles ne sont pas standard, d'où l'underscore. Cette règle de l'underscore ne s'applique toutefois bien évidemment pas aux fonctions des bibliothèques tierces qui accompagnent le compilateur (API du système et autres bibliothèques spécifiques). Par exemple, sous les systèmes supportant POSIX (c'est-à-dire UNIX et tous ses clones), les fonctions read, write, exec* font partie de l'API POSIX (on les appelle souvent « appels système », car ce sont la plupart du temps des fonctions du système) et non de la bibliothèque du C donc pas besoin d'underscore. L'API POSIX est cependant proposée en tant qu'extension de CRT sous Windows (et elle n'est pas supportée à 100 %…) d'où l'apparition des noms avec underscore (_read, _write, _exec*, etc.).

Cette convention de nommage fut adoptée pour la première fois par le langage C++ et est utilisée aujourd'hui par tous les compilateurs respectant la norme de ce langage (la première version de cette norme date de 1998). Elle n'est pas obligatoire en C, mais beaucoup de compilateurs C (ou C++…) sont en fait des compilateurs C/C++ (c'est-à-dire qu'ils savent compiler aussi bien du code C que du code C++, même si ce sont des langages différents) donc ils appliquent ce système aux deux langages, ce qui n'est pas incompatible avec la norme du C.

Pour les mot-clés non standard, on met deux underscores en avant au lieu d'un seul. Par exemple : __declspec, __stdcall, __asm, __int32, __int64, etc.

Mis à jour le 15 juin 2009 Melem

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.