Conditionals in C

October 5, 2026 • 5 min read

Conditionals in C
Table of contents

Part of the series:C Explained

Up to now every statement in a program ran in order, top to bottom. Real programs need branches: run this code only if something is true. That is what conditionals do. This post covers if/else, the else if chain, switch, and the truthiness rule that underpins all of them.

Truthiness in C

A condition is any expression whose value decides the branch. C does not require a real boolean here: any non-zero value is true, and zero is false. That means comparisons, which produce 1 or 0, work naturally, and so do plain numbers:

if (5) { ... }      // always true
if (0) { ... }      // never true
if (-1) { ... }     // true! (-1 is non-zero)

This rule is everywhere in C: in if, while, for, and even in return values. A function that returns 0 for “all good” is describing failure, because in C zero means false.

if / else

The if statement runs its block when the condition is true. An optional else runs when it is not:

int temperature = 35;

if (temperature > 30) {
    printf("It is hot.\n");
}
int score = 55;

if (score >= 60) {
    printf("Passed.\n");
} else {
    printf("Did not pass.\n");
}

For a single statement, the braces are optional:

if (score >= 60)
    printf("Passed.\n");

But always use braces anyway; adding one line later without braces silently changes the logic. Braces are how you say “this whole block belongs to the branch”.

else if: multiple options

Chain as many alternatives as you need:

int grade = 85;

if (grade >= 90) {
    printf("A\n");
} else if (grade >= 80) {
    printf("B\n");
} else if (grade >= 70) {
    printf("C\n");
} else {
    printf("Below C\n");
}

Only one branch runs: C tests each condition in order and stops at the first true one. The final else runs if none matched; it is optional.

Guard clauses are the other common pattern: check for the bad cases first and return early, leaving the main logic un-nested:

if (items <= 0) {
    return 0;
}
// ... main logic ...

Nested conditionals

An if can contain another if. Nesting is sometimes unavoidable, but deep nesting is hard to read. Prefer early returns and combining conditions:

if (age >= 18) {
    if (hasID) {
        printf("Welcome in.\n");
    }
}

The same logic reads better as:

if (age >= 18 && hasID) {
    printf("Welcome in.\n");
}

The dangling else

When you write else after nested ifs without braces, the else binds to the nearest unmatched if. That is the “dangling else” rule, and it trips everyone at least once:

if (a)
    if (b)
        printf("both\n");
else
    printf("a only\n");   // misleading: this else belongs to the inner if

Braces fix it. This is a general rule of the language: never be clever with optional braces.

switch: choosing from a list

When you compare one integer value against many possible constants, switch is cleaner than a long else if chain:

int day = 3;

switch (day) {
    case 1:
        printf("Monday\n");
        break;
    case 2:
        printf("Tuesday\n");
        break;
    case 3:
        printf("Wednesday\n");
        break;
    default:
        printf("Unknown day\n");
}

How it works: the expression selects a matching case, and execution starts there. The break is what stops execution from continuing into the next case. If you forget it, execution falls through to the next case, which is usually a bug but occasionally useful for sharing code between cases:

switch (month) {
    case 4:
    case 6:
    case 9:
    case 11:
        printf("30 days\n");
        break;
    case 2:
        printf("28 or 29 days\n");
        break;
    default:
        printf("31 days\n");
}

The default case catches anything unmatched, like else. switch works with int, char, and enum values, one of the places the upcoming structs post will mention again.

The assignment trap

The operators post warned about = vs ==. Inside a condition it turns from a bug into a trap with no compiler complaint:

if (x = 5) { ... }   // assigns 5 to x, then tests if 5 is non-zero: always true!
if (x == 5) { ... }  // what you meant

Compile with -Wall and GCC warns about most of these (-Wparentheses). Some codebases write if (5 == x) to make a mistaken = a compile error, because you cannot assign to a literal. If you ever see constants on the left, that is why.

The big picture

if/else branches on a condition, else if chains alternatives, switch picks from a list of constants, and the truthiness rule (zero is false, everything else is true) connects it all to the operators from the previous post. Braces keep branches honest, and break keeps switch cases separate. Now that the program can decide, the next step is repeating: loops.