• Aucun résultat trouvé

Compilation conditionnelle

Dans le document Conservatoire National des Arts et Métiers (Page 181-187)

adresses croissantes

9. Les fichiers

10.10 Compilation conditionnelle

Les mécanismes de compilation conditionnelle ont pour but de compiler ou d’ignorer certaines parties du programme, en fonction d’un test effectué à la compilation. La commande du préprocesseur permettant de réaliser la compilation conditionnelle est la commande #if qui peut prendre plusieurs formes :

• Le #if simple. Quand le préprocesseur rencontre :

#if condition

ensemble_lignes_de_code

#endif

il évalue la condition. Si la condition est vraie, les lignes de code sont compilées sinon, elles sont ignorées.

• Le #if avec #else. Le else permet de compiler deux blocs de lignes différents suivant l’état de la condition :

#if condition

ensemble_lignes_de_code_1

#else

ensemble_lignes_de_code_2

#endif

• Le #if avec #elif. Pour réaliser plusieurs branches, on utilise le #elif qui est équivalent à un else if.

#if condition1

ensemble_lignes_de_code_1

#elif condition2

ensemble_lignes_de_code_2

#elif condition3

ensemble_lignes_de_code_3

#else

ensemble_lignes_de_code_4

#endif

Dans les 3 cas précédents, il est possible de remplacer les #if par des #ifdef ou

#ifndef :

#ifdef nom_de_macro ou bien

#ifndef nom_de_macro

Dans ce cas, le test porte sur la définition ou non d’une macro (et non sur sa valeur). #ifdef nom_de_macro est équivalent à « si la macro est définie » et #ifndef nom_de_macro est équivalent à « si la macro n’est pas définie ». Une autre possibilité existe, l’utilisation de l’opérateur defined associé à un #if. Elle est similaire au #ifdef, mais elle permet en plus de tester plusieurs macros en même temps.

#if defined(macro1) || defined(macro2)

Un cas classique d’utilisation de la compilation conditionnelle est d’éviter plusieurs inclusions d’un même fichier d’entête .h. Cela arrive notamment dans le cas d’un projet complexe comprenant plusieurs fichiers sources. Pour être certain d’inclure un fichier une seule fois, il suffit d’utiliser le mécanisme suivant :

/* fichier exemple.h */

#ifndef EXEMPLE_H

#define EXEMPLE_H

/* corps du fichier exemple.h */

...

#endif

Dans cet exemple, le fichier exemple.h ne peut être inclus qu’une fois dans un source. En effet, la macro EXEMPLE_H n’est définie que dans ce fichier. Lors du premier passage du préprocesseur, la variable étant indéfinie, celui-ci analyse l’intégralité du fichier et définit la macro. Si ce fichier est à nouveau inclus dans le source par erreur, le préprocesseur ne le lira pas car la macro EXEMPLE_H existe déjà. Il faut noter que l’on aurait aussi pu utiliser un defined :

/* fichier exemple.h */

#if !defined(EXAMPLE_H)

#define EXEMPLE_H

/* corps du fichier exemple.h */

...

#endif

La commande #error permet d’afficher un message d’erreur lors de la compilation. Cette commande a pour intérêt de vérifier des conditions permettant au programme de s’exécuter sur ce système d’exploitation. Voici un exemple où on vérifie que la taille des entiers est suffisante :

#include <limits.h>

#if INT_MAX < 1000000

#error " entiers trop petits sur cette machine"

#endif

Une des applications traditionnelle de la compilation conditionnelle est l’adaptation d’un programme à un environnement comme WINDOWS ou LINUX. Par exemple, dans le projet image, la commande système permettant la copie d’un fichier était différente entre les deux systèmes d’exploitation. La macro WIN32 étant systématiquement définie sous Visual C++, il suffit donc d’écrire les lignes suivantes pour régler définitivement le problème :

#ifdef WIN32

system("copy toto tutu");

#else

system("cp toto tutu");

#endif

De la même manière, il est parfois commode d’avoir une version d’un programme en mode Debug avec beaucoup de printf pour surveiller l’évolution des variables et une version normale sans ces printf. Voici par exemple, le projet mastermind :

main() {

int ref[NBCHIFFRES], essai[NBCHIFFRES];

int NbPos, NbChif, NbCoup, i, status;

NbCoup = 0;

tirage(ref, NBCHIFFRES);

#ifdef DEBUG

for (i = 0 ; i < NBCHIFFRES ; i++) printf("ref[%d]=%d", i, ref[i]);

#endif

do {

do {

status = entree(essai, NBCHIFFRES);

} ...

Sous Linux, si vous compilez le programme avec :

cc -DDEBUG master.c -o master

vous allez visualiser le tirage alors que si vous compilez normalement :

cc master.c -o master

le tirage n’apparaît plus. Il est à noter qu’avec Visual C++, une variable _DEBUG est définie automatiquement par le compilateur quand vous êtes en mode Debug mais qu’elle ne l’est plus quand vous êtes en mode Release (version finale).

Exercice 10.15 : le fichier suivant est un exemple réel de fichier d’entête, limits.h. Il indique à Visual C++ les limitations numériques des variables qui dépendent du système d’exploitation.

Expliquez son fonctionnement. Faites attention à bien repérer les paires #if ... #endif.

/***

*limits.h - implementation dependent values

*

* Copyright(c)1985-1997, Microsoft Corporation. All rights reserved.

*

* Purpose:

* Contains defines for a number of implementation dependent values

* which are commonly used in C programs. [ANSI]

****/

#if _MSC_VER > 1000

#pragma once

#endif

#ifndef _INC_LIMITS

#define _INC_LIMITS

#if !defined(_WIN32) && !defined(_MAC)

#error ERROR: Only Mac or Win32 targets supported!

#endif

#define CHAR_MAX UCHAR_MAX

#endif /* _CHAR_UNSIGNED */

#define MB_LEN_MAX 2 /* max. # bytes in multibyte char */

#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */

#define LONG_MAX 2147483647L /* maximum (signed) long value */

#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */

#if _INTEGRAL_MAX_BITS >= 8

#define _I32_MIN (-2147483647i32-1) /* minimum signed 32 bit value */

#define _I32_MAX 2147483647i32 /* maximum signed 32 bit value */

#define _UI32_MAX 0xffffffffui32 /* maximum unsigned 32 bit value */

#endif

#if _INTEGRAL_MAX_BITS >= 64 /* minimum signed 64 bit value */

#define _I64_MIN (-9223372036854775807i64 - 1) /* maximum signed 64 bit value */

#define _I64_MAX 9223372036854775807i64 /* maximum unsigned 64 bit value */

#define _UI64_MAX 0xffffffffffffffffui64

#endif

#if _INTEGRAL_MAX_BITS >= 128 /* minimum signed 128 bit value */

#define _I128_MIN (-170141183460469231731687303715884105727i128 - 1) /* maximum signed 128 bit value */

#define _I128_MAX 170141183460469231731687303715884105727i128 /* maximum unsigned 128 bit value */

#define _UI128_MAX 0xffffffffffffffffffffffffffffffffui128

#endif

#ifdef _POSIX_

#define _POSIX_ARG_MAX 4096

#define _POSIX_CHILD_MAX 6

#define _POSIX_LINK_MAX 8

#define _POSIX_MAX_CANON 255

#define _POSIX_MAX_INPUT 255

#define _POSIX_NAME_MAX 14

#define _POSIX_NGROUPS_MAX 0

#define _POSIX_OPEN_MAX 16

#define _POSIX_PATH_MAX 255

#define _POSIX_PIPE_BUF 512

#define _POSIX_SSIZE_MAX 32767

#define _POSIX_STREAM_MAX 8

#define _POSIX_TZNAME_MAX 3

#define ARG_MAX 14500 /* 16k heap, minus overhead */

Dans le document Conservatoire National des Arts et Métiers (Page 181-187)

Documents relatifs