#define
Je ne vais pas trop m'étaler sur ce paramètre car il est expliqué dans le tuto de M@teo21.
Pour résumer, cette directive sert à définir un nom ou une macro.
La macro suivante définit un nom sans aucun paramètre :
Code : C
La macro suivante définit un nom et associe une valeur à ce nom :
Code : C
Une macro peut être redéfinie autre part dans le programme à la seule condition que celle-ci soit
exactement pareille, seul quelques espaces peuvent être rajoutés. Exemple :
Code : C | /* Incorrect */
#define N 1
#define N 2
|
Code : C | /* Correct */
#define N 1
#define N 1
|
La macro suivante contient 1 paramètre et retourne la valeur absolue de ce paramètre :
Code : C | #define MY_ABS(x) (((x) < 0) ? -(x) : (x))
|
Notez que les parenthèses sont très importantes dans les macros dues à la priorité des opérateurs. Certaines peuvent être omises, d'autres non. Pour être sûr de ne pas avoir de mauvaise surprise je vous conseille d'abuser des parenthèses pour ne laisser aucune ambiguïté possible !
Il faut faire très attention aux effets de bords. Une macro appelée normalement ne pose pas de problèmes, mais on aurait aussi pu appeler notre macro comme ça
MY_ABS(a++). Cette expression évalue 2 fois (a++), ce qui est gênant. Mettre des parenthèses limite déjà certains effets de bords, mais c'est à l'utilisateur de faire attention à ce qu'il fait !
Vous pouvez créer des macros qui portent le même nom qu'une fonction déjà existante. Cependant, il faut bien différencier l'appel de la fonction de celle de la macro, sinon c'est uniquement la macro qui est prise en compte et pas la fonction. Pour utiliser la macro il faut l'appeler comme on le ferait normalement, par contre pour la fonction il faut entourer le nom de parenthèses. Certains puristes diront qu'il faut aussi déclarer votre fonction avec des parenthèses pour ne pas interférer avec les macros.
Code : C 1
2
3
4
5
6
7
8
9
10
11
12 | #include <stdio.h>
#include <stdlib.h>
#define puts(s) printf("Macro : %s\n", (s))
int main(void) {
char s[] = "Salut :-)";
puts(s); /* Macro */
(puts)(s); /* Fonction */
return EXIT_SUCCESS;
}
|
Les macros sur plusieurs lignes
Les macros peuvent être définies sur plusieurs lignes, pour ça il faut terminer la ligne par un antislash (\) et avoir un retour à la ligne juste après. Un antislash non suivit d'un retour la la ligne termine la macro et tout ce qui suit n'est pas compris dedans.
Il est important de noter qu'aucune macro ne peut contenir de directives commençant par
# (y compris la directive nulle). Dès que le préprocesseur rencontre un antislash suivit d'un retour à la ligne il supprime ces 2 là pour en faire une seule ligne. Voilà pourquoi l'utilisation d'autres directives à l'intérieur d'une macro est incorrect.

On peut parfois retrouver cette utilisation pour les chaînes de caractères. Une chaîne de caractère longue peut être 'coupée' par l'antislash + retour à la ligne et être terminée sur la ligne d'après.
Voilà un exemple de ce qu'il faut pas faire :
Code : C | #define MAJEUR(age) \
#if (age >= 18) \
...
|
Un petit exemple pour montrer comment agit le préprocesseur :
Code : C | #define M() 3 + \
2 + 1;
int main(void) {
char s[] = "Bonjour a tous ! Vous etes bien sur le site du zero\
mais comme cette chaine est trop longue je la met sur plusieurs\
lignes. Evitez tout de meme d'utiliser ca, c'est pas tres propre !";
M()
return 0;
}
|
Secret (cliquez pour afficher)Code : Console | int main(void) {
char s[] = "Bonjour a tous ! Vous etes bien sur le site du zero mais comme cette chaine est trop longue je la met sur plusieurs lignes. Evitez tout de meme d'utiliser ca, c'est pas tres propre !";
3 + 2 + 1;
return 0;
} |
Il faut faire attention tout de même aux macros faites sur plusieurs lignes. Le piège étant de mettre une suite d'instruction et d'appeler cette macro dans un if sans accolades, par exemple. Un exemple vaut mieux qu'un long discours :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | #include <stdio.h>
#include <stdlib.h>
#define M(a) \
if ((a) < 0) \
printf("Inferieur a 0\n"); \
if ((a) > 0) \
printf("Superieur a 0\n"); \
if ((a) == 0) \
printf("Egal a 0");
int main(void) {
int a = 5;
if (a < 0)
M(a)
else
printf("Autre instruction");
return EXIT_SUCCESS;
}
|
Ce code bidon et anodin n'affichera pas
Autre instruction mais
Superieur a 0 Autre instruction.
Pour palier ce problème, une astuce assez courante est d'utiliser la boucle
do { ... } while(0); qui permet de regrouper les instructions et éventuellement, d'obliger le programmeur à mettre un point virgule après l'appel de la macro. Pour que le code du dessus soit correct, on aurait dû écrire :
Secret (cliquez pour afficher)Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | #include <stdio.h>
#include <stdlib.h>
#define M(a) \
do { \
if ((a) < 0) \
printf("Inferieur a 0\n"); \
if ((a) > 0) \
printf("Superieur a 0\n"); \
if ((a) == 0) \
printf("Egal a 0"); \
} while(0)
int main(void) {
int a = 5;
if (a < 0)
M(a);
else
printf("Autre instruction");
return EXIT_SUCCESS;
}
|
On peut aussi n'utiliser que les accolades, mais l'utilisation de la boucle sert à bien montrer au programmeur qu'il s'agit d'un bloc d'instructions.
defined
L'opérateur
defined agit au même titre que
#ifdef : il vérifie si la macro existe ; il est remplacé par 1 si elle existe, par 0 le cas échéant. Avec
#if ou
#ifdef on ne peut tester qu'un seul paramètre à la fois, avec l'utilisation de
defined on va pouvoir en tester plusieurs.

L'utilisation se fait avec ou sans parenthèses.
Exemple pour l'implémentation sur différents systèmes d'exploitation :
Code : C | #if defined __APPLE__ || defined linux
# include <unistd.h>
#elif defined ( WIN32 ) || defined ( WIN64 )
# include <windows.h>
#endif
|
Vous trouverez une liste des différentes constantes définies en fonction de l'implémentation
en allant sur ce lien.
* Bonus GCC *
Plutôt que de mettre des
#define un peu partout pour faire des tests (par exemple le
#ifdef DEBUG qu'on retrouve souvent) on peut définir ce paramètre avec l'option
-D de GCC. Avec l'exemple du DEBUG cité précédemment, on a
$ gcc -DDEBUG main.c. Cette commande aura la même action que
#define DEBUG et agira sur l'ensemble du fichier.
Cette astuce fonctionne avec Code::Blocks, il suffit d'aller dans les options de compilation.
#undef
Le
#undef fait exactement l'inverse de ce que fait
#define.

Il supprime ce qui a été défini auparavant !
Utilisation très simple :
Code : C
Où X est le nom à supprimer.