FirstHack Learn
Log in Sign up free

Conditionals: if, else and switch

9 min read · 39 views

Conditionals: if, else and switch

Everything a program does that feels intelligent comes down to choosing between paths. C gives you three tools for that choice: if, switch, and the conditional operator. They differ in what they can test and in how readable they are, not in what they can achieve.

The if statement

C
#include <stdio.h>

int main(void)
{
    int marks = 63;

    if (marks >= 40) {
        printf("Result: Pass\n");
    } else {
        printf("Result: Fail\n");
    }

    if (marks >= 75) {
        printf("Distinction awarded\n");
    }

    printf("Marks recorded: %d\n", marks);

    return 0;
}

The condition inside the brackets is evaluated, and if the result is anything other than zero the block runs. There is no true or false keyword in classic C; there are only numbers, and zero means false.

The braces are technically optional for a single statement, but write them anyway. Without them, only the very next statement belongs to the if, and indentation lies to you:

TEXT
if (marks >= 40)
    printf("Pass\n");
    printf("Well done\n");    /* always runs, regardless of marks */

This has caused real security bugs in production software. Two extra characters prevent it.

if, else if, else

For more than two outcomes, chain the tests. Order matters, because the first matching branch wins and the rest are skipped entirely.

C
#include <stdio.h>

int main(void)
{
    int marks = 82;
    char grade;

    if (marks >= 90) {
        grade = 'O';
    } else if (marks >= 80) {
        grade = 'A';
    } else if (marks >= 70) {
        grade = 'B';
    } else if (marks >= 60) {
        grade = 'C';
    } else if (marks >= 40) {
        grade = 'D';
    } else {
        grade = 'F';
    }

    printf("Marks %d -> Grade %c\n", marks, grade);

    return 0;
}

Notice that the second test is written as marks >= 80 rather than marks >= 80 && marks < 90. The extra condition is unnecessary: if the program reached that line at all, the first test already failed, so marks must be below 90. Writing the chain from highest to lowest lets each test stay simple.

Reverse the order and the whole thing breaks. If marks >= 40 came first, every passing student would get a D.

The switch statement

When you are comparing one variable against a list of exact constant values, switch states that intent more clearly than a long chain.

C
#include <stdio.h>

int main(void)
{
    int day = 3;

    switch (day) {
        case 1:
            printf("Monday\n");
            break;
        case 2:
            printf("Tuesday\n");
            break;
        case 3:
            printf("Wednesday\n");
            break;
        case 6:
        case 7:
            printf("Weekend\n");
            break;
        default:
            printf("Not a valid day\n");
    }

    return 0;
}

Three rules govern switch.

  1. The controlling expression must be an integer type. That includes char, since a char is a small integer. It cannot be a float, and it cannot be a string.
  2. Each case label must be a compile-time constant. case x: where x is a variable will not compile, and neither will case marks > 50:.
  3. Execution falls through from one case into the next unless you write break.

That third rule surprises people. switch jumps to the matching label and then keeps running downwards, ignoring further case labels, until it meets a break or the closing brace. Sometimes that is exactly what you want, as with cases 6 and 7 above sharing one body. Far more often, a missing break is a bug.

⚠️A missing break is silent

Delete the break after case 2 in the program above and set day to 2. You get both Tuesday and Wednesday. Nothing warns you by default. Add break to every case as a reflex, including the last one, so that inserting a new case later does not break the old one.

The ternary operator

For a simple two-way choice that produces a value, the conditional operator is compact and clear.

C
#include <stdio.h>

int main(void)
{
    int a = 47, b = 62;

    int larger = (a > b) ? a : b;
    printf("Larger value : %d\n", larger);

    int marks = 38;
    printf("Status       : %s\n", marks >= 40 ? "Pass" : "Fail");

    int n = -15;
    printf("Absolute     : %d\n", n < 0 ? -n : n);

    return 0;
}

Read condition ? x : y as "if condition, then x, otherwise y". It is an expression, so it produces a value you can assign or pass to a function. That is its real advantage over if, which is a statement and produces nothing.

Use it for short, obvious choices. Nesting ternaries inside ternaries technically works and reliably produces code nobody can read, including you next week.

The bug that catches everybody

C
#include <stdio.h>

int main(void)
{
    int marks = 30;

    if (marks == 100) {
        printf("This does not print. Correct.\n");
    }

    printf("marks is still %d\n", marks);

    int x = 0;
    if ((x = 5)) {
        printf("This DOES print, and x is now %d\n", x);
    }

    return 0;
}

== compares. = assigns. Writing if (marks = 100) does three things at once: it overwrites marks with 100, it evaluates to 100, and since 100 is non-zero the branch always runs. Your original data is gone and the condition is meaningless.

The compiler usually warns about this, which is one more reason to compile with -Wall. Some programmers write the constant on the left, as in if (100 == marks), so that a slip becomes a hard compile error rather than a silent bug. You will see that style in textbooks and older codebases.

Choosing between them

Situation Best tool
Two outcomes, complex condition if / else
Ranges, such as grade boundaries else if chain
One variable against many exact constants switch
Picking one of two values for an assignment Ternary
Testing a float, a string, or a range Never switch

Common mistakes

  • Using = instead of ==. Assigns instead of comparing, and the branch runs. Compile with -Wall so you are told.
  • Semicolon after if. if (x > 5); ends the statement immediately, so the block below always runs. This is legal C and produces no error.
  • Forgetting break in a switch. Cases fall through into each other. Add it every time.
  • Chaining comparisons like maths. if (0 < x < 100) compiles but is wrong: 0 < x yields 0 or 1, and both are less than 100, so the condition is always true. Write if (x > 0 && x < 100).
  • Comparing floats with ==. Rounding means 0.1 + 0.2 == 0.3 is false. Compare the difference against a small tolerance instead.
  • Ordering an else-if chain wrongly. From loosest test to strictest, every value lands in the first branch. Go from strictest to loosest.

Branching is where a program stops being a straight line. Write a grade calculator, a leap-year checker and a simple menu with switch, and the patterns will settle quickly. There are graded problems waiting on the Practice page.

Create a free account to track what you have finished.