πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English

Booleans in C β€” truthy & falsy

In C, 0 is false and everything else is true. A simple rule with plenty of pitfalls. This page walks through the safe way, including C99's bool type.

K&R days β†’ C99 bool

Early C had no boolean type at all. Code relied entirely on the "0 is false, anything else is true" rule.

Old style (pre-C99)

int done = 0;         // 0 = false
while (!done) {
    if (condition) done = 1;    // 1 = true
}

C99 and later

#include <stdbool.h>   // provides bool, true, false

bool done = false;
while (!done) {
    if (condition) done = true;
}
Under the hood: bool is a macro for _Bool, and true/false are just 1/0. _Bool is a special type that only ever stores 0 or 1 β€” assigning any non-zero value gives you 1.
_Bool b = 42;     // becomes 1 (not 42)
printf("%d\n", b);  // β†’ 1

int i = 42;
printf("%d\n", i);  // β†’ 42

Truthy / falsy rules

Anywhere C tests a condition β€” if, while, for, ternary ?: β€” the rule is:
0 β†’ false
non-zero β†’ true
NULL β†’ false (pointer 0)
0.0 β†’ false (float 0)
if (0)   { // not taken }
if (-1)  { // taken (non-zero is true) }
if (42)  { // taken }
if (0.0) { // not taken }

char *p = NULL;
if (p)    { // not taken (NULL is 0) }
"True" isn't a single value: both -1 and 42 are truthy. If you need to tell them apart, compare explicitly β€” "equals 1" is not the same as "is true."

Result of comparison operators

The result of ==, !=, <, >, <=, and >= is always an int β€” either 0 or 1, not true/false.
int a = 5, b = 3;
int r = (a > b);   // r == 1 (int)
printf("%d\n", r);  // 1

// Handy trick: sum the boolean results directly.
int count = 0;
for (int i = 0; i < n; i++) {
    count += (a[i] > 0);   // count the positive elements
}

Pointers & floats

Pointers: NULL or not

char *p = malloc(100);
if (p) {
    // not NULL β†’ allocation succeeded
    free(p);
}

// equivalent
if (p)          { /* ... */ }
if (p != NULL) { /* ... */ }
Which style? K&R-style codebases prefer if (p); readability-first codebases prefer if (p != NULL). It's mostly a team-style choice.

Floats: beware == 0.0

❌ Exact float equality

double x = 0.1 + 0.2;
if (x == 0.3) {       // false!
    // actual x β‰ˆ 0.30000000000000004
}

βœ… Epsilon comparison

#include <math.h>

double x = 0.1 + 0.2;
if (fabs(x - 0.3) < 1e-9) {
    // equal within tolerance
}
Comparing exactly against 0.0 is fine (zero is representable). For computed values, an epsilon comparison is safer.

Common bugs

Bug 1: if (x == true) is dangerous

int x = 2;           // 2 is truthy but not 1

if (x == true) {     // NG: true is 1 β†’ 2 == 1 is false
    printf("not taken\n");
}

if (x) {            // OK: non-zero is true
    printf("taken\n");
}
Rule of thumb: if you're using a value as a boolean, just test it directly with if (x) or if (!x). Never compare to true. If you really need a clean 0/1, use the !!x idiom below.

Bug 2: = vs ==

❌ Assignment

if (x = 5) {
    // "assign 5 to x, then test 5"
    // always true
}

βœ… Comparison

if (x == 5) {
    // test equality
}
How to avoid it: -Wall warns on if (x = 5). Some teams use "Yoda conditions" (if (5 == x)) so a typo becomes a compile error.

Bug 3: ! precedence

if (!x && y)        // (!x) && y β€” "x is false AND y is true"
if (!(x && y))      // "at least one of x or y is false"
// Different meanings β€” use parentheses when in doubt.

Bug 4: bitwise vs logical

if (a & b)   // bitwise AND: any common set bit
if (a && b)  // logical AND: both a and b are truthy

// a = 4 (100), b = 3 (011)
// a & b  β†’ 0 (no common bit) β†’ falsy
// a && b β†’ true (both non-zero)

Idiom: !!x to normalize to 0/1

Applying ! twice collapses any value down to exactly 0 or 1.
int x = 42;
int normalized = !!x;   // 42 β†’ !42=0 β†’ !0=1 β†’ 1

int y = 0;
int n2 = !!y;           // 0 β†’ !0=1 β†’ !1=0 β†’ 0

When it's useful

A more explicit alternative: bool b = (x != 0); reads better. Assigning to _Bool also normalizes automatically, so _Bool b = x; works the same way.

Short-circuit evaluation

&& and || evaluate left to right and stop as soon as the result is determined.

&& example

if (p != NULL && p->value > 0) {
    // if p is NULL, p->value is not evaluated (no crash)
}

|| example

if (x == 0 || y / x > 10) {
    // if x is 0, y/x is not evaluated (no division by zero)
}
Order matters: x == 0 || y / x > 10 is safe, but y / x > 10 || x == 0 can still divide by zero. Always put the guard first.

Side-effect idioms

// Call a function only when a flag is set.
if (mode_debug && log_debug(msg)) {
    // log_debug runs only when mode_debug is truthy
}
Heads-up: unlike JavaScript, C's || returns 0 or 1 β€” not the operand value. So x = get_value() || 10; does not give you a "default value"; it gives you 0 or 1.

Challenges

Challenge 1: predict then run
Predict the output of
printf("%d %d %d %d\n", !0, !1, !!5, !!-3);
then compile it and check.
Challenge 2: the if (x == true) trap
With int x = 2;, write a program that shows if (x == true) and if (x) going down different branches.
Challenge 3: float equality
Show that 0.1 + 0.2 == 0.3 evaluates to false, then fix it with an epsilon comparison.
Challenge 4: NULL-safe with short-circuit
Write a function that takes a char *s and returns 1 when s is non-NULL and its first character is 'A'. Use && in the right order to avoid a NULL dereference.
Challenge 5: count via comparison
Count how many elements of an int array are β‰₯ 0 without using if β€” just add up the boolean-valued comparison expression inside a loop.

Review Quiz

Check your understanding of this lesson!

Q1. In C, which value is treated as "false"?

0
Any negative number
Any number other than 0.0

Only 0 is false. Any non-zero value, including negative numbers, counts as true.

Q2. What types and constants become available after including stdbool.h?

bool, true, false
Boolean, TRUE, FALSE
bit, on, off

Since C99, including <stdbool.h> gives you bool, true, and false.

Q3. When does if (x) evaluate x as true?

When x is any non-zero value
Only when x is a positive integer
Only when x is the true constant

In C, 0 is false and anything non-zero β€” including negative numbers and non-NULL pointers β€” is true.