Menu

Booleanos em C: stdbool.h, _Bool e veracidade

C não tinha tipo booleano até o C99. Zero é falso, todo o resto é verdadeiro, comparações resultam em int - e stdbool.h te dá bool, true e false por cima. Mais o bug de = vs == que esse desenho possibilita.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

C passou seus primeiros 27 anos sem um tipo booleano. Isso não é um descuido - é uma decisão de projeto com consequências que você encontra no seu primeiro dia, e um bug famoso que decorre diretamente dela.

Zero é falso, todo o resto é verdadeiro

Não existe um valor especial de verdadeiro/falso nas regras de condição do C. if, while, for, &&, || e ! fazem exatamente uma pergunta: este valor é zero?

Números negativos são verdadeiros. Frações são verdadeiras. Um ponteiro não nulo é verdadeiro. Apenas zero - em qualquer tipo - é falso.

É por isso que o código C é cheio de idiomas que parecem lacônicos até você conhecer a regra:

if (count)            /* se count não for zero */
if (!count)           /* se count for zero */
if (p)                /* se p não for NULL */
if (!strcmp(a, b))    /* se as strings forem iguais - strcmp retorna 0 quando casam */

Esse último pega as pessoas. strcmp retorna 0 quando as strings casam, então "igual" se lê como "não" dentro de um if. Escrever if (strcmp(a, b) == 0) é mais claro e faz exatamente a mesma coisa.

Comparações produzem int

Uma comparação em C não é um tipo especial de expressão. Ela avalia para um int comum com o valor 1 ou 0, e você pode imprimi-la, armazená-la ou fazer aritmética com ela:

O operador ! funciona da mesma forma: produz 1 se seu operando for zero e 0 caso contrário. Então !!x é um idioma clássico que normaliza qualquer valor para exatamente 0 ou 1.

bool, true e false (C99)

C99 adicionou um tipo booleano de verdade chamado _Bool, e um cabeçalho que lhe dá um nome legível:

<stdbool.h> é minúsculo: ele define bool como _Bool, true como 1 e false como 0. É o cabeçalho inteiro. Mas usá-lo torna a intenção visível de um jeito que int flag = 1; nunca faz.

_Bool tem um comportamento genuinamente distinto: ele armazena apenas 0 ou 1. Atribuir qualquer valor diferente de zero converte para exatamente 1:

Ainda não existe um %b no printf para booleanos - imprima-os com %d, ou converta para texto você mesmo:

No C23 isso ficou ainda mais simples: bool, true e false viraram palavras-chave de verdade, então o include não é mais obrigatório. A maior parte do código hoje ainda mira C17, então mantenha o #include <stdbool.h>.

Retornando um booleano de uma função

É aqui que bool justifica seu lugar de forma mais clara:

Um tipo de retorno bool diz a quem chama o que o valor significa. Um retorno int de uma função chamada check_file poderia ser um booleano, uma contagem ou um código de erro - e nas convenções das bibliotecas C é frequentemente o terceiro, em que 0 significa sucesso. Nomear o tipo elimina o palpite.

O bug de = vs ==

Como a atribuição é uma expressão que resulta no valor atribuído, e como qualquer valor diferente de zero é verdadeiro, isto compila:

Duas coisas deram errado de uma vez. A condição atribuiu 5 a x, destruindo seu valor, e então testou 5, que é diferente de zero, então o ramo executou. O programa não tem erro nem aviso por padrão.

Pior, a versão com 0 nunca executa seu ramo:

if (found = 0) { /* nunca executa - e found agora é 0 */ }

Três defesas, em ordem de utilidade:

Compile com avisos. -Wall pega isso:

warning: suggest parentheses around assignment used as truth value

Chegar a zero avisos é a verdadeira solução aqui, e custa uma flag.

Escreva a constante primeiro. A "condição Yoda" transforma o erro de digitação em um erro de compilação:

if (5 == x)   /* correto */
if (5 = x)    /* erro: não é um lvalue - o compilador te impede */

Alguns times adoram isso e outros acham ilegível; qualquer posição é defensável, mas funciona.

Seja deliberado quando for intencional. A atribuição dentro de uma condição é genuinamente útil para ler entrada:

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

Os parênteses extras em torno de c = getchar() são exigidos por precedência, e também sinalizam que a atribuição é intencional. Esse é o idioma - se a sua atribuição dentro de uma condição não se parece com isso, provavelmente é um erro de digitação.

Erros comuns com booleanos

Comparar com true. if (flag == true) funciona com stdbool.h, mas quebra no momento em que flag é um int guardando 42, porque 42 == 1 é falso. Escreva if (flag).

Comparações encadeadas. if (0 < x < 10) é sempre verdadeiro: 0 < x resulta em 0 ou 1, e ambos são menores que 10. Escreva if (x > 0 && x < 10).

Bit a bit em vez de lógico. & e | não são && e ||. Eles não fazem curto-circuito, e combinam bits:

Ambos os operandos são "verdadeiros" e, ainda assim, a & b é 0. Usar & onde você queria && produz uma condição que só está errada para certos valores, que é o tipo de bug mais difícil de encontrar.

Comparar floats por igualdade. if (0.1 + 0.2 == 0.3) é falso. Compare contra uma tolerância - veja tipos de dados para entender o porquê.

Perguntas frequentes

C tem um tipo booleano?

Desde o C99, sim: _Bool é um tipo embutido, e incluir <stdbool.h> te dá as grafias mais amigáveis bool, true e false. Antes do C99 não havia booleano nenhum - os programas usavam int com 0 para falso e 1 para verdadeiro, o que ainda funciona e ainda é comum.

O que conta como verdadeiro em C?

Qualquer valor diferente de zero. 1, -5, 0.01 e um ponteiro não nulo são todos verdadeiros em uma condição; apenas 0, 0.0 e um ponteiro nulo são falsos. Não existe uma verificação booleana separada - if (x) simplesmente testa se x é diferente de zero.

Como uso bool em C?

Adicione #include <stdbool.h> e então bool ready = true; funciona como você esperaria. Por baixo dos panos bool é _Bool, que armazena apenas 0 ou 1 - atribuir qualquer valor diferente de zero a ele guarda exatamente 1.

Qual é a diferença entre = e == em C?

= atribui, == compara. if (x = 5) atribui 5 a x e então testa 5, que é diferente de zero, então o ramo sempre executa. if (x == 5) testa igualdade. O primeiro compila sem erro, que é por que -Wall e o hábito de escrever if (5 == x) existem.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR