C n'a pas de types booléens intégrés. Quelle est la meilleure façon de les utiliser en C?
C n'a pas de types booléens intégrés. Quelle est la meilleure façon de les utiliser en C?
Réponses:
Du meilleur au pire:
Option 1 (C99)
#include <stdbool.h>
Option 2
typedef enum { false, true } bool;
Option 3
typedef int bool;
enum { false, true };
Option 4
typedef int bool;
#define true 1
#define false 0
Si vous êtes indécis, optez pour le # 1!
<stdbool.h>
bool
le compilateur choisit peuvent être plus adaptés à l'objectif prévu d'une valeur booléenne que d'utiliser un int
(c'est-à-dire que le compilateur peut choisir d'implémenter un bool
différemment d'un int
). Cela peut également entraîner une vérification de type plus stricte au moment de la compilation, si vous êtes chanceux.
int
pour bool
? C'est du gaspillage. Utilisez unsigned char
. Ou utiliser le C de builtin _Bool
.
Quelques réflexions sur les booléens en C:
Je suis assez vieux pour utiliser simplement des int
s comme mon type booléen sans aucun typedefs ou définitions ou énumérations spéciales pour les valeurs true / false. Si vous suivez ma suggestion ci-dessous sur ne jamais comparer avec les constantes booléennes, alors vous n'avez qu'à utiliser 0/1 pour initialiser les indicateurs de toute façon. Cependant, une telle approche peut être jugée trop réactionnaire en ces temps modernes. Dans ce cas, il faut absolument l'utiliser <stdbool.h>
car il a au moins l'avantage d'être standardisé.
Quel que soit le nom des constantes booléennes, utilisez-les uniquement pour l'initialisation. N'écris jamais quelque chose comme
if (ready == TRUE) ...
while (empty == FALSE) ...
Ceux-ci peuvent toujours être remplacés par le plus clair
if (ready) ...
while (!empty) ...
Notez que ceux-ci peuvent en fait être lus à haute voix de façon raisonnable et compréhensible.
Donnez à vos variables booléennes des noms positifs, c'est-à-dire full
au lieu de notfull
. Ce dernier conduit à un code difficile à lire facilement. Comparer
if (full) ...
if (!full) ...
avec
if (!notfull) ...
if (notfull) ...
Les deux anciens couples lisent naturellement, tandis qu'ils !notfull
sont difficiles à lire, même s'ils le sont, et deviennent bien pires dans les expressions booléennes plus complexes.
Les arguments booléens doivent généralement être évités. Considérons une fonction définie comme ceci
void foo(bool option) { ... }
Dans le corps de la fonction, il est très clair ce que l'argument signifie, car il a un nom pratique et, espérons-le, significatif. Mais, les sites d'appels ressemblent
foo(TRUE);
foo(FALSE):
Ici, il est essentiellement impossible de dire ce que signifiait le paramètre sans toujours regarder la définition ou la déclaration de fonction, et cela devient bien pire dès que vous ajoutez encore plus de paramètres booléens. Je propose soit
typedef enum { OPT_ON, OPT_OFF } foo_option;
void foo(foo_option option);
ou
#define OPT_ON true
#define OPT_OFF false
void foo(bool option) { ... }
Dans les deux cas, le site d'appel ressemble maintenant à
foo(OPT_ON);
foo(OPT_OFF);
que le lecteur a au moins une chance de comprendre sans en tirer la définition foo
.
a == b
marche pas ?
a
et en b
comptant à partir de zéro, je recommanderais a > 0 == b > 0
plutôt. Si vous insistez pour tirer parti de la véracité des valeurs arbitraires non nulles, !!var
la valeur booléenne 0/1 est équivalente à var
, vous pouvez donc écrire !!a == !!b
, bien que beaucoup de lecteurs trouveront cela déroutant.
!a == !b
est également suffisant pour tester l'égalité, les non-zéros deviennent nuls et les zéros deviennent un.
!!a
"convertir non-booléen a en sa valeur de vérité équivalente", alors que je lirais !a
"inverser logiquement la variable booléenne a". En particulier, je chercherais une raison précise pour laquelle l'inversion logique était souhaitée.
Un booléen en C est un entier: zéro pour faux et non nul pour vrai.
Voir aussi Type de données booléen , section C, C ++, Objective-C, AWK .
Voici la version que j'ai utilisée:
typedef enum { false = 0, true = !false } bool;
Parce que false n'a qu'une seule valeur, mais un vrai logique peut avoir plusieurs valeurs, mais la technique définit true pour être ce que le compilateur utilisera pour l'opposé de false.
Cela résout le problème de quelqu'un codant quelque chose qui se résumerait à ceci:
if (true == !false)
Je pense que nous serions tous d'accord pour dire que ce n'est pas une bonne pratique, mais pour le coût unique de faire "vrai =! Faux", nous éliminons ce problème.
[EDIT] Au final, j'ai utilisé:
typedef enum { myfalse = 0, mytrue = !myfalse } mybool;
pour éviter la collision de noms avec d'autres schémas qui définissaient true
et false
. Mais le concept reste le même.
[EDIT] Pour afficher la conversion de l'entier en booléen:
mybool somebool;
int someint = 5;
somebool = !!someint;
Le premier (le plus à droite)! convertit l'entier non nul en 0, puis le deuxième (le plus à gauche)! convertit le 0 en myfalse
valeur. Je vais laisser comme exercice au lecteur de convertir un entier nul.
[EDIT] C'est mon style d'utiliser le réglage explicite d'une valeur dans une énumération lorsque la valeur spécifique est requise même si la valeur par défaut serait la même. Exemple: parce que false doit être nul j'utilise false = 0,
plutôt quefalse,
true
, false
et bool
sont mises en évidence dans la plupart des IDE parce qu'ils sont des valeurs enum et un typedef, par opposition à #defines
, qui sont rarement la coloration syntaxique.
gcc -ansi -pedantic -Wall
ne donne aucun avertissement, donc j'ai confiance gcc
; Si cela fonctionne même pour c89
cela, cela devrait fonctionner c99
aussi.
!
ne peut renvoyer que des valeurs 0
et 1
, par conséquent true = !false
, affectera toujours la valeur 1. Cette méthode ne donne aucune sécurité supplémentaire typedef enum { false, true } bool;
.
if(!value)
), mais cette exception n'est pas applicable dans ce cas spécifique.
Si vous utilisez un compilateur C99, il prend en charge les types booléens:
#include <stdbool.h>
int main()
{
bool b = false;
b = true;
}
Tout d'abord. C, c'est-à-dire que l'ISO / CEI 9899 a un type booléen depuis 19 ans maintenant . C'est beaucoup plus de temps que la durée prévue de la carrière de programmation C avec des parties amateur / académique / professionnel combinées lors de la visite de cette question . Le mien dépasse cela de peut-être 1-2 ans. Cela signifie que pendant le temps qu'un lecteur moyen a appris quoi que ce soit sur C, C a en fait eu le type de données booléen .
Pour le type de données,, #include <stdbool.h>
et utilisez true
, false
et bool
. Ou ne l'incluez pas, et utilisez _Bool
, 1
et à la 0
place.
Il existe diverses pratiques dangereuses promues dans les autres réponses à ce fil. Je vais les aborder:
typedef int bool;
#define true 1
#define false 0
Ce n'est pas non, car un lecteur occasionnel - qui a appris le C au cours de ces 19 années - s'attendrait à ce que cela se bool
réfère au type de données réel bool
et se comporte de la même manière, mais ce n'est pas le cas! Par exemple
double a = ...;
bool b = a;
Avec C99 bool
/ _Bool
, b
serait réglé sur false
si siff a
était zéro, et true
sinon. C11 6.3.1.2p1
- Lorsqu'une valeur scalaire est convertie en
_Bool
, le résultat est 0 si la valeur se compare à 0; sinon, le résultat est 1. 59)Notes de bas de page
59) Les NaN ne sont pas comparables à 0 et se convertissent donc à 1.
Avec le typedef
en place, le double
serait contraint à un int
- si la valeur du double n'est pas dans la plage de int
, le comportement n'est pas défini .
Naturellement, il en va de même si true
et false
ont été déclarés dans un enum
.
Ce qui est encore plus dangereux, c'est de déclarer
typedef enum bool {
false, true
} bool;
car maintenant toutes les valeurs en plus de 1 et 0 ne sont pas valides et si une telle valeur était affectée à une variable de ce type, le comportement serait totalement indéfini .
Par conséquent, si vous ne pouvez pas utiliser C99 pour une raison inexplicable, pour les variables booléennes, vous devez utiliser:
int
et valeurs 0
et 1
tels quels ; et effectuez soigneusement les conversions de domaine à partir de toute autre valeur vers celles-ci avec double négation!!
BOOL
, TRUE
et FALSE
!int
ou unsigned int
pour contenir une énumération, mais je ne connais rien dans la norme qui ferait en sorte qu'une énumération se comporte autrement que comme un entier type.
typedef enum {
false = 0,
true
} t_bool;
!0 = 1
par la norme C, et !a = 0
pour toute valeur non nulle de a
. Le problème est que tout non différent de zéro est considéré comme vrai. Donc, si a
et b
sont tous les deux "vrais", ce n'est pas nécessairement le cas que `a == b`.
C a un type booléen: bool (au moins pour les 10 (!) Dernières années)
Incluez stdbool.h et true / false fonctionnera comme prévu.
bool
être une macro qui se développe _Bool
. La différence est importante car vous pouvez #undef
une macro (et c'est autorisé, au moins à titre de mesure transitoire), mais vous ne pouvez pas untypedef
un typedef. Cependant, cela ne modifie pas l'orientation principale de votre premier commentaire.
Tout ce qui n'est pas nul est évalué comme vrai dans les opérations booléennes, vous pouvez donc simplement
#define TRUE 1
#define FALSE 0
et utiliser les constantes.
Juste un complément à d'autres réponses et quelques précisions, si vous êtes autorisé à utiliser C99.
+-------+----------------+-------------------------+--------------------+
| Name | Characteristic | Dependence in stdbool.h | Value |
+-------+----------------+-------------------------+--------------------+
| _Bool | Native type | Don't need header | |
+-------+----------------+-------------------------+--------------------+
| bool | Macro | Yes | Translate to _Bool |
+-------+----------------+-------------------------+--------------------+
| true | Macro | Yes | Translate to 1 |
+-------+----------------+-------------------------+--------------------+
| false | Macro | Yes | Translate to 0 |
+-------+----------------+-------------------------+--------------------+
Certaines de mes préférences:
_Bool
ou bool
? Les deux sont bien, mais bool
semblent meilleurs que le mot-clé _Bool
.bool
et _Bool
sont: false
ou true
. Affecter 0
ou 1
au lieu de false
ou true
est valide, mais est plus difficile à lire et à comprendre le flux logique.Quelques informations de la norme:
_Bool
n'est PAS unsigned int
, mais fait partie du groupe des types entiers non signés . Il est suffisamment grand pour contenir les valeurs 0
ou 1
.bool
true
et false
mais ce n'est certainement pas une bonne idée. Cette capacité est considérée comme obsolète et sera supprimée à l'avenir._Bool
ou bool
, si la valeur scalaire est égale 0
ou comparable à 0
elle le sera 0
, sinon le résultat est 1
: _Bool x = 9;
9
est convertie en 1
lorsqu'elle est affectée à x
._Bool
est de 1 octet (8 bits), généralement le programmeur est tenté d'essayer d'utiliser les autres bits, mais n'est pas recommandé, car la seule garantie qui est donnée est qu'un seul bit est utilisé pour stocker les données, pas comme le type char
qui en a 8 bits disponibles.Vous pouvez utiliser un caractère ou un autre conteneur de petit nombre pour cela.
Pseudo-code
#define TRUE 1
#define FALSE 0
char bValue = TRUE;
int
), car sur certaines architectures, vous obtenez un impact significatif sur les performances en décompressant / masquant les vérifications de ces variables.
Vous pouvez utiliser _Bool, mais la valeur de retour doit être un entier (1 pour vrai, 0 pour faux). Cependant, il est recommandé d'inclure et d'utiliser bool comme en C ++, comme indiqué dans cette réponse du forum daniweb , ainsi que cette réponse , de cette autre question stackoverflow:
_Bool: type booléen de C99. L'utilisation directe de _Bool n'est recommandée que si vous conservez un code hérité qui définit déjà des macros pour bool, true ou false. Sinon, ces macros sont normalisées dans l'en-tête. Incluez cet en-tête et vous pouvez utiliser bool comme vous le feriez en C ++.
Les expressions conditionnelles sont considérées comme vraies si elles ne sont pas nulles, mais la norme C exige que les opérateurs logiques renvoient eux-mêmes 0 ou 1.
@Tom: #define TRUE! FALSE est mauvais et complètement inutile. Si le fichier d'en-tête fait son chemin dans le code C ++ compilé, cela peut entraîner des problèmes:
void foo(bool flag);
...
int flag = TRUE;
foo(flag);
Certains compilateurs généreront un avertissement concernant la conversion int => bool. Parfois, les gens évitent cela en faisant:
foo(flag == TRUE);
pour forcer l'expression à être un booléen C ++. Mais si vous #définissez VRAI! FAUX, vous vous retrouvez avec:
foo(flag == !0);
qui finit par faire une comparaison int-to-bool qui peut déclencher l'avertissement de toute façon.
Si vous utilisez C99, vous pouvez utiliser le _Bool
type. Aucun #include
s n'est nécessaire. Vous devez cependant le traiter comme un entier, où 1
est true
et 0
est false
.
Vous pouvez ensuite définir TRUE
et FALSE
.
_Bool this_is_a_Boolean_var = 1;
//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;
#include <stdbool.h>
et utiliser bool
, true
et false
comme le veut la norme.
Voici ce que j'utilise:
enum {false, true};
typedef _Bool bool;
_Bool
est un type intégré en C. Il est destiné aux valeurs booléennes.
Vous pouvez simplement utiliser la #define
directive comme suit:
#define TRUE 1
#define FALSE 0
#define NOT(arg) (arg == TRUE)? FALSE : TRUE
typedef int bool;
Et utilisez comme suit:
bool isVisible = FALSE;
bool isWorking = TRUE;
isVisible = NOT(isVisible);
etc
arg
et l'expression dans son ensemble: #define NOT(arg) (((arg) == TRUE) ? FALSE : TRUE)
. Cependant, il serait préférable de tester la fausseté (elle donnera la bonne réponse même si elle arg
était 23 au lieu de 0 ou 1:. #define NOT(arg) (((arg) == FALSE) ? TRUE : FALSE)
Mais l'expression entière peut être réduite à #define NOT(arg) (!(arg))
, bien sûr, ce qui produit le même résultat.