La norme C indique-t-elle explicitement la valeur de vérité 0 ou 1?


86

Nous savons que tous les nombres qui ne sont pas égaux à 0sont considérés commetrue en C, nous pouvons donc écrire:

int a = 16;

while (a--)
    printf("%d\n", a);  // prints numbers from 15 to 0

Cependant, je me demandais si vrai / faux sont définis comme 1/0 en C, alors j'ai essayé le code ci-dessous:

printf("True = %d, False = %d\n", (0 == 0), (0 != 0));  // prints: True = 1, False = 0

La norme C indique-t-elle explicitement les valeurs de vérité de vrai et de faux comme 1et 0respectivement?


3
Je suppose que cette question SO est pertinente
Imran Ali

3
J'essaye d'exécuter ceci sous gccavec -std=c89et cela donne le même résultat.
Kevin Dong

1
@Blackhole, du 15 au 0.
Arturo Torres Sánchez

6
Presque dupe mais plus d'une décennie avant SO / SE: c-faq.com/bool/bool2.html .
dave_thompson_085

1
Ce faux est zéro est canon, mais vrai est généralement considéré comme "différent de zéro". Les programmeurs étant ce qu'ils sont cependant, nous avons tous utilisé 1 comme "non nul" pour nos diverses raisons. Vous êtes encouragé à ne pas faire confiance à un vrai pour être exactement 1. Alors que (0 == 0) est un dans votre exemple, quelque chose comme (12 == 12) pourrait tout aussi facilement avoir la valeur 12; aussi «vrai».
Ingénieur

Réponses:


96

La norme C indique-t-elle explicitement les valeurs de vérité de trueet falsecomme 0et 1respectivement?

Le standard C définit trueet falsecomme des macros dans stdbool.hlesquelles se développent respectivement vers 1et 0.

C11-§7.18:

Les trois macros restantes peuvent être utilisées dans les #ifdirectives de prétraitement. Elles sont

true

qui se développe jusqu'à la constante entière 1,

false

qui se développe jusqu'à la constante entière 0[...]

Pour les opérateurs ==et !=, selon la norme

C11-§6.5.9 / 3:

Les opérateurs ==(égal à) et !=(non égal à) sont analogues aux opérateurs relationnels à l'exception de leur priorité inférieure. 108) Chacun des opérateurs donne 1si la relation spécifiée est vraie et 0si elle est fausse. Le résultat a du type int. Pour toute paire d'opérandes, exactement l'une des relations est vraie.


20
Il me semble que la question porte sur 0 == 0et 0 != 0etc., pas sur la valeur des macros.
MM

9
Je pense que quand il a écrit, trueil voulait dire "la valeur d'une vraie comparaison" ou quelque chose, pas la macrotrue
MM

1
@KevinDong; Oui, le projet C99 a un paragraphe similaire.
haccks

1
@haccks: vous pouvez dire à ce sujet sans hésiter "identique" Je viens de pciké votre citation car j'étais trop paresseux pour chercher par paragraphe puisque je me réfère à c99 quand c'est nécessaire. Et j'ai pu le trouver en le recherchant simplement via ctrl+ f;)
dhein

2
@MooingDuck: NaN == NaNest faux et NaN != NaNest vrai. Il n'y a aucun problème avec cette déclaration.
kennytm

51

Il n'est pas explicitement indiqué en C11. Toutes les opérations au niveau de la langue renverront 1 comme véridique (et accepteront tout différent de zéro, y compris NaN, comme vrai).

  • Si vous vous inquiétez de _Bool, alors true doit être 1 car la norme ne lui demande que 0 et 1. (§6.2.5 / 2).
  • Également dans <stdbool.h>la macro se truedéveloppe à 1(§7.18 / 3)
  • ==, !=, <, >, <=Et >=revenir à 0 ou 1 (§6.5.8 / 6, §6.5.9 / 3).
  • !, &&et ||renvoyer 0 ou 1 (§6.5.3.3 / 5, §6.5.13 / 3, §6.5.14 / 3)
  • defined se développe à 0 ou 1 (§6.10.1 / 1)

Mais toutes les fonctions standard de la bibliothèque, par exemple, islowerdites simplement "différent de zéro" pour la vérité (par exemple, §7.4.1 / 1, §7.17.5.1 / 3, §7.30.2.1 / 1, §7.30.2.2.1 / 4).


§6.2.5 / 2 : Un objet déclaré comme type _Boolest suffisamment grand pour stocker les valeurs 0 et 1.

§6.5.5.3 / 5 : Le résultat de l'opérateur de négation logique !est 0 si la valeur de son opérande se compare inégale à 0, 1 si la valeur de son opérande est égale à 0.…

§6.5.8 / 6 : Chacun des opérateurs <(inférieur à), >(supérieur à), <=(inférieur ou égal à) et >=(supérieur ou égal à) doit donner 1 si la relation spécifiée est vraie et 0 si elle c'est faux. 107)…

§6.5.9 / 3 : Les opérateurs ==(égal à) et !=(non égal à) sont analogues aux opérateurs relationnels sauf pour leur priorité inférieure.108) Chacun des opérateurs donne 1 si la relation spécifiée est vraie et 0 si elle est faux. …

§6.5.13 / 3 : L' &&opérateur doit donner 1 si ses deux opérandes se comparent inégal à 0; …

§6.5.14 / 3 : L' ||opérateur doit donner 1 si l'un de ses opérandes se compare différent à 0; …

§6.10.1 / 1 :… il peut contenir des expressions d'opérateurs unaires de la forme - defined identifier- ou - defined ( identifier )- qui valent 1 si…

§7.4.1 (Fonctions de classification de caractères) / 1 : Les fonctions de ce paragraphe renvoient une valeur non nulle (vraie) si et seulement si…

§7.18 / 3 : Les trois macros restantes conviennent pour une utilisation dans les #ifdirectives de prétraitement. Ce sont - true- qui se développent jusqu'à la constante entière 1,…

§7.17.5.1 / 3 : La atomic_is_lock_freefonction générique renvoie une valeur différente de zéro (vrai) si et seulement si les opérations de l'objet sont sans verrouillage. …

§7.30.2.1 (Fonctions de classification des caractères larges) / 1 : Les fonctions de ce paragraphe renvoient une valeur différente de zéro (vrai) si et seulement si…

§7.30.2.2.1 / 4 : La iswctypefonction renvoie une valeur différente de zéro (vrai) si et seulement si…


23

Il y a deux domaines de la norme dont vous devez être conscient lorsque vous traitez avec des valeurs booléennes (par lesquelles je veux dire des valeurs vraies / fausses plutôt que le bool/_Booltype C spécifique ) en C.

Le premier concerne le résultat des expressions et peut être trouvé dans diverses parties de C11 6.5 Expressions(opérateurs relationnels et d'égalité, par exemple). L'essentiel est que chaque fois qu'une valeur booléenne est générée par une expression, elle ...

... donne 1 si la relation spécifiée est vraie et 0 si elle est fausse. Le résultat est de type int.

Donc, oui, le résultat de toute expression générant un booléen sera un pour vrai, ou zéro pour faux. Cela correspond à ce que vous trouverez stdbool.hlà où les macros standard trueet falsesont définies de la même manière.

Gardez à l'esprit cependant que, suivant le principe de robustesse «soyez conservateur dans ce que vous envoyez, libéral dans ce que vous acceptez», l'interprétation des nombres entiers dans le contexte booléen est un peu plus détendue.

Encore une fois, à partir de différentes parties de 6.5, vous verrez un langage comme:

L' ||opérateur doit donner 1 si l'un de ses opérandes n'est pas égal à 0; sinon, il donne 0. Le résultat est de type int.

À partir de cela (et d'autres parties), il est évident que zéro est considéré comme faux et toute autre valeur est vraie.


En passant, le langage spécifiant la valeur utilisée pour la génération et l'interprétation booléennes apparaît également dans C99 et C89, ils existent donc depuis un certain temps. Même K&R (deuxième édition et première édition de l' ANSI-C ) a précisé cela, avec des segments de texte tels que:

Les expressions relationnelles telles i > jque les expressions logiques connectées par &&et ||sont définies pour avoir une valeur 1si true et 0si false.

Dans la partie de test if, while, for, etc, "true" signifie simplement "non nulle".

L' &&opérateur ... renvoie 1 si ses deux opérandes ne sont pas égaux à zéro, 0 sinon.

L' ||opérateur ... renvoie 1 si ses opérandes se comparent à zéro et 0 dans le cas contraire.

Les macros dans stdbool.happaraissent également dans C99, mais pas dans C89 ou K&R car ce fichier d'en-tête n'existait pas à ce stade.


2
noter que ||, ==, !=etc. rendement int, pas un type booléen
MM

2
Je vote cette question pour la bonne. Pour moi, la question concerne également les opérateurs relationnels et non les macros.
ckruczek

"Dans la partie de test if, while, for, etc, "true" signifie simplement "non nulle"." C'est la partie la plus importante de la réponse et c'est, à mon avis, un choix malheureux de Dennis Ritchie il y a longtemps. Quiconque a écrit des fonctions qui renvoient des codes d'erreur comme la valeur de retour l'ont généralement #define noErr 0et tout code d'erreur différent de zéro est une erreur. Et puis le problème est que la simplicité et la beauté if ( ready_to_do_something() ){do_something();} ne fonctionnent pas. Cela doit être if ( !not_ready_to_do_something() ){do_something();}"Il y a beaucoup de mensonges, mais une seule vérité". TRUE devrait être 0.
robert bristow-johnson

Par curiosité, comment les premières ébauches de règles C ont-elles spécifié le comportement de "&&" et "||" dans le cas où les opérandes avaient des valeurs autres que 0 ou 1? Le texte que vous citez dit "expressions logiques" reliées par && et ||, mais que faire si ces opérateurs connectent des choses autres que des expressions logiques?
supercat

1
@sdenham, oui. Dans le premier exemplaire de K&R que j'ai (première édition, tirage 14, un si tôt qu'il mentionne les caractéristiques matérielles de quatre machines typiques, le PDP-11, Honeywell-6000, IBM-370 et Interdata-8/32), A.7.6/7/10/11(relationnel / égalité / logique-et / logique-ou) spécifient tous qu'il donne 0 ou 1 comme résultat. Ayez une réponse mise à jour pour inclure cela.
paxdiablo

10

Vous mélangez beaucoup de choses différentes: instructions de contrôle, opérateurs et types booléens. Chacun a ses propres règles.

Les instructions de contrôle fonctionnent comme par exemple l' ifinstruction, C11 6.4.8.1:

Dans les deux formes, la première sous-déclaration est exécutée si l'expression est différente de 0.

while, foretc. ont la même règle. Cela n'a rien à voir avec «vrai» ou «faux».

Quant aux opérateurs qui sont censés donner un résultat booléen, ils donnent en fait un intavec la valeur 1 ou 0. Par exemple les opérateurs d'égalité, C11 6.5.9:

Chacun des opérateurs donne 1 si la relation spécifiée est vraie et 0 si elle est fausse

Tout ce qui précède est dû au fait que C n'avait pas de type booléen avant 1999, et même quand il en a obtenu un, les règles ci-dessus n'ont pas été modifiées. Donc, contrairement à la plupart des autres langages de programmation où les instructions et les opérateurs donnent un type booléen (comme C ++ et Java), ils donnent juste un int, avec une valeur zéro ou non zéro. Par exemple, sizeof(1==1)donnera 4 en C mais 1 en C ++.

Le type booléen réel en C est nommé _Boolet nécessite un compilateur moderne. L' en- tête stdbool.hdéfinit les macros bool, trueet false, qui se développent à _Bool, 1et 0respectivement (pour la compatibilité avec C ++).


Il est cependant considéré comme une bonne pratique de programmation de traiter les instructions de contrôle et les opérateurs comme s'ils exigeaient / produisaient réellement un type booléen. Certaines normes de codage comme MISRA-C recommandent une telle pratique. C'est:

if(ptr == NULL)au lieu de if(ptr).

if((data & mask) != 0)au lieu de if(data & mask).

Le but d'un tel style est d'augmenter la sécurité du type à l'aide d'outils d'analyse statique, ce qui réduit les bogues. On peut soutenir que ce style n'a de sens que si vous utilisez des analyseurs statiques. Bien que dans certains cas, cela conduise à un code plus lisible et auto-documenté, par exemple

if(c == '\0') 

Bien, l'intention est claire, le code est auto-documenté.

contre

if(c) 

Mal. Cela pourrait vouloir dire n'importe quoi, et nous devons chercher le type de cpour comprendre le code. Est-ce un entier, un pointeur ou un caractère?


1
sizeof(bool)est une implémentation spécifique en C ++. Voir stackoverflow.com/questions/4897844/is-sizeofbool-defined .
David Hammen

@DavidHammen Tout comme sizeof (0 == 0) est également défini par l'implémentation. Ce n'est qu'un exemple.
Lundin

Je pensais que C avait changé les règles pour les types booléens. D'autres types de types uintN (y compris les types "bit" de nombreux compilateurs plus anciens) stockent les N bits inférieurs d'une valeur et ignorent tous les bits supérieurs, tandis que les nouveaux types booléens "ou" ensemble tous les bits.
supercat

1
Cela devrait-il être le cas if(ptr != NULL), ou peut if(!ptr)- être ?
Mathieu K.

1
if(c == '\0')se prête à l'erreur de codage particulièrement courante des débutants if(c = '\0'), je l'évite donc. D'accord, if(c)c'est mauvais ... ça devrait être, par exemple,if(valveIsOpen)
aja

4

J'ai programmé dans de nombreuses langues. J'ai vu true être 1 ou -1 selon la langue. La logique derrière l'être vrai 1 était qu'un bit était soit un 0 soit un 1. La logique derrière l'être vrai -1 était que le! l'opérateur était un complément à un. Il a changé tous les 1 en 0 et tous les 0 en 1 dans un int. Donc, pour un int,! 0 = -1 et! (- 1) = 0. Cela m'a suffisamment déclenché pour que je ne compare pas quelque chose pour être == vrai, mais plutôt que pour être! = Faux. De cette façon, mon style de programmation fonctionne dans tous les langages. Donc ma réponse est de ne pas vous en soucier, mais de programmer pour que votre code fonctionne correctement dans les deux cas.


Comment puis ! changez tous les 0 en 1 et produisez toujours 0 pour! 5?
codehot le

@codeshot Ça ne peut pas. Mais ce qu'il fait valoir, c'est que toutes les langues ne traitent pas l'opérande de! comme un booléen. Certains régal! comme C's ~ - c'est-à-dire un complément au niveau du bit. Dans ce cas, la détermination de la valeur résultante nécessite de connaître le type de la variable en premier lieu, donc! (Uint32_t) 5 serait 4 294 967 290. Mais! 0 est toujours 4 294 967 295, et 4 294 967 295 est la vérité.
Pegasus Epsilon

1

Cette réponse doit être examinée d'un peu plus près.

La définition réelle en C ++ est que tout ce qui n'est pas 0 est traité comme vrai. Pourquoi est-ce pertinent? Parce que C ++ ne sait pas ce qu'est un entier par la façon dont nous le pensons - nous créons ce sens, tout ce qu'il contient est le shell et les règles pour ce que cela signifie. Il sait ce que sont les bits, ce qui constitue un entier.

1 en tant qu'entier est librement représenté en bits, disons un entier signé de 8 bits comme 0000 0001. Plusieurs fois, ce que nous voyons visuellement est un peu un mensonge, -1 est une façon beaucoup plus courante de le représenter en raison de la nature signée de «entier». Je ne peux vraiment pas dire vrai proprement dit, pourquoi? Parce que ce n'est PAS une opération est 1111 1110. C'est un problème vraiment majeur pour un booléen. Quand nous parlons d'un booléen, c'est juste 1 bit - c'est vraiment simple, 0 est faux et 1 est vrai. Toutes les opérations logiques sont insignifiantes. C'est pourquoi «-1» doit être désigné comme «vrai» pour les entiers (signés). 1111 1111 NOT'ed devient 0000 0000 --- la logique tient et nous sommes bons. Les entiers non signés sont un peu délicats et étaient beaucoup plus couramment utilisés dans le passé - où 1 signifie vrai parce qu'il est facile d'impliquer la logique selon laquelle '

Voilà l'explication. Je dis que la réponse acceptée ici est fausse - il n'y a pas de définition claire dans la définition C / C ++. Un booléen est un booléen, vous pouvez traiter un entier comme un booléen, mais le fait que la sortie soit un entier ne dit rien sur l'opération en cours d'exécution est au niveau du bit.


4
La question portait sur C, pas sur C ++.
glglgl

0

Cela s'est produit à cause des opérateurs relationnels dans votre printfdéclaration.

Opérateur ==et opérateur!=

Puisque c'est (0 == 0)vrai, cela donne une valeur1

alors que, (0 != 0)n'est pas vrai, donne une valeur 0.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.