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 ?
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 ?
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).
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 ?
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 :
TRUE
et FALSE
comme (0==0)
et (1==0)
est antérieure à C99.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++.
#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 étant1
. 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
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 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.