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

Booleans in C β€” truthy & falsy

In C: 0 is false, anything else is true. Simple rule, lots of pitfalls. Combined with C99's bool type, this page shows you the safe way.

K&R days β†’ C99 bool

Early C had no boolean type. Everything used 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 stores 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 a condition is tested (if, while, for, ternary ?:):
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: -1 and 42 are both truthy. If you need to distinguish them, compare explicitly β€” "equals 1" is not the same as "is true."

Result of comparison operators

The result of ==, !=, <, >, <=, >= 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: add comparison results
int count = 0;
for (int i = 0; i < n; i++) {
    count += (a[i] > 0);   // count 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 use if (p). Readability-first codebases prefer if (p != NULL). Mostly a team-style question.

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
}
Checking exact 0.0 is fine (0 is representable). For computed values, an epsilon 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: if you're using a value as a boolean, just test it: if (x) / if (!x). Never compare to true. If you really need 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
}
Prevention: -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))      // either x or y is false
// Different meaning! Use parentheses.

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 into 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

More explicit alternative: bool b = (x != 0); reads better. Assigning to _Bool also auto-normalizes: _Bool b = x; works the same.

Short-circuit evaluation

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

&& 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; 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 if 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 and check.
Challenge 2: the if (x == true) trap
With int x = 2;, write a program that shows if (x == true) and if (x) taking different branches.
Challenge 3: float equality
Show that 0.1 + 0.2 == 0.3 is false, then fix it with an epsilon comparison.
Challenge 4: NULL-safe with short-circuit
Write a function taking char *s that returns 1 when s is non-NULL and its first char is 'A'. Use && in the right order to avoid NULL deref.
Challenge 5: count via comparison
Count how many elements of an int array are β‰₯ 0 without using if: add the boolean-valued comparison expression inside a loop.