C Booleanos (bool)

Muitas perguntas de um programa têm só duas respostas: o cliente pagou? Está chovendo? A senha está certa? A nota é maior que 7? Para guardar essas respostas existe o tipo bool, que só tem dois valores: true (verdadeiro) e false (falso). Ele parece o tipo mais simples de todos, e é, mas em C tem uma história curiosa (por 27 anos ele nem existiu) e algumas surpresas: o printf o mostra como 1 e 0, e qualquer número pode virar bool. Os booleanos são a base de todas as decisões (if) e repetições (while) que vêm depois.

Você vai aprender
  • Como criar variáveis bool com true e false C23.
  • Por que o printf mostra 1 e 0, e como mostrar "sim" e "não".
  • Que uma comparação (>, ==…) pode ser guardada num bool.
  • Como números viram bool (zero é falso, o resto é verdadeiro), e o caminho inverso.
  • A história: int no C antigo, _Bool e stdbool.h do C99 ao C17, e o código antigo que não compila no C23.
  • Como dar bons nomes a variáveis bool, e por que não ler um bool com scanf.

Os programas foram compilados na máquina fictícia da Ana com o comando padrão do guia, gcc -std=c23 -Wall -Wextra (gcc 15.2), na pasta ~/c/bool.


A ideia: o interruptor

Um interruptor de luz tem duas posições: ligado ou desligado. Não existe "meio ligado". Um bool é um interruptor guardado na memória: true é ligado, false é desligado.

O nome vem de George Boole, um matemático inglês do século XIX que criou uma álgebra só com "verdadeiro" e "falso" (a "álgebra booleana"). Cem anos depois, ela virou a base dos circuitos de todo computador.


1. true, false e o que o printf mostra

#include <stdio.h>

int main(void) {
    bool chovendo = true;
    bool pago = false;
    printf("chovendo: %d\n", chovendo);
    printf("pago: %d\n", pago);
    printf("pago: %s\n", pago ? "sim" : "não");
    printf("tamanho: %zu byte\n", sizeof(bool));
    return 0;
}
$ gcc -std=c23 -Wall -Wextra bool.c -o bool
$ ./bool
chovendo: 1
pago: 0
pago: não
tamanho: 1 byte
bool chovendo = true;
Uma variável bool com o valor verdadeiro. bool, true e false são palavras da linguagem desde o C23: não precisam de #include. Escrevem-se em minúsculas e sem aspas ("true" seria um texto; veja a seção 4).
printf("…%d…", chovendo)
O printf não tem uma lacuna que escreva "true". Um bool vai na lacuna %d, como um número: true aparece como 1 e false como 0. É herança do C antigo, em que "verdadeiro" e "falso" eram só os números 1 e 0 (seção 5).
pago ? "sim" : "não"
Para mostrar uma palavra, escolha o texto conforme o valor: "se pago, o texto "sim"; senão, "não"". O ? : é o operador ternário, que tem aula própria (Operador Ternário); por enquanto, use-o como uma receita, com %s. Pode trocar por "true" : "false", "ligado" : "desligado", o que quiser.
sizeof(bool)
Um bool ocupa 1 byte (aula Tamanho dos Tipos), embora só precisasse de 1 bit: o byte é o menor pedaço de memória que o computador consegue endereçar.
E o %b?

O C23 trouxe ao printf a lacuna %b, que mostra um número em binário (base 2): no gcc, printf("%b\n", 5) mostra 101, e um bool verdadeiro sai 1. Não é "true" em palavras, e a biblioteca C do editor do guia ainda não conhece o %b (a linha sai vazia). Para booleanos, fique com %d ou com o ? :.

Missão: o programa mostra Estoque vazio? 0. Use a receita do ? : com %s para ele mostrar Estoque vazio? não.

#include <stdio.h>

int main(void) {
    bool estoque_vazio = false;
    printf("Estoque vazio? %d\n", estoque_vazio);
    return 0;
}
#include <stdio.h>

int main(void) {
    bool estoque_vazio = false;
    printf("Estoque vazio? %s\n", estoque_vazio ? "sim" : "não");
    return 0;
}

2. Comparações cabem num bool

Você raramente escreve true ou false à mão. Quase sempre, um bool nasce de uma pergunta: uma comparação.

#include <stdio.h>

int main(void) {
    int idade = 17;
    bool maior_de_idade = idade >= 18;
    bool adolescente = idade >= 13 && idade <= 19;
    printf("maior de idade: %d\n", maior_de_idade);
    printf("adolescente: %d\n", adolescente);
    printf("idade == 17: %d\n", idade == 17);
    printf("idade != 17: %d\n", idade != 17);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra compara.c -o compara
$ ./compara
maior de idade: 0
adolescente: 1
idade == 17: 1
idade != 17: 0
idade >= 18
"idade é maior ou igual a 18?" O C calcula a resposta (0, falso, porque 17 não é) e a guarda em maior_de_idade.
idade >= 13 && idade <= 19
Duas perguntas ligadas por && ("e"): só é verdadeiro se as duas forem. Os operadores lógicos (&&, ||, !) têm aula própria: Operadores Lógicos.
idade == 17
"É igual a 17?" Dois sinais de igual: um só (=) guardaria 17 na variável (aula Avisos).
idade != 17
"É diferente de 17?"

Um detalhe do C: a resposta de uma comparação é, tecnicamente, um int que vale 1 ou 0, não um bool (aula Operadores de Comparação). Na prática, tanto faz: guardada num bool, ela vira true ou false. Prefira bool para guardar respostas de sim ou não: o tipo já diz ao leitor o que a variável é.


3. Números viram bool (e vice-versa)

O C converte números em bool sozinho, com uma regra só: zero é false; qualquer outro valor é true. E no caminho inverso, true vira 1 e false vira 0.

#include <stdio.h>

int main(void) {
    bool a = 0;
    bool b = 1;
    bool c = -7;
    bool d = 0.0;
    bool e = 0.5;
    printf("%d %d %d %d %d\n", a, b, c, d, e);
    int f = 0.5;
    bool g = 2;
    int h = 2;
    printf("int 0.5 = %d, bool 2 = %d, int 2 = %d\n", f, g, h);
    printf("true + true = %d\n", true + true);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra converte.c -o converte
$ ./converte
0 1 1 0 1
int 0.5 = 0, bool 2 = 1, int 2 = 2
true + true = 2
ValorNum bool viraPor quê
0, 0.0false (0)Zero é o único "falso".
1, -7, 0.5, 2true (1)Diferente de zero. Até o negativo e o 0,5.
true numa conta ou num int1Por isso true + true deu 2.
false numa conta ou num int0

Repare na segunda linha da saída, que mostra a diferença entre bool e int:

  • int f = 0.5; deu 0: o int joga fora a parte depois da vírgula (aula Conversão de Tipos). Já bool e = 0.5; deu 1: 0,5 não é zero, então é verdadeiro.
  • bool g = 2; guardou 1, e int h = 2; guardou 2. Um bool só guarda 0 ou 1, nunca outro número. Por isso, para "sim ou não", ele é mais seguro que um int: não existe um "sim" valendo 2.

O gcc não avisou nada. O Clang, no editor do guia, avisa as duas conversões de 0.5: implicit conversion from 'double' to 'bool' changes value from 0.5 to true [-Wliteral-conversion] ("conversão implícita de double para bool muda o valor de 0.5 para true").

Essa regra é a mesma que o if usa: if (estoque) quer dizer "se o estoque não for zero" (aula Operadores Lógicos). E é por isso que as funções de ctype.h, como isdigit, podem devolver 2048 para dizer "sim" (no Linux, isdigit('7') vale 2048): qualquer número diferente de zero é verdadeiro. Guardado num bool, o 2048 vira 1 (aula Classificar Caracteres).

Missão: o programa antigo da Ana usava um int como interruptor, e alguém guardou 2 nele: a saída é Ligado: 2, que não faz sentido. Troque o tipo de ligado para bool (sem mudar o = 2) e rode: o que aparece agora?

#include <stdio.h>

int main(void) {
    int ligado = 2;
    printf("Ligado: %d\n", ligado);
    return 0;
}
#include <stdio.h>

int main(void) {
    bool ligado = 2;
    printf("Ligado: %d\n", ligado);
    return 0;
}

4. A pegadinha do texto "false"

$ cat texto.c
#include <stdio.h>

int main(void) {
    bool resposta = "false";
    printf("%d\n", resposta);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra texto.c -o texto
$ ./texto
1                        « o texto "false" virou verdadeiro!

Sem aviso nenhum, nem do gcc nem do Clang. O texto "false" (com aspas) não é o valor false: é um texto guardado em algum lugar da memória, e o C converteu o endereço desse lugar (que não é zero) em verdadeiro. Endereços são assunto de Ponteiros. A lição: true e false nunca levam aspas.


5. A história: int, _Bool, stdbool.h e o C23

Você vai encontrar booleanos escritos de três jeitos, conforme a idade do código:

ÉpocaComo se escreviaObservação
C89 / C90int pago = 1; ou #define TRUE 1Não havia tipo booleano: usava-se int, com 0 e 1 (e cada programa inventava os seus nomes).
C99 ao C17#include <stdbool.h> e bool pago = true;O tipo de verdade se chamava _Bool (um nome feio de propósito, para não quebrar programas que já usavam a palavra bool). O cabeçalho stdbool.h criava os apelidos bool, true e false.
C23bool pago = true;bool, true e false são palavras da linguagem. O stdbool.h ainda existe, para o código antigo continuar compilando, mas não é mais necessário.

Código do C99 continua compilando no C23, e o contrário funciona com o stdbool.h:

#include <stdio.h>
#include <stdbool.h>

int main(void) {
    bool pronto = true;
    _Bool velho = 1;
    printf("%d %d\n", pronto, velho);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra c99.c -o c99 && ./c99
1 1
$ gcc -std=c17 -Wall -Wextra c99.c -o c99 && ./c99
1 1

Sem o #include <stdbool.h>, com -std=c17, o bool é um nome desconhecido (unknown type name ‘bool’); a aula Versões do C mostra esse erro e tem uma missão sobre ele. O _Bool funciona em todas as versões desde o C99, mas ninguém escreve assim em código novo.

Código antigo que inventou o próprio bool

Antes do C99, muitos programas criavam o próprio tipo booleano. No C23, isso quebra, porque bool, true e false agora são palavras reservadas:

#include <stdio.h>

typedef enum { false, true } bool;

int main(void) {
    bool pronto = true;
    printf("%d\n", pronto);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra antigo.c -o antigo
antigo.c:3:16: error: cannot use keyword ‘false’ as enumeration constant
    3 | typedef enum { false, true } bool;
      |                ^~~~~
antigo.c:3:16: note: ‘false’ is a keyword with ‘-std=c23’ onwards
antigo.c:3:30: error: expected ‘;’, identifier or ‘(’ before ‘bool’
    3 | typedef enum { false, true } bool;
      |                              ^~~~
antigo.c:3:30: warning: useless type name in empty declaration
$ gcc -std=c17 -Wall -Wextra antigo.c -o antigo && ./antigo
1

A linha 3 (com typedef e enum, assuntos das aulas typedef e Enums) cria um tipo chamado bool com os valores false e true. O gcc explica: "não é possível usar a palavra-chave false como constante de enumeração", e a nota: "false é palavra-chave a partir de -std=c23". Com -std=c17, compila. Conserto, se o código for seu: apague a linha 3 e use o bool da linguagem. Se for um programa antigo de outra pessoa, compile-o com -std=c17.


6. Bons nomes para bool

Uma variável bool responde a uma pergunta de sim ou não. Dê a ela um nome que soe como a pergunta (ou a afirmação), para que o código se leia como uma frase. No estilo do guia, o snake_case (aula Nomes de Variáveis):

RuimBomLê-se
bool status;bool pago;"pago?"
bool flag;bool tem_estoque;"tem estoque?"
bool idade;bool maior_de_idade;"é maior de idade?"
bool chuva;bool esta_chovendo;"está chovendo?"
bool nao_ativo;bool ativo;Evite nomes negativos: "não não ativo" confunde.

Para onde o bool vai

Até aqui, você só mostrou booleanos. O verdadeiro uso deles é decidir: o if executa um trecho só se a condição for verdadeira, e o while repete enquanto ela for. Um gostinho do que vem em if e else:

$ cat gostinho.c
#include <stdio.h>

int main(void) {
    bool pago = true;
    if (pago) {
        printf("Pode retirar o pedido.\n");
    }
    return 0;
}
$ gcc -std=c23 -Wall -Wextra gostinho.c -o gostinho
$ ./gostinho
Pode retirar o pedido.

Com um bom nome, if (pago) se lê "se pago". Troque true por false, e a frase não aparece.


Quando dá errado

Ler um bool com scanf("%d"): stack smashing detected
$ cat lebool.c
#include <stdio.h>

int main(void) {
    bool pago = false;
    printf("Pagou? (1 = sim, 0 = não) ");
    scanf("%d", &pago);
    printf("pago = %d\n", pago);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra lebool.c -o lebool
lebool.c: In function ‘main’:
lebool.c:6:13: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘_Bool *’ [-Wformat=]
    6 |     scanf("%d", &pago);
      |            ~^   ~~~~~
      |             |   |
      |             |   _Bool *
      |             int *
$ echo 1 | ./lebool
Pagou? (1 = sim, 0 = não) pago = 1
*** stack smashing detected ***: terminated
Abortado                   (imagem do núcleo gravada) echo 1 | ./lebool

O que significa: o %d do scanf escreve um int inteiro (4 bytes) no endereço que recebeu, mas o bool só tem 1 byte. Os outros 3 bytes foram escritos por cima da memória vizinha, e a proteção do gcc percebeu no fim do programa: "esmagamento da pilha detectado" (a pilha é a área da memória das variáveis; aula Pilha e Heap). O aviso já dizia: %d espera int *, e veio _Bool * (o nome verdadeiro do bool). No editor do guia, o mesmo programa roda sem cair, o que é pior: o estrago acontece em silêncio.

Como consertar: não existe formato do scanf para bool. Leia num int e converta: int resposta = 0; scanf("%d", &resposta); bool pago = resposta; (aula Entrada com scanf).

O printf mostra 1 e 0 em vez de true e false

O que significa: é o comportamento normal: o printf não tem uma lacuna para bool em palavras.

Como consertar: printf("%s\n", pago ? "true" : "false"); (ou "sim"/"não").

Um bool deu verdadeiro quando deveria ser falso

O que significa: ele recebeu um número diferente de zero (até 0,5 ou −1), ou um texto entre aspas ("false"), que sempre vira verdadeiro.

Como consertar: use true/false sem aspas, ou uma comparação: bool tem_estoque = estoque > 0;.

unknown type name ‘bool’

O que significa: o código está sendo compilado com um C anterior ao C23 (-std=c17, -std=c99, ou um compilador antigo, como o gcc 13 e anteriores, que não usam o C23 por padrão).

Como consertar: compile com -std=c23, ou acrescente #include <stdbool.h> no topo (funciona nos dois).

cannot use keyword ‘false’ as enumeration constant

O que significa: um código antigo criou o próprio bool, true ou false, e no C23 essas palavras já são da linguagem (seção 5).

Como consertar: apague as definições antigas, ou compile esse programa com -std=c17.


Referência rápida

EscritaO que faz
bool pago = true;Variável booleana C23 (no C99 ao C17, com #include <stdbool.h>)
printf("%d", pago);Mostra 1 ou 0
printf("%s", pago ? "sim" : "não");Mostra uma palavra conforme o valor
bool b = x > 10;Guarda o resultado de uma comparação
bool b = numero;0 → false; qualquer outro → true
int n = true;true → 1; false → 0
sizeof(bool)1 (byte)
_BoolO nome antigo do tipo (C99); ainda funciona

Exercícios

1. O valor falso

Complete com o valor booleano "falso":

bool pago = ;

2. O cabeçalho antigo

No C99 ao C17, que cabeçalho é preciso incluir para escrever bool, true e false?

#include <>

3. Em palavras

Complete para mostrar sim ou não:

printf("%s\n", pago "sim" : "não");

4. O que printf("%d\n", 3 < 2); mostra?

5. Quanto vale bool b = -3; quando mostrado com %d?

6. E bool b = "false";?

7. Qual é o jeito seguro de ler "sim ou não" do teclado para um bool?

Missão final: votar e dirigir

Missão: o Bruno tem 17 anos. Crie duas variáveis bool: pode_votar (no Brasil, a partir de 16 anos) e pode_dirigir (a partir de 18), cada uma calculada com uma comparação usando idade. Mostre as duas em palavras, sem nenhum aviso:

Pode votar: sim
Pode dirigir: não
#include <stdio.h>

int main(void) {
    int idade = 17;
    // crie pode_votar e pode_dirigir
    // mostre as duas em palavras

    return 0;
}
#include <stdio.h>

int main(void) {
    int idade = 17;
    bool pode_votar = idade >= 16;
    bool pode_dirigir = idade >= 18;
    printf("Pode votar: %s\n", pode_votar ? "sim" : "não");
    printf("Pode dirigir: %s\n", pode_dirigir ? "sim" : "não");
    return 0;
}

Resumo

  • bool guarda true ou false (sem aspas) e ocupa 1 byte. No C23, são palavras da linguagem; do C99 ao C17, vêm do #include <stdbool.h> (o tipo de verdade é o _Bool).
  • O printf mostra um bool com %d: 1 ou 0. Para palavras, pago ? "sim" : "não" com %s.
  • Uma comparação (==, !=, <, >, <=, >=) vale 1 ou 0 e pode ser guardada num bool.
  • Zero vira false; qualquer outro número (até −7 e 0,5) vira true. true numa conta vale 1. Um bool nunca guarda outro número.
  • Um texto entre aspas, até "false", vira true.
  • Código antigo que criou o próprio bool não compila no C23: use o da linguagem, ou -std=c17.
  • Não leia um bool com scanf("%d"): leia num int.
  • Dê nomes que soem como perguntas: pago, tem_estoque, esta_chovendo.