Comparaison de macro avec directive if

25

Pourquoi la #ifcondition du code suivant est-elle remplie:

#include <iostream>
#define VALUE foo    

int main() {    
#if VALUE == bar
    std::cout << "WORKS!" << std::endl;
#endif // VALUE
}
michalt38
la source

Réponses:

26

La page sur cppreference.com déclare:

Après toute expansion de macro et évaluation d'expressions définies et __has_include (depuis C ++ 17), tout identifiant qui n'est pas un littéral booléen est remplacé par le nombre 0 (cela inclut les identifiants qui sont des mots-clés lexicaux, mais pas des jetons alternatifs comme et ).

Donc, les deux fooet barsont remplacés par 0.

BessieTheCow
la source
Qu'en est-il de C ++ 98, C ++ 03, C ++ 11 et C ++ 14?
kelalaka
1
@kelalaka C'est tout de même. Il en est ainsi depuis C89.
SS Anne
1
@kelalaka Le "depuis C ++ 17" fait uniquement référence à la partie "et __has_include". Avant C ++ 17, c'était la même chose, juste avec cette partie omise.
BessieTheCow
15

Dans une #ifinstruction, tout identifiant qui reste après la substitution de macro (à l'exception de trueet false) est remplacé par la constante 0. Donc votre directive devient

#if 0 == 0

ce qui est vrai.

1201ProgramAlarm
la source
Pour clarifier, '#define VALUE foo' définit le symbole 'VALUE' à résoudre à la même valeur que le symbole 'foo', mais le symbole 'foo' n'a aucune signification, il sera donc interprété comme 0.
ManicDee
14

En effet, ni aucune définition ni valeur foone leur baront été attribuées - elles sont donc identiques (c'est-à-dire remplacées par une valeur "0"). Les compilateurs donneront des avertissements à ce sujet.

Le MSVCcompilateur (Visual Studio 2019) donne ce qui suit:

avertissement C4668: «foo» n'est pas défini comme une macro de préprocesseur, remplaçant par «0» pour «# if / # elif»
avertissement C4668: «bar» n'est pas défini comme une macro de préprocesseur, remplaçant par «0» pour «#if / # elif '

Ainsi, VALUEla valeur «0» est donnée (par défaut pour foo) et a barégalement «0», donc est VALUE == barévalué à «VRAI».

De même, clang-cldonne ce qui suit:

avertissement: 'foo' n'est pas défini, évalue à 0 [-Wundef]
avertissement: 'bar' n'est pas défini, évalue à 0 [-Wundef]

Adrian Mole
la source
L'avertissement est-il obligatoire ou est-il une caractéristique des compilateurs?
kelalaka
1
@kelalaka À ma connaissance, aucun «avertissement» du compilateur n'est obligatoire! Même avec les compilateurs MSVCet clang-cl, cet avertissement peut être désactivé (soit spécifiquement, soit en définissant un «niveau» d'avertissement approprié).
Adrian Mole
0

Pour accomplir ce que vous recherchez, essayez ceci:

#include <iostream>
#define DEBUG  

int main() {    
#ifdef DEBUG
    std::cout << "WORKS!" << std::endl;
#endif
}

Dans ce cas, vous pouvez désactiver les instructions de débogage en remplaçant "define" par "undef".

#include <iostream>
#undef DEBUG  

int main() {    
#ifdef DEBUG
    std::cout << "WORKS!" << std::endl;
#endif
}

Vous pourriez trouver que votre compilateur vous permet de définir DEBUG en dehors du code lui-même, à quel point vous pouvez réduire le code à

#include <iostream>

int main() {    
#ifdef DEBUG
    std::cout << "WORKS!" << std::endl;
#endif
}

Et puis appelez le compilateur avec une option telle que -DDEBUG = 0

Consultez le chapitre sur la programmation défensive dans Steve McConnell, «Code Complete».

ManicDee
la source