Utilisation de valeurs booléennes en C

Réponses:

1050

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

Explication

  • L'option 1 ne fonctionnera que si vous utilisez C99 et c'est la "manière standard" de le faire. Choisissez ceci si possible.
  • Les options 2, 3 et 4 auront en pratique le même comportement identique. Les # 2 et # 3 n'utilisent cependant pas #defines, ce qui à mon avis est mieux.

Si vous êtes indécis, optez pour le # 1!

Thomas Bonini
la source
1
Pouvez-vous expliquer pourquoi ce sont les meilleurs au pires choix?
endolith
1
@endolith L'alignement, les optimisations et la façon de stocker un que <stdbool.h> boolle 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 booldiffé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.
blubberdiblub
1
Pourquoi utiliser intpour bool? C'est du gaspillage. Utilisez unsigned char. Ou utiliser le C de builtin _Bool.
user12211554
@NoBody L'utilisation d'un type plus petit peut permettre d'économiser de la mémoire, mais cela pourrait ne pas l'accélérer. Souvent, il est plus rapide d'utiliser la taille de mot native du processeur au lieu d'une taille plus petite car cela pourrait nécessiter que le compilateur effectue des décalages de bits pour l'aligner correctement
Ted Klein Bergman
241

Quelques réflexions sur les booléens en C:

Je suis assez vieux pour utiliser simplement des ints 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 fullau 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 !notfullsont 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.

Dale Hagglund
la source
1
Et comment comparez-vous deux variables pour l'égalité? Ne jamais utiliser de constantes booléennes fonctionne très bien, mais cela ne résout pas le problème lors de la comparaison avec une non constante.
Baruch
Pardonnez-moi, mais je ne comprends pas la question. Demandez-vous comment je compare deux variables booléennes pour l'égalité? Si oui, ça ne a == bmarche pas ?
Dale Hagglund
5
@Kenji Ce que vous dites est vrai, même si je pense que l'utilisation de valeurs autres que 1 comme équivalent de true est presque toujours une mauvaise idée. Donc, dans votre exemple, en supposant cela aet en bcomptant à partir de zéro, je recommanderais a > 0 == b > 0plutôt. Si vous insistez pour tirer parti de la véracité des valeurs arbitraires non nulles, !!varla valeur booléenne 0/1 est équivalente à var, vous pouvez donc écrire !!a == !!b, bien que beaucoup de lecteurs trouveront cela déroutant.
Dale Hagglund
3
!a == !best également suffisant pour tester l'égalité, les non-zéros deviennent nuls et les zéros deviennent un.
ryanpattison du
5
@rpattiso Vous avez tout à fait raison, bien sûr, mais je suppose que je lirais !!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.
Dale Hagglund
74

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 trueet 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 myfalsevaleur. 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,

Michael Potter
la source
5
En outre un autre avantage à l' utilisation énumérations est l'intégration IDE - true, falseet boolsont 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.
Curieux: Ignorer si cela fonctionne ou non, est-il valide C (99+) pour permettre à une énumération de référencer une valeur antérieure dans la même énumération ?
@ tgm1024 gcc -ansi -pedantic -Wallne donne aucun avertissement, donc j'ai confiance gcc; Si cela fonctionne même pour c89cela, cela devrait fonctionner c99aussi.
yyny
1
"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." L'opérateur de négation !ne peut renvoyer que des valeurs 0et 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;.
user694733
1
Le plus ancien que j'ai trouvé provient de C90 (6.3.3.3 Opérateurs arithmétiques unaires): "Le résultat de l'opérateur de négation logique! Est 0 si la valeur de son opérande est différente de 0. 1 si la valeur de son opérande est égale à 0. Le result a le type int. L'expression! E est équivalente à (O == E). " Cela devrait couvrir tout compilateur qui a jamais prétendu prendre en charge la norme C. Les compilateurs peuvent bien sûr ignorer légalement cette règle dans les cas où cela n'a pas d'importance pour le comportement observable (comme if(!value)), mais cette exception n'est pas applicable dans ce cas spécifique.
user694733
30

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, falseet bool. Ou ne l'incluez pas, et utilisez _Bool, 1et à la 0place.


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 boolré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, bserait réglé sur false si siff a était zéro, et truesinon. C11 6.3.1.2p1

  1. 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 typedefen place, le doubleserait 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 trueet falseont é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:

  • type intet valeurs 0et 1 tels quels ; et effectuez soigneusement les conversions de domaine à partir de toute autre valeur vers celles-ci avec double négation!!
  • ou si vous insistez vous ne vous souvenez pas que 0 est falsy et truish non nul, au moins l' utilisation des majuscules afin qu'ils ne soient pas confondus avec les concepts de C99: BOOL, TRUEet FALSE!
Antti Haapala
la source
1
Quelle partie de la norme C limiterait les objets de types énumérés à contenir les valeurs qui y sont explicitement énumérées? Si la plus grande valeur pour une constante énumérée est inférieure à UCHAR_MAX ou USHRT_MAX, une implémentation pourrait utiliser un type plus petit que intou unsigned intpour 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.
supercat
18
typedef enum {
    false = 0,
    true
} t_bool;
mouviciel
la source
2
2 à MAX_INT devrait également être vrai
technosaurus
2
@technosaurus Cette approche ne garantit pas! false == true car! false peut être n'importe quel nombre différent de zéro. Une solution simple serait d'affecter explicitement true à! False.
Andrew
3
@Andrew Ce n'est pas vrai. !0 = 1par la norme C, et !a = 0pour 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 aet bsont tous les deux "vrais", ce n'est pas nécessairement le cas que `a == b`.
Jeremy West
14

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.

dmeister
la source
12
10 ans en standard, mais pas 10 ans en compilateurs! La compilation C de MSVC ++ ne prend pas du tout en charge C99, à l'exception de // l'autorisation des commentaires, et il est peu probable qu'elle le fasse. _Bool est également défini dans C99 comme un type intégré, tandis que bool est un typedef dans l'en-tête <stdbool.h>.
Clifford
5
@Clifford 4 ans après votre commentaire ... rien n'a changé. MSVC est un compilateur C ++ et je pense que MS a dit qu'il ne souhaitait pas vraiment prendre en charge toutes les nouvelles fonctionnalités C (C99 et C11). Mais je ne peux pas considérer que MSVC ne prend pas en charge les nouvelles fonctionnalités C comme raison (surtout lorsque vous le dites contre 10 ans de réponse). 10 ans, c'est vraiment long dans le monde de la programmation. Tout compilateur décent devrait le prendre en charge dans beaucoup moins de 10 ans si le fournisseur a l'intention de le prendre en charge.
PP
2
@ KingsIndian: Je ne sais pas pourquoi vous m'avez adressé votre commentaire ou même ressenti le besoin de commenter. Je ne faisais qu'énoncer la situation telle qu'elle était au moment de la rédaction. Je n'appuyais pas cette situation, faisant simplement remarquer que la "réponse" pouvait ne pas s'appliquer en toutes circonstances.
Clifford
@Clifford: Strictement, la norme doit boolêtre une macro qui se développe _Bool. La différence est importante car vous pouvez #undefune macro (et c'est autorisé, au moins à titre de mesure transitoire), mais vous ne pouvez pas untypedefun typedef. Cependant, cela ne modifie pas l'orientation principale de votre premier commentaire.
Jonathan Leffler
2
VS2015 et versions ultérieures (et peut-être plus tôt, jusqu'à un certain point) n'ont aucun problème avec la compilation boolthrough <stdbool.h>under C. Il se résout à _Bool.
11

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.

ggambett
la source
10
mais utilisez-les avec précaution: puisqu'un vrai résultat peut être n'importe quelle valeur non nulle, les tests if (t == TRUE) {...} et if (t), qui sont équivalents dans d'autres langues, ne sont pas équivalents en C .
Fortega
1
Vous avez raison, mais c'est également vrai en C ++ qui a un type bool, non? Pendant le débogage, j'ai vu des variables booléennes avec des valeurs de 5837834939 ...
ggambett
1
En C ++, le test if (t == true) est égal au test if (t), car C ++ effectue une certaine conversion (tout ce qui n'est pas 0 ou une valeur de pointeur nul est converti en true)
Fortega
6
Tout ce que vous devez supposer d'une valeur booléenne vraie, c'est qu'elle est différente de zéro. Donc, du code comme if (b) est sûr tandis que if (b == TRUE) ne l'est pas; ce dernier est une mauvaise pratique (et inutile).
Clifford
5

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:

  • _Boolou bool? Les deux sont bien, mais boolsemblent meilleurs que le mot-clé _Bool.
  • Les valeurs acceptées pour boolet _Boolsont: falseou true. Affecter 0ou 1au lieu de falseou trueest valide, mais est plus difficile à lire et à comprendre le flux logique.

Quelques informations de la norme:

  • _Booln'est PAS unsigned int, mais fait partie du groupe des types entiers non signés . Il est suffisamment grand pour contenir les valeurs 0ou 1.
  • NE PAS, mais oui, vous êtes en mesure de redéfinir bool trueet falsemais ce n'est certainement pas une bonne idée. Cette capacité est considérée comme obsolète et sera supprimée à l'avenir.
  • L'affectation d'un type scalaire (types arithmétiques et types de pointeurs) à _Boolou bool, si la valeur scalaire est égale 0ou comparable à 0elle le sera 0, sinon le résultat est 1: _Bool x = 9; 9est convertie en 1lorsqu'elle est affectée à x.
  • _Boolest 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 charqui en a 8 bits disponibles.
Comportement indéfini
la source
2

C'est ça:

#define TRUE 1
#define FALSE 0
RngTng
la source
7
Je vais avec quelque chose comme #define TRUE! FALSE
Tom
2

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;
Filip Ekberg
la source
En C également, il s'agit généralement d'un int, et il peut entraîner une perte d'avertissements de précision par un autre code utilisant int.
Thomas Bonini
À moins que vous n'optimisiez manuellement l'espace, il est toujours préférable d'utiliser la taille de mot normale du matériel (par exemple: généralement un int), car sur certaines architectures, vous obtenez un impact significatif sur les performances en décompressant / masquant les vérifications de ces variables.
Kingsley
2

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 ++.

Lokian
la source
2

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.

jamesdlin
la source
1

Si vous utilisez C99, vous pouvez utiliser le _Booltype. Aucun #includes n'est nécessaire. Vous devez cependant le traiter comme un entier, où 1est trueet 0est false.

Vous pouvez ensuite définir TRUEet FALSE.

_Bool this_is_a_Boolean_var = 1;


//or using it with true and false
#define TRUE 1
#define FALSE 0
_Bool var = TRUE;
That_Linux_Guy
la source
Ou vous pouvez #include <stdbool.h>et utiliser bool, trueet falsecomme le veut la norme.
SS Anne
1

De nos jours, C99 prend en charge les types booléens, mais vous en avez besoin #include <stdbool.h>.

Exemple:

#include <stdbool.h>

int main() 
{ 
    bool arr[2] = {true, false}; 

    printf("%d\n", arr[0] && arr[1]);
    printf("%d\n", arr[0] || arr[1]);

    return 0; 
} 

Production:

0
1
Kalana
la source
0

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.

user12211554
la source
-2

Vous pouvez simplement utiliser la #definedirective 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

Prakash Bhattarai
la source
5
NOT macro doit être protégée par des parenthèses autour de la arget 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.
Jonathan Leffler