Skip to content
VLSI Mentor

C Part I · Module 4 · Decisions and Control Flow

if, else and Multi-Way Decisions

Every program you have written so far has had exactly one possible execution: the statements inside main ran from the first to the last, each of them once, in the order you wrote them. That is a severe limitation, and it is the one this chapter removes. An if statement lets the value of an expression decide which statements run — which means the program now has more than one possible path through it, and you have to be able to say which one was taken and why. Nothing in this chapter is a new idea about values. The condition is an ordinary expression of the kind Module 3 spent twelve chapters on, judged against zero by the rule Chapter 2.10 established. What is new is that an expression's value can now change what executes next.

Foundation17 min readCPart IFundamentalsControl Flow

Part I · Module 4 · Chapter 4.1 · if, else and Multi-Way Decisions

1. Every Program So Far Had Only One Path

Look back at any example from the last three modules. Declarations, assignments, a few printf calls, return 0. Run it a thousand times with the same starting values and you get the same thing a thousand times, because there was never a choice to make. Execution entered main at the top and left at the bottom, and every statement in between ran exactly once.

Real programs are not like that. A program that reads a sensor has to behave differently when the reading is out of range. A program that processes a packet has to behave differently when the checksum fails. In every case the shape is the same:

Some statements should run only under a condition.

C's basic tool for that is the if statement.

2. The Smallest if

Azvya Education Pvt. Ltd.VLSI Mentor
running a statement only sometimes
#include <stdio.h>

int main(void)
{
    int temperature = 85;

    printf("checking\n");

    if (temperature > 70) {
        printf("too hot\n");
    }

    printf("done\n");

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
checking
too hot
done

Three things happened, and it is worth being precise about each.

The condition was evaluated. temperature > 70 is an expression — exactly the kind Chapter 3.5 introduced. With temperature holding 85, it produced the int value 1.

That value selected a path. Because the value was not zero, the statement after the condition ran.

Execution then continued past the whole if statement. printf("done\n") is not part of the if and runs either way.

Change temperature to 42 and the condition produces 0, the printf("too hot\n") is skipped, and the output is checking followed by done. The program text is identical; the path through it is not.

3. What the Condition Actually Is

Here is the rule, and it is smaller than most beginners expect:

if (expression) statement evaluates the expression. If the result compares unequal to zero, the statement runs. Otherwise it does not.

There is no separate notion of a "condition type" in C. The parentheses hold an ordinary expression, and its value is judged by the zero/non-zero rule from Chapter 2.10 — the same rule that governs &&, || and !.

That has a consequence you will meet in real code constantly: the expression does not have to be a comparison.

Azvya Education Pvt. Ltd.VLSI Mentor
a plain value used as a condition
#include <stdio.h>

int main(void)
{
    int error_count = 0;
    int retry_count = 3;

    if (error_count) {
        printf("there were errors\n");
    }

    if (retry_count) {
        printf("retries remain: %d\n", retry_count);
    }

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
retries remain: 3

if (error_count) means if error_count is not zero. if (retry_count) means if retry_count is not zero. Both are legal, both are idiomatic C, and both are judged by exactly the rule above.

Whether you should write them that way is a separate question. if (retry_count > 0) says what you mean more loudly, and costs nothing. The reason to understand the short form is that you will read it in other people's code, and misreading it as something special would be worse than writing it yourself.

Five stages. First, evaluate the condition, which is one ordinary expression producing one value. Second, compare that value with zero, using C's rule that zero is false and any other value is true. Third, if the value is non-zero, run the controlled statement. Fourth, if the value is zero, skip the controlled statement and run the else statement if one is present. Fifth, execution continues at the statement after the whole if, having taken exactly one of the two paths.What an if statement does01EVALUATE THE CONDITIONone ordinary expression, one value02COMPARE THAT VALUE WITH ZEROzero is false; anything else is true03NON-ZERO — RUN THE CONTROLLED STATEMENTthe branch is taken04ZERO — SKIP IT, OR RUN THE ELSE STATEMENTthe other branch, if there is one05CONTINUE AFTER THE WHOLE IF STATEMENTexactly one of the two paths ran
Figure 1 — how an if statement selects a path. The condition is an ordinary expression; the only thing that makes it a condition is where it is written. Its value is compared with zero, exactly one of the two paths runs, and execution then rejoins at the statement following the whole if. Nothing here requires a dedicated boolean type, which is why if worked in C for decades before bool existed.

4. else: the Other Path

if on its own gives you run this or do nothing. Attaching an else gives you run this or run that:

Azvya Education Pvt. Ltd.VLSI Mentor
choosing between two statements
#include <stdio.h>

int main(void)
{
    int temperature = 42;

    if (temperature > 70) {
        printf("too hot\n");
    } else {
        printf("within range\n");
    }

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
within range

The guarantee an else gives you is worth stating explicitly, because a great deal of correct code depends on it:

Exactly one of the two branches runs. Not both, and not neither.

5. if Controls Exactly One Statement

This is the single most important structural fact in the chapter, and the one that explains a bug you are otherwise doomed to write.

The form is:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if (expression) statement
if (expression) statement else statement

One statement — not "the indented part", and not "everything until the blank line". C does not use indentation to decide what is controlled. Indentation is for humans and the compiler ignores it entirely.

So how do the examples above control two or three statements? Because of this rule:

A block — a sequence of statements wrapped in braces — is itself one statement.

The braces are what turn several statements into the single statement that if controls. (Braces also affect where names declared inside them are visible; that is a separate topic, and Chapter 5.6 owns it. Here, braces are purely about grouping.)

What goes wrong without them

Azvya Education Pvt. Ltd.VLSI Mentor
a bug the compiler will warn about
#include <stdio.h>

int main(void)
{
    int faults = 0;

    if (faults > 0)
        printf("fault detected\n");
        printf("raising alarm\n");

    printf("end\n");

    return 0;
}

The author plainly intended both messages to depend on faults > 0. The indentation says so. Here is what actually runs with faults holding 0:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
raising alarm
end

The alarm was raised with no fault. The if controls the first printf only, because that is one statement and the rule above says one statement is all it gets. The second printf is simply the next statement in main, which runs unconditionally.

Modern compilers notice the mismatch between the indentation and the structure:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warning: misleading indentation; statement is not part of the previous 'if'
    [-Wmisleading-indentation]

That diagnostic is a warning, not an error — the program is perfectly legal C, it just does not do what the layout suggests. Chapter 1.5's habit of compiling with warnings enabled is what turns this from a silent logic bug into a message.

The fix is the braces:

Azvya Education Pvt. Ltd.VLSI Mentor
the corrected version
    if (faults > 0) {
        printf("fault detected\n");
        printf("raising alarm\n");
    }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
end

6. Multi-Way Decisions

Many decisions are not two-way. A level might be invalid, low, normal or high. C has no special construct for this, and does not need one — it falls out of what you already have.

Azvya Education Pvt. Ltd.VLSI Mentor
an else-if chain
#include <stdio.h>

int main(void)
{
    int level = 55;

    if (level < 0) {
        printf("invalid\n");
    } else if (level < 25) {
        printf("low\n");
    } else if (level < 75) {
        printf("normal\n");
    } else {
        printf("high\n");
    }

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
normal

else if is not a construct in C. There is no such keyword pair. What you are looking at is an else whose single controlled statement happens to be another if statement — which, as §5 established, is allowed, because an if statement is a statement. Written without the conventional formatting, the same code is:

Azvya Education Pvt. Ltd.VLSI Mentor
the same chain, formatted to show what it really is
    if (level < 0) {
        printf("invalid\n");
    } else {
        if (level < 25) {
            printf("low\n");
        } else {
            if (level < 75) {
                printf("normal\n");
            } else {
                printf("high\n");
            }
        }
    }

Everybody writes the first form, because the second drifts off the right-hand side of the screen for no benefit. But knowing they are the same thing tells you two useful facts:

The tests are tried in order, and the chain stops at the first match. level holding 55 is not less than 0, not less than 25, and is less than 75 — so normal prints and the final else is never reached. Once a branch is selected, none of the remaining conditions are evaluated at all.

A trailing else is optional. Without one, it is possible for no branch to run. With one, exactly one branch always runs. Which you want is a design decision, not a formatting one.

Chain order is part of the logic

Because the chain stops at the first match, reordering the branches can change the answer. This is not a style preference:

Azvya Education Pvt. Ltd.VLSI Mentor
classifying a reading
#include <stdio.h>

int main(void)
{
    const int minimum_valid = 0;
    const int maximum_valid = 4095;
    const int warning_level = 3000;
    const int critical_level = 3800;

    int sensor_connected = 1;
    int reading = 3900;

    if (!sensor_connected) {
        printf("no sensor attached\n");
    } else if (reading < minimum_valid || reading > maximum_valid) {
        printf("reading %d is outside the valid range\n", reading);
    } else if (reading >= critical_level) {
        printf("critical: %d\n", reading);
    } else if (reading >= warning_level) {
        printf("warning: %d\n", reading);
    } else {
        printf("normal: %d\n", reading);
    }

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
critical: 3900

Now swap only the critical_level and warning_level branches, changing nothing else:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warning: 3900

The same reading, from the same sensor, with the same thresholds, has been downgraded from critical to a warning — because 3900 is greater than or equal to 3000, so the warning test matched first and the critical test was never evaluated. Overlapping conditions must be ordered from the most specific to the least specific, and in a threshold chain that means most severe first.

Notice also that the validity checks come before the classification checks. Deciding whether a value is meaningful before deciding what it means is a habit worth forming now; it is the same reasoning that makes condition order a correctness question in Chapter 3.6.

7. Two Traps Worth Knowing by Name

The dangling else

When an else could plausibly belong to either of two nested if statements, which one does it attach to?

Azvya Education Pvt. Ltd.VLSI Mentor
where does this else belong?
#include <stdio.h>

int main(void)
{
    int a = 0;
    int b = 0;

    if (a > 0)
        if (b > 0)
            printf("both positive\n");
    else
        printf("a is not positive\n");

    printf("end\n");

    return 0;
}

The indentation claims the else belongs to the outer if. The author expected a is not positive. Here is what runs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
end

Nothing from the if printed at all. The rule is:

An else is associated with the nearest preceding if that does not already have one.

So the else belongs to if (b > 0), not if (a > 0). Since a > 0 was false, the entire outer if statement — inner if, else and all — was skipped.

Clang says so directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warning: add explicit braces to avoid dangling else [-Wdangling-else]

And the cure is the one from §5. With braces there is no ambiguity to resolve, because the inner if is closed before the else appears:

Azvya Education Pvt. Ltd.VLSI Mentor
braces remove the ambiguity
    if (a > 0) {
        if (b > 0) {
            printf("both positive\n");
        }
    } else {
        printf("a is not positive\n");
    }

This trap only exists for people who omit braces. That is the whole reason §5's rule is worth following mechanically.

Assignment where you meant comparison

Chapter 3.4 established something that makes this trap possible: assignment is an expression, and its value is the value assigned. Combine that with §3 — a condition is any expression — and if (status = 1) is perfectly legal C.

Azvya Education Pvt. Ltd.VLSI Mentor
one character wrong
#include <stdio.h>

int main(void)
{
    int status = 0;

    if (status = 1) {
        printf("status is 1\n");
    }

    printf("status now holds %d\n", status);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
status is 1
status now holds 1

Both lines are wrong, in different ways. status was 0, so the branch should not have been taken — but the condition was not a test at all. It assigned 1 to status, and the whole expression then had the value 1, which is non-zero, so the branch ran. And as a parting gift, the variable the program was supposedly inspecting has been modified.

The diagnostic names it precisely:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warning: using the result of an assignment as a condition without parentheses
    [-Wparentheses]
note: use '==' to turn this assignment into an equality comparison

8. The Mental Model to Keep

A condition is just an expression. if evaluates it, compares the result with zero, and uses that to choose which single statement runs next. A block is one statement. else if is nothing but an else whose statement is another if.

Everything in this chapter is a consequence of those four sentences, including both traps.

9. What You Should Be Able to Say Now

  1. Before this chapter, every program had exactly one path; if is what introduces a choice.
  2. An if condition is an ordinary expression, judged by the rule that zero is false and anything else is true.
  3. The condition does not have to be a comparison — if (count) is legal and means if (count != 0).
  4. if controls exactly one statement; a braced block is one statement.
  5. Indentation has no effect on what is controlled — only braces do.
  6. else guarantees that exactly one of the two branches runs.
  7. else if is not a construct; it is an else whose statement is another if.
  8. A chain is tried in order and stops at the first match, so overlapping conditions must go from most specific to least specific.
  9. An else attaches to the nearest preceding if that does not already have one.
  10. if (x = 1) assigns and then tests; -Wparentheses catches it.

10. Exercises

Exercise 1 — predict the output. Do not compile yet. Write down what each program prints, then check.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int value = 0;
if (value) {
    printf("A\n");
}
if (value == 0) {
    printf("B\n");
}
if (!value) {
    printf("C\n");
}

Exercise 2 — trace the chain. For level holding each of −5, 0, 24, 25, 74, 75 and 200, say which branch of §6's first chain runs. Two of those values sit exactly on a boundary; say which branch takes them and why.

Exercise 3 — find the bug. This is supposed to print the warning only for readings above the threshold. Say exactly what it prints when reading holds 10, and name the rule that explains it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int reading = 10;
if (reading > 100)
    printf("above threshold\n");
    printf("logging the event\n");

Exercise 4 — repair it. Fix Exercise 3 with the smallest change that makes the program match its evident intent, and say which compiler warning would have revealed the problem.

Exercise 5 — explain the silence. In §7's dangling-else program, nothing at all was printed by the if. Explain why, in terms of which if the else belongs to and which condition was false.

Exercise 6 — classify the mistake. A colleague writes if (ready = 1) and reports that the branch runs every time, whatever ready held before. Explain what the expression evaluates to, why that makes the branch unconditional, and what ready holds afterwards.

Exercise 7 — order the chain. A valve controller must report shut below 5, throttled below 50, and open otherwise, and must reject any value above 100 as invalid. Write the chain. Then say what goes wrong if the invalid check is placed last instead of first.

Exercise 8 — compare two versions. One programmer writes if (count != 0) and another writes if (count). Say whether the two differ in behaviour, then say which you would put in code intended to be read by somebody else, and why.

11. Summary

  • An if statement makes a statement run only when a condition holds — the first construct in this curriculum that gives a program more than one path.
  • The condition is an ordinary expression, compared with zero. Nothing about it is special except its position.
  • if controls exactly one statement, and a block is one statement. Indentation controls nothing.
  • else supplies the other path, and guarantees exactly one of the two runs.
  • An else if chain is nested if statements. It is tried in order and stops at the first match, so order is logic, not formatting.
  • The dangling else attaches to the nearest preceding if. Braces make the question disappear.
  • = is not ==. Assignment in a condition is legal, assigns, and then tests the assigned value.

A two-way decision is now straightforward, and a multi-way decision costs you a chain. But an if statement is a statement — it chooses what to do, and it cannot be used where a value is needed. The next chapter introduces the one operator in C that makes a choice and produces a value, along with the type rules that decide what that value is.

Standards & specifications

Governing standard
ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)

Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.