Conditionals in C
October 5, 2026 • 5 min read

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.
/sponsor
Enjoyed this post? You can sponsor me and this site.