web-dev-qa-db-fra.com

Pourquoi #define TRUE (1 == 1) dans une macro booléenne C au lieu de simplement 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 TRUE comme 1 et FAUX comme 0?

158
user2468316

Cette approche utilisera le type boolean actuel (et résoudra en true et false) si le compilateur le prend en charge. (spécifiquement, C++)

Cependant, il serait préférable de vérifier si C++ est utilisé (via le __cplusplus macro) et utilise réellement true et false.

Dans un compilateur C, cela équivaut à 0 et 1.
[. ____]

153
SLaks

La réponse est la portabilité. Les valeurs numériques de TRUE et FALSE ne sont pas importantes. Ce qui est important est qu’une déclaration telle que if (1 < 2) est évaluée à if (TRUE) et une instruction telle que if (1 > 2) est évalué à if (FALSE).

Certes, en C, (1 < 2) Est évalué à 1 Et (1 > 2) Est évalué à 0, Ainsi, comme d'autres l'ont déjà dit, il n'y a pas de différence pratique en ce qui concerne le compilateur est concerné. Mais en laissant le compilateur définir TRUE et FALSE selon ses propres règles, vous explicitez leur signification pour les programmeurs, et vous garantissez la cohérence de votre programme et de toute autre bibliothèque (en supposant l’autre bibliothèque suit les normes C (vous seriez surpris).


n peu d'histoire
Certains BASIC ont défini FALSE comme 0 Et TRUE comme -1. Comme beaucoup de langues modernes, ils interprètent toute valeur autre que zéro sous la forme TRUE, mais ils évaluent expressions booléennes qui étaient vraies comme -1. Leur opération NOT a été implémentée en ajoutant 1 et en retournant le signe, car il était efficace de le faire de cette façon. Donc 'NOT x' est devenu -(x+1). Un effet secondaire de ceci est qu'une valeur comme 5 Est évaluée à TRUE, mais que NOT 5 Est évaluée à -6, Qui est également TRUE ! Trouver ce genre de bogue n'est pas amusant.

Meilleures pratiques
Étant donné les règles de facto , zéro est interprété comme étant FALSE et une valeur non nulle est interprétée comme TRUE, vous devriez ne jamais comparer d'expressions d'aspect booléen à 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 beaucoup de programmeurs utilisent le raccourci pour traiter ints comme bools. Ils ne sont pas identiques, mais les compilateurs le permettent généralement. Ainsi, par exemple, il est parfaitement légal d'écrire

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

Cela a l'air légitime, et le compilateur l'acceptera avec joie, mais il ne fera probablement pas ce que vous voudriez. C'est parce que la valeur de retour de strcmp() est

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

La ligne ci-dessus ne renvoie donc TRUE que lorsque yourString > myString.

La bonne façon de faire 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 quelques-uns 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 correctes (pédantiquement?), Et les idiomes sont presque universellement reconnus. Mais considérons que les "bonnes" versions ne sont pas moins efficaces, elles sont garanties d’être portables, elles vont même supporter les linters les plus stricts, et même les nouveaux programmeurs les comprendront.

N'est-ce pas la peine?

136
Adam Liss

L'astuce (1 == 1) Est utile pour définir TRUE d'une manière transparente pour C, tout en permettant une meilleure saisie en C++. Le même code peut être interprété en C ou C++ si vous écrivez dans un dialecte appelé "Clean C" (qui compile en C ou C++) ou si vous écrivez des fichiers d'en-tête d'API pouvant être utilisés par les programmeurs C ou C++.

Dans les 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. Toutefois, dans les unités de traduction C++, 1 == 1 Est de type bool. La macro TRUE ainsi définie s’intègre donc mieux dans C++.

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, Cela ne fonctionnera pas bien en C++. foo(TRUE) voudra la surcharge int.

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

Toutefois:

  • cette pratique consistant à définir TRUE et FALSE comme (0==0) et (1==0) est antérieure à C99.
  • il y a toujours de bonnes raisons de ne pas utiliser C99 et de travailler avec C90.

Si vous travaillez dans un projet mixte C et C++ et que vous ne voulez pas C99, définissez plutôt les minuscules true, false et bool.

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

Cela étant dit, l'astuce 0==0 Était (est?) Utilisée par certains programmeurs même dans du code qui n'avait jamais été conçu pour interagir avec C++. Cela n'achète rien et suggère que le programmeur comprend mal le fonctionnement des booléens en C


Au cas où l'explication C++ n'était pas 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

En ce qui concerne la question des commentaires sur la surcharge des fonctions C++ relatives à la programmation mixte C et C++. Celles-ci illustrent simplement une différence de type. Une raison valable de vouloir qu'une constante true soit bool lorsqu'elle est compilée en tant que C++ est destinée aux diagnostics clairs. Aux niveaux d'avertissement les plus élevés, un compilateur C++ peut nous avertir d'une conversion si nous passons un entier sous la forme d'un paramètre bool. Une raison d'écrire dans Clean C n'est pas seulement que notre code est plus portable (puisqu'il est compris par les compilateurs C++, pas seulement les compilateurs C), mais nous pouvons également bénéficier des avis de diagnostic des compilateurs C++.

50
Kaz
#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. Il est garanti que 1==1 Sera évalué à 1 Et que !(1==1) sera évalué à 0.

Il n'y a absolument aucune raison d'utiliser le premier formulaire. Notez que le premier formulaire 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é selon cette règle:

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

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

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 estiment que !0 Joue la sécurité.

Également en C99, les définitions stdbool.h Des macros booléennes true et false utilisent directement des littéraux:

#define true   1
#define false  0
18
ouah

Outre le C++ (déjà mentionné), un autre avantage concerne les outils d'analyse statique. Le compilateur supprimera toute inefficacité, mais un analyseur statique peut utiliser ses propres types abstraits pour faire la distinction entre les résultats de comparaison et les autres types entiers. Il sait donc implicitement que TRUE doit être le résultat d'une comparaison et ne doit pas être considéré comme 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 ont gâché leur priorité d'opérateur.

12
sh1

La différence pratique est nulle. 0 est évalué à false et 1 est évalué à true. Le fait que vous utilisiez un expression booléenne (1 == 1) ou 1, pour définir true, ne fait aucune différence. Ils sont tous deux évalués à int.

Notez que la bibliothèque standard C fournit un en-tête spécifique pour la définition de booléens: stdbool.h.

3
Shoe

Nous ne savons pas quelle est la valeur exacte de TRUE et les compilateurs peuvent avoir leurs propres définitions. Vous devez donc utiliser l’interne du compilateur pour la définition. Ce n'est pas toujours nécessaire si vous avez de bonnes habitudes de programmation mais que vous pouvez éviter les problèmes de mauvais style de codage, par exemple:

si ((a> b) == VRAI)

Cela pourrait être un désastre si vous définissez manuellement TRUE sur 1, tandis que la valeur interne de TRUE en est une autre.

3
capiggue
  1. Élément de liste

Généralement, dans le langage de programmation C, 1 est défini comme vrai et 0 comme faux. C'est pourquoi vous voyez souvent ce qui suit:

#define TRUE 1 
#define FALSE 0

Toutefois, tout nombre non égal à 0 serait également considéré comme vrai dans une instruction conditionnelle. Par conséquent, en utilisant ce qui suit:

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

Vous pouvez simplement montrer explicitement que vous essayez de jouer la sécurité en rendant faux égal à ce qui n'est pas vrai.

2
Sabashan Ragavan