Menu

Les booléens en C : stdbool.h, _Bool et la véracité

Le C n'a eu aucun type booléen jusqu'à C99. Zéro est faux, tout le reste est vrai, les comparaisons produisent un int - et stdbool.h ajoute bool, true et false par-dessus. Plus le bug = contre == que cette conception rend possible.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Le C a passé ses 27 premières années sans type booléen. Ce n'est pas un oubli - c'est une décision de conception avec des conséquences que vous rencontrez dès le premier jour, et un bug célèbre qui en découle directement.

Zéro est faux, tout le reste est vrai

Il n'existe aucune valeur vrai/faux spéciale dans les règles de condition du C. if, while, for, &&, || et ! posent tous exactement une question : cette valeur est-elle nulle ?

Les nombres négatifs sont vrais. Les fractions sont vraies. Un pointeur non nul est vrai. Seul zéro - dans n'importe quel type - est faux.

C'est pourquoi le code C regorge d'idiomes qui paraissent laconiques tant qu'on ignore la règle :

if (count)            /* si count n'est pas nul */
if (!count)           /* si count vaut zero */
if (p)                /* si p n'est pas NULL */
if (!strcmp(a, b))    /* si les chaines sont egales - strcmp renvoie 0 en cas d'egalite */

Le dernier piège les gens. strcmp renvoie 0 quand les chaînes correspondent, donc « égales » se lit « non » dans un if. Écrire if (strcmp(a, b) == 0) est plus clair et fait exactement la même chose.

Les comparaisons produisent un int

Une comparaison en C n'est pas une sorte spéciale d'expression. Elle s'évalue en un int ordinaire valant 1 ou 0, et vous pouvez l'afficher, la stocker ou faire de l'arithmétique avec :

L'opérateur ! fonctionne de la même manière : il produit 1 si son opérande est nul et 0 sinon. Ainsi !!x est un idiome classique qui normalise n'importe quelle valeur à exactement 0 ou 1.

bool, true et false (C99)

C99 a ajouté un véritable type booléen appelé _Bool, et un en-tête qui lui donne un nom lisible :

<stdbool.h> est minuscule : il définit bool comme _Bool, true comme 1 et false comme 0. C'est tout l'en-tête. Mais l'utiliser rend l'intention visible d'une façon que int flag = 1; ne fera jamais.

_Bool a un comportement réellement distinct : il ne stocke que 0 ou 1. Affecter n'importe quelle valeur non nulle la convertit en exactement 1 :

Il n'existe toujours pas de %b dans printf pour les booléens - affichez-les avec %d, ou convertissez-les vous-même en texte :

En C23, cela s'est encore simplifié : bool, true et false sont devenus de vrais mots-clés, donc l'inclusion n'est plus nécessaire. La plupart du code actuel vise encore C17, alors gardez le #include <stdbool.h>.

Renvoyer un booléen depuis une fonction

C'est là que bool justifie le plus clairement sa place :

Un type de retour bool indique à l'appelant ce que la valeur signifie. Un retour int depuis une fonction nommée check_file pourrait être un booléen, un compte ou un code d'erreur - et dans les conventions des bibliothèques C, c'est souvent le troisième, où 0 signifie succès. Nommer le type supprime la devinette.

Le bug = contre ==

Parce que l'affectation est une expression qui produit la valeur affectée, et parce que toute valeur non nulle est vraie, ceci compile :

Deux choses ont mal tourné d'un coup. La condition a affecté 5 à x, détruisant sa valeur, puis a testé 5, qui est non nul, donc la branche s'est exécutée. Le programme n'a ni erreur ni avertissement par défaut.

Pire, la version avec 0 n'exécute jamais sa branche :

if (found = 0) { /* jamais prise - et found vaut maintenant 0 */ }

Trois défenses, par ordre d'utilité :

Compilez avec les avertissements. -Wall l'attrape :

warning: suggest parentheses around assignment used as truth value

Arriver à zéro avertissement est le vrai correctif ici, et cela coûte une option.

Écrivez la constante en premier. La « condition de Yoda » transforme la faute de frappe en erreur de compilation :

if (5 == x)   /* correct */
if (5 = x)    /* erreur : ce n'est pas une lvalue - le compilateur vous arrete */

Certaines équipes adorent, d'autres trouvent cela illisible ; les deux positions se défendent, mais cela fonctionne.

Soyez explicite quand c'est voulu. L'affectation dans une condition est réellement utile pour lire une entrée :

int c;
while ((c = getchar()) != EOF) { ... }

Les parenthèses supplémentaires autour de c = getchar() sont requises par la précédence, et elles signalent aussi que l'affectation est intentionnelle. C'est l'idiome - si votre affectation-dans-une-condition ne ressemble pas à cela, c'est probablement une faute de frappe.

Erreurs courantes avec les booléens

Comparer à true. if (flag == true) fonctionne avec stdbool.h, mais cela casse dès que flag est un int valant 42, car 42 == 1 est faux. Écrivez if (flag).

Comparaisons chaînées. if (0 < x < 10) est toujours vrai : 0 < x produit 0 ou 1, et les deux sont inférieurs à 10. Écrivez if (x > 0 && x < 10).

Bit à bit au lieu de logique. & et | ne sont pas && et ||. Ils n'évaluent pas en court-circuit, et ils combinent des bits :

Les deux opérandes sont « vrais », et pourtant a & b vaut 0. Utiliser & là où vous vouliez && produit une condition fausse seulement pour certaines valeurs, le type de bug le plus difficile à trouver.

Comparer des flottants pour l'égalité. if (0.1 + 0.2 == 0.3) est faux. Comparez plutôt avec une tolérance - voir types de données pour comprendre pourquoi.

Questions fréquentes

Le C possède-t-il un type booléen ?

Depuis C99, oui : _Bool est un type intégré, et inclure <stdbool.h> vous donne les orthographes plus agréables bool, true et false. Avant C99, il n'y avait aucun booléen - les programmes utilisaient int avec 0 pour faux et 1 pour vrai, ce qui fonctionne toujours et reste courant.

Qu'est-ce qui compte comme vrai en C ?

Toute valeur non nulle. 1, -5, 0.01 et un pointeur non nul sont tous vrais dans une condition ; seuls 0, 0.0 et un pointeur nul sont faux. Il n'y a pas de test booléen distinct - if (x) teste simplement si x est non nul.

Comment utiliser bool en C ?

Ajoutez #include <stdbool.h> et bool ready = true; fonctionne comme vous l'attendez. En interne, bool est _Bool, qui ne stocke que 0 ou 1 - lui affecter n'importe quelle valeur non nulle y stocke exactement 1.

Quelle est la différence entre = et == en C ?

= affecte, == compare. if (x = 5) affecte 5 à x puis teste 5, qui est non nul, donc la branche s'exécute toujours. if (x == 5) teste l'égalité. La première compile sans erreur, ce qui explique l'existence de -Wall et de l'habitude d'écrire if (5 == x).

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER