152 votes

Pourquoi #define TRUE (1==1) dans une macro booléenne en C au lieu de simplement indiquer 1 ?

J'ai vu des définitions en C

#define TRUE (1==1)
#define FALSE (!TRUE)

Est-ce nécessaire ? Quel est l'avantage de définir simplement VRAI comme 1, et FAUX comme 0 ?

148voto

SLaks Points 391154

Cette approche utilisera les données réelles boolean (et le résoudre en true et false ) si le compilateur le supporte. (spécifiquement, C++)

Toutefois, il serait préférable de vérifier si le langage C++ est utilisé (via la fonction __cplusplus ) et utiliser réellement true et false .

Dans un compilateur C, ceci est équivalent à 0 et 1 .
(notez que si vous enlevez les parenthèses, cela ne fonctionnera pas en raison de l'ordre des opérations).

131voto

Adam Liss Points 27815

La réponse est la portabilité. Les valeurs numériques de TRUE et FALSE ne sont pas importants. Ce que est important est qu'une déclaration comme if (1 < 2) évalue à if (TRUE) et une déclaration comme if (1 > 2) évalue à if (FALSE) .

Accordé, en C, (1 < 2) évalue à 1 et (1 > 2) évalue à 0 Comme d'autres l'ont dit, il n'y a pas de différence pratique en ce qui concerne le compilateur. Mais en laissant le compilateur définir TRUE et FALSE selon ses propres règles, vous rendez leur signification explicite pour les programmeurs, et vous garantissez la cohérence entre votre programme et toute autre bibliothèque (en supposant que l'autre bibliothèque respecte les standards du C ... vous seriez étonné).


Un peu d'histoire
Quelques BASICs définis FALSE comme 0 et TRUE comme -1 . Comme beaucoup de langues modernes, elles interprétées toute valeur non nulle comme TRUE mais ils évalué les expressions booléennes qui étaient vraies en tant que -1 . Leur NOT a été implémentée en ajoutant 1 et en inversant le signe, parce qu'il était plus efficace de le faire de cette façon. Ainsi, "NOT x" est devenu -(x+1) . Un effet secondaire de ceci est qu'une valeur telle que 5 évalue à TRUE mais NOT 5 évalue à -6 qui est aussi TRUE ! Trouver ce genre de bogue n'est pas amusant.

Meilleures pratiques
Compte tenu de la de facto règles selon lesquelles le zéro est interprété comme FALSE et tout une valeur non nulle est interprétée comme TRUE vous devriez ne compare jamais des expressions d'apparence booléenne à TRUE ou FALSE . Exemples :

if (thisValue == FALSE)  // Don't do this!
if (thatValue == TRUE)   // Or this!
if (otherValue != TRUE)  // Whatever you do, don't do this!

Pourquoi ? Parce que de nombreux programmeurs utilisent le raccourci consistant à traiter int comme bool s. Ce n'est pas la même chose, mais les compilateurs le permettent généralement. Ainsi, par exemple, il est parfaitement légal d'écrire

if (strcmp(yourString, myString) == TRUE)  // Wrong!!!

Ce regarde légitime, et le compilateur l'acceptera volontiers, mais il ne fera probablement pas ce que vous voulez. C'est parce que la valeur de retour de strcmp() est

      0 si yourString == myString
    <0 si yourString < myString
    >0 si yourString > myString

Donc la ligne ci-dessus renvoie TRUE seulement quand yourString > myString .

La bonne façon de procéder est soit

// Valid, but still treats int as bool.
if (strcmp(yourString, myString))

ou

// Better: lingustically clear, compiler will optimize.
if (strcmp(yourString, myString) != 0)

De même :

if (someBoolValue == FALSE)     // Redundant.
if (!someBoolValue)             // Better.
return (x > 0) ? TRUE : FALSE;  // You're fired.
return (x > 0);                 // Simpler, clearer, correct.
if (ptr == NULL)                // Perfect: compares pointers.
if (!ptr)                       // Sleazy, but short and valid.
if (ptr == FALSE)               // Whatisthisidonteven.

Vous trouverez souvent certains de ces "mauvais exemples" dans le code de production, et de nombreux programmeurs expérimentés ne jurent que par eux : ils fonctionnent, certains sont plus courts que leurs alternatives (pédantesques ?) correctes, et les idiomes sont presque universellement reconnus. Mais attention : les "bonnes" versions ne sont pas moins efficaces, elles sont garanties portables, elles passeront même les linters les plus stricts, et même les nouveaux programmeurs les comprendront.

Cela n'en vaut-il pas la peine ?

50voto

Kaz Points 18072

Le site (1 == 1) est utile pour définir TRUE d'une manière qui est transparente pour le C, tout en offrant un meilleur typage en C++. Le même code peut être interprété comme C ou C++ si vous écrivez dans un dialecte appelé "Clean C" (qui se compile soit en C soit en C++) ou si vous écrivez des fichiers d'en-tête d'API qui peuvent être utilisés par les programmeurs C ou C++.

En unités de traduction C, 1 == 1 a exactement la même signification que 1 ; et 1 == 0 a la même signification que 0 . Cependant, dans les unités de traduction C++ , 1 == 1 a un type bool . Ainsi, le TRUE Une macro définie de cette manière s'intègre mieux dans le C++.

Un exemple de cette meilleure intégration est que, par exemple, si la fonction foo a des surcharges pour int et pour bool alors foo(TRUE) choisira le bool surcharge. Si TRUE est simplement défini comme 1 alors il ne fonctionnera pas correctement dans le C++. foo(TRUE) voudront le int surcharge.

Bien sûr, C99 a introduit bool , true et false et ceux-ci peuvent être utilisés dans les fichiers d'en-tête qui fonctionnent avec C99 et avec C.

Cependant :

  • cette pratique consistant à définir TRUE et FALSE comme (0==0) et (1==0) est antérieure à C99.
  • il y a encore de bonnes raisons de rester à l'écart de C99 et de travailler avec C90.

Si vous travaillez dans un projet mixte C et C++, et que vous ne voulez pas du C99, définissez les minuscules de l'option true , false et bool à la place.

#ifndef __cplusplus
typedef int bool;
#define true (0==0)
#define false (!true)
#endif

Ceci étant dit, le 0==0 a été (est ?) utilisé par certains programmeurs même dans du code qui n'a jamais été destiné à interagir avec C++ de quelque manière que ce soit. Cela n'apporte rien et suggère que le programmeur a une mauvaise compréhension de la façon dont les booléens fonctionnent en C.


Au cas où l'explication en C++ n'aurait pas été claire, voici un programme de test :

#include <cstdio>

void foo(bool x)
{
   std::puts("bool");  
}

void foo(int x)
{
   std::puts("int");  
}

int main()
{
   foo(1 == 1);
   foo(1);
   return 0;
}

Le résultat :

bool
int

Pour ce qui est de la question posée dans les commentaires sur la pertinence des fonctions C++ surchargées pour la programmation mixte C et C++. Elles ne font qu'illustrer une différence de type. Une raison valable pour vouloir une fonction true constante pour être bool lorsqu'il est compilé en C++ est pour des diagnostics propres. Au niveau d'alerte le plus élevé, un compilateur C++ peut nous avertir d'une conversion si nous passons un entier en tant que bool paramètre. Une raison d'écrire en Clean C est non seulement que notre code est plus portable (puisqu'il est compris par les compilateurs C++, et pas seulement par les compilateurs C), mais nous pouvons bénéficier des avis diagnostiques des compilateurs C++.

18voto

ouah Points 75311
#define TRUE (1==1)
#define FALSE (!TRUE)

est équivalent à

#define TRUE  1
#define FALSE 0

en C.

Le résultat des opérateurs relationnels est 0 ou 1 . 1==1 est garanti d'être évalué à 1 et !(1==1) est garanti d'être évalué à 0 .

Il n'y a absolument aucune raison d'utiliser la première forme. Notez que la première forme n'est cependant pas moins efficace car sur presque tous les compilateurs, une expression constante est évaluée au moment de la compilation plutôt qu'au moment de l'exécution. Ceci est autorisé par la règle suivante :

(C99, 6.6p2) "Une expression constante peut être évaluée pendant la traduction plutôt que pendant l'exécution, et par conséquent peut être utilisée à n'importe quel endroit où une constante peut être."

PC-Lint va même émettre un message (506, valeur constante booléenne) si vous n'utilisez pas un littéral pour TRUE et FALSE macros :

Pour C, TRUE devrait être défini comme étant 1 . Cependant, d'autres langages utilisent des quantités autres que 1, de sorte que certains programmeurs pensent que !0 joue la sécurité.

Toujours dans C99, le stdbool.h définitions des macros booléennes true et false utiliser directement des littéraux :

#define true   1
#define false  0

12voto

sh1 Points 1477

Outre le C++ (déjà mentionné), un autre avantage concerne les outils d'analyse statique. Le compilateur éliminera toute inefficacité, mais un analyseur statique peut utiliser ses propres types abstraits pour distinguer les résultats de la comparaison des autres types d'entiers, de sorte qu'il sait implicitement que TRUE doit être le résultat d'une comparaison et ne doit pas être supposé compatible avec un entier.

Évidemment, C dit qu'ils sont compatibles, mais vous pouvez choisir d'interdire l'utilisation délibérée de cette fonctionnalité pour aider à mettre en évidence les bogues -- par exemple, lorsque quelqu'un pourrait avoir confondu & et && ou ils se sont trompés dans l'ordre des opérateurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X