Les define



Nous allons découvrir maintenant une nouvelle directive de préprocesseur : le #define.

Cette directive permet de définir une constante de préprocesseur. Cela permet d'associer une valeur à un mot. Voici un exemple :

#define NOMBRE_VIES_INITIALES 3



Vous devez écrire dans l'ordre :

  • le #define ;
  • le mot auquel la valeur va être associée ;
  • la valeur du mot.


Attention : malgré les apparences (notamment le nom que l'on a l'habitude de mettre en majuscules), cela est très différent des constantes que nous avons étudiées jusqu'ici, telles que :

const int NOMBRE_VIES_INITIALES = 3;


Les constantes occupaient de la place en mémoire. Même si la valeur ne changeait pas, votre nombre 3 était stocké quelque part dans la mémoire. Ce n'est pas le cas des constantes de préprocesseur !

Comment ça fonctionne ? En fait, le #define remplace dans votre code source tous les mots par leur valeur correspondante. C'est un peu comme la fonction « Rechercher / Remplacer » de Word si vous voulez. Ainsi, la ligne :

#define NOMBRE_VIES_INITIALES 3


… remplace dans le fichier chaque NOMBRE_VIES_INITIALES par 3.

Voici un exemple de fichier .c avant passage du préprocesseur :

#define NOMBRE_VIES_INITIALES 3


int main(int argc, char *argv[])

{

    int vies = NOMBRE_VIES_INITIALES;


    /* Code ...*/



Après passage du préprocesseur :

int main(int argc, char *argv[])

{

    int vies = 3;


    /* Code ...*/


Avant la compilation, tous les #define auront donc été remplacés par les valeurs correspondantes. Le compilateur « voit » le fichier après passage du préprocesseur, dans lequel tous les remplacements auront été effectués.


Quel intérêt par rapport à l'utilisation de constantes comme on l'a vu jusqu'ici ?


Eh bien, comme je vous l'ai dit, ça ne prend pas de place en mémoire. C'est logique, vu que lors de la compilation il ne reste plus que des nombres dans le code source.

Un autre intérêt est que le remplacement se fait dans tout le fichier dans lequel se trouve le #define. Si vous aviez défini une constante en mémoire dans une fonction, celle-ci n'aurait été valable que dans la fonction puis aurait été supprimée. Le #define en revanche s'appliquera à toutes les fonctions du fichier, ce qui peut s'avérer parfois pratique selon les besoins.

Un exemple concret d'utilisation des #define ?


En voici un que vous ne tarderez pas à utiliser. Lorsque vous ouvrirez une fenêtre en C, vous aurez probablement envie de définir des constantes de préprocesseur pour indiquer les dimensions de la fenêtre :

#define LARGEUR_FENETRE  800

#define HAUTEUR_FENETRE  600


L'avantage est que si plus tard vous décidez de changer la taille de la fenêtre (parce que ça vous semble trop petit), il vous suffira de modifier les #define puis de recompiler.

À noter : les #define sont généralement placés dans des .h, à côté des prototypes (vous pouvez d'ailleurs aller voir les .h des bibliothèques comme stdlib.h, vous verrez qu'il y a des #define !).


Les #define sont donc « faciles d'accès », vous pouvez changer les dimensions de la fenêtre en modifiant les #define plutôt que d'aller chercher au fond de vos fonctions l'endroit où vous ouvrez la fenêtre pour modifier les dimensions. 


C'est donc du temps gagné pour le programmeur. 

En résumé, les constantes de préprocesseur permettent de « configurer » votre programme avant sa compilation. C'est une sorte de mini-configuration.

Un define pour la taille des tableaux


On utilise souvent les define pour définir la taille des tableaux. On écrit par exemple :

#define TAILLE_MAX      1000


int main(int argc, char *argv[])

{

    char chaine1[TAILLE_MAX], chaine2[TAILLE_MAX];

    // ...


Mais… je croyais qu'on ne pouvait pas mettre de variable ni de constante entre les crochets lors d'une définition de tableau ?


Oui, mais TAILLE_MAX n'est PAS une variable ni une constante. En effet je vous l'ai dit, le préprocesseur transforme le fichier avant compilation en :

int main(int argc, char *argv[])

{

    char chaine1[1000], chaine2[1000];

    // ...


… et cela est valide !

En définissant TAILLE_MAX ainsi, vous pouvez vous en servir pour créer des tableaux d'une certaine taille. Si à l'avenir cela s'avère insuffisant, vous n'aurez qu'à modifier la ligne du #define, recompiler, et vos tableaux de char prendront tous la nouvelle taille que vous aurez indiquée.

Calculs dans les define


Il est possible de faire quelques petits calculs dans les define.


Par exemple, ce code crée une constante LARGEUR_FENETRE, une autre HAUTEUR_FENETRE, puis une troisième NOMBRE_PIXELS qui contiendra le nombre de pixels affichés à l'intérieur de la fenêtre (le calcul est simple : largeur * hauteur) :

#define LARGEUR_FENETRE  800

#define HAUTEUR_FENETRE  600

#define NOMBRE_PIXELS    (LARGEUR_FENETRE * HAUTEUR_FENETRE)


La valeur de NOMBRE_PIXELS est remplacée avant la compilation par le code suivant : (LARGEUR_FENETRE * HAUTEUR_FENETRE), c'est-à-dire par (800 * 600), ce qui fait 480000.


Mettez toujours votre calcul entre parenthèses comme je l'ai fait par sécurité pour bien isoler l'opération.

Vous pouvez faire toutes les opérations de base que vous connaissez : addition (+), soustraction (-), multiplication (*), division (/) et modulo (%).

Les constantes prédéfinies


En plus des constantes que vous pouvez définir vous-mêmes, il existe quelques constantes prédéfinies par le préprocesseur.

Chacune de ces constantes commence et se termine par deux symboles underscore _ (vous trouverez ce symbole sous le chiffre 8, tout du moins si vous avez un clavier AZERTY français).

  • __LINE__ : donne le numéro de la ligne actuelle.
  • __FILE__ : donne le nom du fichier actuel.
  • __DATE__ : donne la date de la compilation.
  • __TIME__ : donne l'heure de la compilation.


Ces constantes peuvent être utiles pour gérer des erreurs, en faisant par exemple ceci :

printf("Erreur a la ligne %d du fichier %s\n", __LINE__, __FILE__);

printf("Ce fichier a ete compile le %s a %s\n", __DATE__, __TIME__);



Erreur a la ligne 9 du fichier main.c


Ce fichier a ete compile le Jan 13 2017 a 19:21:10


Les définitions simples


Il est aussi possible de faire tout simplement :

#define CONSTANTE


… sans préciser de valeur.
Cela veut dire pour le préprocesseur que le mot CONSTANTE est défini, tout simplement. Il n'a pas de valeur, mais il « existe ».


Quel peut en être l'intérêt ?


L'intérêt est moins évident que tout à l'heure, mais il y en a un et nous allons le découvrir très rapidement.

Créé avec HelpNDoc Personal Edition: Documentation Qt Help facile