Menu

Booleanos en C: stdbool.h, _Bool y la verdad por valor

C no tuvo un tipo booleano hasta C99. Cero es falso, todo lo demás es verdadero, las comparaciones dan int - y stdbool.h te añade bool, true y false. Además, el error de = frente a == que este diseño habilita.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

C pasó sus primeros 27 años sin un tipo booleano. Eso no es un descuido: es una decisión de diseño con consecuencias que encuentras en tu primer día, y un error famoso que se deriva directamente de ella.

Cero es falso, todo lo demás es verdadero

No hay un valor especial de verdadero/falso en las reglas de condición de C. if, while, for, &&, || y ! hacen exactamente una pregunta: ¿este valor es cero?

Los números negativos son verdaderos. Las fracciones son verdaderas. Un puntero no nulo es verdadero. Solo el cero, en cualquier tipo, es falso.

Por eso el código en C está lleno de modismos que parecen escuetos hasta que conoces la regla:

if (count)            /* si count no es cero */
if (!count)           /* si count es cero */
if (p)                /* si p no es NULL */
if (!strcmp(a, b))    /* si las cadenas son iguales - strcmp devuelve 0 al coincidir */

Ese último pilla a mucha gente. strcmp devuelve 0 cuando las cadenas coinciden, así que "iguales" se lee como "no" dentro de un if. Escribir if (strcmp(a, b) == 0) es más claro y hace exactamente lo mismo.

Las comparaciones producen int

Una comparación en C no es una clase especial de expresión. Se evalúa a un int corriente con el valor 1 o 0, y puedes imprimirlo, guardarlo o hacer aritmética con él:

El operador ! funciona igual: produce 1 si su operando es cero y 0 en caso contrario. Así que !!x es un modismo clásico que normaliza cualquier valor a exactamente 0 o 1.

bool, true y false (C99)

C99 añadió un tipo booleano de verdad llamado _Bool, y una cabecera que le da un nombre legible:

<stdbool.h> es diminuta: define bool como _Bool, true como 1 y false como 0. Eso es toda la cabecera. Pero usarla hace visible la intención de una forma que int flag = 1; nunca logra.

_Bool tiene un comportamiento genuinamente distinto: almacena solo 0 o 1. Asignar cualquier valor distinto de cero lo convierte exactamente en 1:

Sigue sin haber un %b en printf para booleanos: imprímelos con %d, o conviértelos a texto tú mismo:

En C23 esto se volvió aún más simple: bool, true y false pasaron a ser palabras clave de verdad, así que el include ya no es necesario. La mayoría del código actual todavía apunta a C17, así que conserva el #include <stdbool.h>.

Devolver un booleano desde una función

Aquí es donde bool se gana su lugar con más claridad:

Un tipo de retorno bool le dice a quien llama qué significa el valor. Un retorno int desde una función llamada check_file podría ser un booleano, un conteo o un código de error - y en las convenciones de la biblioteca de C suele ser lo tercero, donde 0 significa éxito. Nombrar el tipo elimina la adivinanza.

El error de = frente a ==

Como la asignación es una expresión que produce el valor asignado, y como cualquier valor distinto de cero es verdadero, esto compila:

Salieron mal dos cosas a la vez. La condición asignó 5 a x, destruyendo su valor, y luego probó 5, que es distinto de cero, así que la rama se ejecutó. El programa no tiene ningún error ni ninguna advertencia de forma predeterminada.

Peor aún, la versión con 0 nunca ejecuta su rama:

if (found = 0) { /* nunca se toma - y found ahora vale 0 */ }

Tres defensas, en orden de utilidad:

Compila con advertencias. -Wall lo detecta:

warning: suggest parentheses around assignment used as truth value

Llegar a cero advertencias es la solución real aquí, y cuesta una sola opción.

Escribe la constante primero. La "condición Yoda" convierte la errata en un error de compilación:

if (5 == x)   /* correcto */
if (5 = x)    /* error: no es un lvalue - el compilador te detiene */

A algunos equipos les encanta y otros lo encuentran ilegible; ambas posturas son defendibles, pero funciona.

Sé deliberado cuando de verdad lo quieras. Asignar dentro de una condición es genuinamente útil para leer entrada:

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

Los paréntesis extra alrededor de c = getchar() son obligatorios por precedencia, y además señalan que la asignación es intencional. Ese es el modismo: si tu asignación dentro de una condición no se parece a eso, probablemente sea una errata.

Errores booleanos comunes

Comparar con true. if (flag == true) funciona con stdbool.h, pero se rompe en cuanto flag es un int que vale 42, porque 42 == 1 es falso. Escribe if (flag).

Comparaciones encadenadas. if (0 < x < 10) siempre es verdadero: 0 < x da 0 o 1, y ambos son menores que 10. Escribe if (x > 0 && x < 10).

Bit a bit en lugar de lógico. & y | no son && ni ||. No hacen cortocircuito, y combinan bits:

Ambos operandos son "verdaderos" y, aun así, a & b vale 0. Usar & donde querías && produce una condición que solo está mal para ciertos valores, que es el tipo de error más difícil de encontrar.

Comparar floats por igualdad. if (0.1 + 0.2 == 0.3) es falso. Compara contra una tolerancia en su lugar; ver tipos de datos para saber por qué.

Preguntas frecuentes

¿C tiene un tipo booleano?

Desde C99, sí: _Bool es un tipo integrado, e incluir <stdbool.h> te da los nombres más amables bool, true y false. Antes de C99 no había ningún booleano: los programas usaban int con 0 para falso y 1 para verdadero, lo que todavía funciona y sigue siendo común.

¿Qué cuenta como verdadero en C?

Cualquier valor distinto de cero. 1, -5, 0.01 y un puntero no nulo son verdaderos en una condición; solo 0, 0.0 y un puntero nulo son falsos. No hay una comprobación booleana aparte: if (x) simplemente prueba si x es distinto de cero.

¿Cómo uso bool en C?

Añade #include <stdbool.h> y entonces bool ready = true; funciona como esperarías. Por debajo, bool es _Bool, que almacena solo 0 o 1: asignarle cualquier valor distinto de cero guarda exactamente 1.

¿Cuál es la diferencia entre = y == en C?

= asigna, == compara. if (x = 5) asigna 5 a x y luego prueba 5, que es distinto de cero, así que la rama siempre se ejecuta. if (x == 5) comprueba la igualdad. La primera compila sin error, y por eso existen tanto -Wall como la costumbre de escribir if (5 == x).

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR