Control Statements
& Program Flow
Give your programs the power of decision — if, else, switch, and loops. The building blocks that transform a linear list of instructions into intelligent, responsive software.
Introduction to Control Statements
Real programs don’t simply march through instructions in a straight line. They face questions and must respond to data dynamically. Is the student’s mark above 50? Is the button currently pressed? Has the sensor exceeded 60 ’C? Depending on the answer, completely different code should run.
This ability to make decisions and repeat actions is what separates a program from a calculator. Control statements give a C program its intelligence.
Student passed?
If marks ≥ 35, print “Pass”. Otherwise print “Fail”. One condition, two outcomes.
ATM PIN correct?
Only allow withdrawal if PIN matches. Nested decisions: correct PIN → sufficient balance → dispense cash.
Temperature > 60 ’C?
Turn the cooling fan ON. Otherwise keep it OFF. A real embedded system decision loop.
Print 1 – 100
Instead of 100 printf calls, a single loop runs the same line 100 times automatically.
Why Control Statements Exist
Think of a traffic signal. It doesn’t open the road permanently — it makes a decision on every cycle:
Programs model exactly this logic. Without control statements, you could only write programs that do the same fixed thing every time — no user input, no sensors, no conditions, no loops. That covers almost nothing useful.
Real-World Decision Examples
| Situation | Condition | True action | False action |
|---|---|---|---|
| ATM machine | PIN correct? | Allow withdrawal | Show error |
| Online payment | Payment successful? | Place order | Cancel order |
| Embedded fan | Temperature > 60’C? | Turn fan ON | Keep fan OFF |
| Login system | Username & password match? | Grant access | Reject login |
| Sensor alarm | Pressure > threshold? | Trigger alarm | Normal operation |
Classification of Control Statements
C’s control statements fall into three families. This chapter covers them all.
Three families of C control statements — all covered in this chapter
The if Statement
The if statement is the most fundamental control statement. It executes a block of code only when a condition evaluates to true. If the condition is false, the block is skipped entirely.
if(condition)
{
/* Runs only when condition is true (non-zero) */
statements;
}The if block only runs when the condition is true. False skips it and program continues normally.
Examples
#include <stdio.h>
int main()
{
int age = 20;
if(age >= 18)
{
printf("Eligible to Vote\n"); /* condition is true */
}
int marks = 90;
if(marks >= 35)
{
printf("Pass\n"); /* condition is true */
}
int number = 12;
if(number % 2 == 0)
{
printf("Even Number\n"); /* condition is true */
}
return 0;
}if(100) executes the block (100 is non-zero = true). This is important: it means a non-NULL pointer is “true”, a non-zero sensor reading is “true”, etc.if…else Statement
When there are exactly two possible outcomes, use if else. Exactly one of the two blocks will execute — never both, never neither.
if(condition)
{
/* Runs when condition is TRUE */
}
else
{
/* Runs when condition is FALSE */
}Exactly one branch always executes. The two paths merge and continue together after the if-else.
/* Voting eligibility */
int age = 15;
if(age >= 18) printf("Adult\n");
else printf("Minor\n"); /* Minor */
/* Even or odd */
int number = 7;
if(number % 2 == 0) printf("Even\n");
else printf("Odd\n"); /* Odd */
/* Embedded: fan control */
int temperature = 45;
if(temperature > 40) printf("Fan ON\n"); /* Fan ON */
else printf("Fan OFF\n");Nested if
A nested if is an if statement placed inside another if block. It is used when a second condition only makes sense to check after the first condition is true.
if(condition1)
{
/* outer condition is true */
if(condition2)
{
/* both conditions are true */
statements;
}
}#include <stdio.h>
int main()
{
int age = 25;
int citizen = 1; /* 1 = yes, 0 = no */
if(age >= 18)
{
/* outer: old enough? */
if(citizen)
{
/* inner: is a citizen? */
printf("Eligible to Vote\n");
}
else
{
printf("Not a citizen\n");
}
}
else
{
printf("Too young to vote\n");
}
return 0;
}if makes code very hard to read. When you find yourself with deeply nested conditions, consider using logical operators (&&, ||) to combine them, or restructure with early returns.else-if Ladder
When there are more than two mutually exclusive outcomes, use an else-if ladder. The conditions are tested top-to-bottom; the first matching block executes, and all others are skipped.
if(condition1) { /* first choice */ }
else if(condition2) { /* second choice */ }
else if(condition3) { /* third choice */ }
else { /* fallback */ }#include <stdio.h>
int main()
{
int marks = 82;
if(marks >= 90)
printf("Grade A\n");
else if(marks >= 75)
printf("Grade B\n"); /* This executes: 82 >= 75 */
else if(marks >= 50)
printf("Grade C\n");
else
printf("Fail\n");
return 0;
}Why else-if Is Better Than Separate ifs
switch Statement
The switch statement is the ideal tool when you need to select one option from a fixed set of integer or character values. It is cleaner and often faster than a long else-if ladder for menu-driven programs.
switch(expression)
{
case constant1:
statements;
break; /* ESSENTIAL: exits the switch */
case constant2:
statements;
break;
default:
statements; /* runs if no case matches */
}How switch Executes
int day = 3;).default.break is encountered or the closing brace.} of the switch block.#include <stdio.h>
int main()
{
int day = 3;
switch(day)
{
case 1: printf("Monday\n"); break;
case 2: printf("Tuesday\n"); break;
case 3: printf("Wednesday\n"); break; /* matches */
case 4: printf("Thursday\n"); break;
case 5: printf("Friday\n"); break;
default: printf("Weekend\n");
}
return 0;
}Fall-Through: What Happens Without break
Without break, execution falls through into subsequent cases. Always add break unless you intend fall-through.
switch vs else-if Ladder
| Aspect | switch | else-if ladder |
|---|---|---|
| Works with | Integer & char constants | Any expression |
| Works with floats/strings? | No | Yes |
| Readability (many choices) | Better | Verbose |
| Performance | Often faster (jump table) | Sequential tests |
| Best for | Menus, fixed options | Ranges, complex logic |
Why Loops? Overview
Loops solve the problem of repetition. Without them, printing “Hello” 100 times would require 100 separate printf calls. With a loop, it’s three lines.
The Three Loop Types
| Loop | When to use | Condition checked | Min. executions |
|---|---|---|---|
for | Known number of iterations | Before each iteration | 0 (can skip entirely) |
while | Unknown iterations, condition-driven | Before each iteration | 0 (can skip entirely) |
do-while | Must run at least once (e.g. menu) | After each iteration | 1 (always) |
while(1) or for(;;) in main() that continuously reads sensors, processes data, and updates outputs. It never exits — that’s by design.while Loop
The while loop keeps executing its body as long as the condition remains true. The condition is checked before each iteration, so if it is false from the start, the body never runs.
while(condition)
{
/* body: runs while condition is true */
/* MUST update something to avoid infinite loop */
}#include <stdio.h>
int main()
{
int i = 1; /* initialization */
while(i <= 5) /* condition */
{
printf("%d\n", i); /* body */
i++; /* update — ESSENTIAL! */
}
return 0;
}Infinite while Loop (Embedded Super Loop)
int main()
{
/* Initialization — runs once */
init_hardware();
init_sensors();
/* Super loop — runs forever */
while(1)
{
read_sensors();
process_data();
update_display();
control_actuators();
}
/* Never reached */
return 0;
}while(1) is the heartbeat of virtually every embedded program. A microcontroller never “finishes” its task — it continuously monitors inputs and drives outputs until power is removed or a reset occurs.while(i <= 5) { printf("%d", i); } — without i++, i stays at 1 forever, printing 1 endlessly. Always ensure something in the body moves the condition toward false.do…while Loop
The do while loop is unique: it executes its body first, then checks the condition. This guarantees the body runs at least once, regardless of the initial condition value.
do
{
/* body always executes at least once */
statements;
}
while(condition); /* ← semicolon required here! */int i = 1;
do
{
printf("%d\n", i);
i++;
}
while(i <= 5);The Key Difference: while vs do-while
Best Use Case: Input Validation Menu
int choice;
do
{
printf("1. Add\n2. Subtract\n3. Exit\n");
printf("Enter choice: ");
scanf("%d", &choice);
}
while(choice != 3); /* keep showing menu until user picks 3 */do-while for menus, input validation loops, and any situation where the body must execute before you can even know whether to continue. If you can know before entering whether to run at all, use while instead.for Loop
The for loop is the most commonly used loop in C. It bundles initialization, condition, and update into a single line, making it ideal when the number of iterations is known in advance.
/* init condition update */
for(int i = 1; i <= 5; i++ )
{
/* body: runs 5 times */
}The for loop cycle: init → check condition → body → update → check condition → ... until false
/* 1. Print 1 to 5 */
for(int i = 1; i <= 5; i++)
printf("%d ", i); /* 1 2 3 4 5 */
/* 2. Print squares */
for(int i = 1; i <= 5; i++)
printf("%d ", i * i); /* 1 4 9 16 25 */
/* 3. Count down */
for(int i = 10; i >= 1; i--)
printf("%d ", i); /* 10 9 8 ... 1 */
/* 4. Infinite for loop (equivalent to while(1)) */
for(;;)
{
/* runs forever */
}while vs for — Same Result, Different Style
Nested Loops
A nested loop is a loop placed inside another loop. For every one iteration of the outer loop, the inner loop runs completely from start to finish.
for(int i = 1; i <= 3; i++) /* outer: 3 iterations */
{
for(int j = 1; j <= 2; j++) /* inner: 2 iterations each */
{
printf("(%d,%d) ", i, j);
}
printf("\n");
}Pattern Programs with Nested Loops
break Statement
The break statement immediately terminates the nearest enclosing loop or switch. Execution jumps to the first statement after the closing brace of that loop or switch.
#include <stdio.h>
int main()
{
for(int i = 1; i <= 10; i++)
{
if(i == 5)
break; /* exit loop when i reaches 5 */
printf("%d ", i);
}
printf("\n(loop ended early)\n");
return 0;
}break in while and do-while
int arr[] = {10, 30, 50, 70, 90};
int target = 50;
int found = 0;
for(int i = 0; i < 5; i++)
{
if(arr[i] == target)
{
found = 1;
printf("Found at index %d\n", i);
break; /* no need to keep searching */
}
}
if(!found)
printf("Not found\n");break exits only the innermost loop containing it. To break out of multiple loops, use a flag variable, goto (one of the few valid uses), or restructure as a function.continue Statement
continue skips the rest of the current iteration and jumps directly to the next iteration’s condition check. Unlike break, the loop itself does not terminate.
#include <stdio.h>
int main()
{
for(int i = 1; i <= 5; i++)
{
if(i == 3)
continue; /* skip when i is 3 */
printf("%d ", i);
}
return 0;
}break vs continue — Visual Comparison
break kills the loop; continue just skips the remainder of the current iteration and loops again
| Aspect | break | continue |
|---|---|---|
| Effect | Exits the loop entirely | Skips current iteration only |
| Loop continues? | No — terminates | Yes — at next iteration |
| Common use | Search and early exit | Skip specific values |
goto Statement
The goto statement transfers control unconditionally to a labeled statement in the same function. It is the most powerful (and most dangerous) jump statement.
#include <stdio.h>
int main()
{
goto end; /* jump — skips printf below */
printf("Hello"); /* NEVER executes */
end: /* label: any identifier followed by : */
printf("World\n"); /* executes */
return 0;
}goto creates “spaghetti code” — programs where control jumps unpredictably, making it impossible to trace what has happened when reaching any given point. For 99% of cases, break, continue, return, and functions are better alternatives.When goto Is Acceptable
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
for(int k = 0; k < 10; k++)
{
if(arr[i][j][k] == target)
goto found; /* break only exits innermost */
}
}
}
printf("Not found\n");
goto done;
found:
printf("Found!\n");
done:
; /* empty statement after final label */Loop Selection Guide
| Situation | Best Loop | Reason |
|---|---|---|
| Known count (print 1–10) | for | Init, condition, update all in one line |
| Condition depends on runtime data | while | Clean when update happens inside body |
| Must run at least once (menu) | do-while | Body executes before first condition check |
| Read until valid input | do-while | Always prompt at least once |
| Embedded super loop | while(1) | Intent is immediately clear |
| Process all array elements | for | Index counter maps naturally to for |
Complete Loop Comparison Table
| Feature | while | do-while | for |
|---|---|---|---|
| Condition checked first? | Yes | No | Yes |
| Executes at least once? | No | Yes | No |
| Init/update in syntax? | No | No | Yes |
| Best for known count? | Fair | Poor | Excellent |
| Needs semicolon after condition? | No | Yes | No |
Practical Programs
Sum of First N Numbers
#include <stdio.h>
int main()
{
int sum = 0;
for(int i = 1; i <= 10; i++)
sum += i; /* sum = sum + i */
printf("Sum = %d\n", sum); /* Sum = 55 */
return 0;
}Factorial of a Number
#include <stdio.h>
int main()
{
int fact = 1;
for(int i = 1; i <= 5; i++)
fact *= i; /* 1×2×3×4×5 */
printf("5! = %d\n", fact); /* 5! = 120 */
return 0;
}Multiplication Table
#include <stdio.h>
int main()
{
int n = 7;
for(int i = 1; i <= 10; i++)
printf("%d x %d = %d\n", n, i, n * i);
return 0;
}Reverse a Number
#include <stdio.h>
int main()
{
int num = 12345, reversed = 0;
while(num != 0)
{
int digit = num % 10; /* extract last digit */
reversed = reversed * 10 + digit;
num /= 10; /* remove last digit */
}
printf("Reversed: %d\n", reversed); /* 54321 */
return 0;
}Basic Calculator with switch
#include <stdio.h>
int main()
{
float a, b;
int op;
printf("Enter two numbers: ");
scanf("%f %f", &a, &b);
printf("1.Add 2.Sub 3.Mul 4.Div\n");
printf("Enter choice: ");
scanf("%d", &op);
switch(op)
{
case 1: printf("%.2f\n", a + b); break;
case 2: printf("%.2f\n", a - b); break;
case 3: printf("%.2f\n", a * b); break;
case 4:
if(b != 0) printf("%.4f\n", a / b);
else printf("Division by zero!\n");
break;
default: printf("Invalid choice\n");
}
return 0;
}Triangle Pattern
for(int i = 1; i <= 5; i++)
{
for(int j = 1; j <= i; j++)
printf("%d ", j);
printf("\n");
}Check Prime Number
#include <stdio.h>
int main()
{
int n = 17, is_prime = 1;
if(n < 2)
{
is_prime = 0;
}
else
{
for(int i = 2; i * i <= n; i++)
{
if(n % i == 0)
{
is_prime = 0;
break;
}
}
}
printf("%d is %s\n", n, is_prime ? "Prime" : "Not Prime");
return 0;
}Fibonacci Series
#include <stdio.h>
int main()
{
int a = 0, b = 1, c, n = 10;
printf("%d %d ", a, b);
for(int i = 2; i < n; i++)
{
c = a + b;
printf("%d ", c);
a = b;
b = c;
}
printf("\n");
return 0;
}Common Mistakes
Mistake 1 — Semicolon after if
Mistake 2 — Assignment instead of comparison
Mistake 3 — Missing braces (dangling else)
Mistake 4 — Missing break in switch
Mistake 5 — Forgetting the loop update (infinite loop)
Mistake 6 — Semicolon after while
Best Practices
Always use braces
Even for single-statement if/else/loops. Braces prevent the dangling-else bug and make adding more statements safe later.
Always add break in switch
Unless fall-through is specifically intended and clearly commented. Accidental fall-through is one of the most confusing C bugs.
Choose for when count is known
If you know you need exactly N iterations, for keeps init, condition, and update together in one readable line.
Initialise loop variables
Never rely on an uninitialized variable as a loop counter. Set it to a known value immediately before or in the for header.
Use parentheses for clarity
Write if((a > 10) && (b < 20)) rather than if(a>10&&b<20). Parentheses cost nothing and save confusion.
Keep nesting shallow
Beyond 3 levels of nested if or loops, readability degrades sharply. Refactor into functions or use early return/break.
Prefer while(1) over for(;;)
For embedded super loops, while(1) makes the intent immediately obvious to beginners. Both compile identically.
Avoid goto (almost always)
Use functions, structured loops, break, and return instead. Reserve goto for the rare multi-level loop escape and error cleanup patterns.
Memory Tricks
“I Switch N Else” — the four decision-making tools
| Statement | Think of it as… | When to use |
|---|---|---|
if | One door: open or skip | Single condition, one possible action |
if else | Fork in the road: left or right | Two mutually exclusive outcomes |
nested if | Gate inside a gate | Secondary condition depends on first |
else-if | Staircase of choices | Multiple mutually exclusive ranges/values |
switch | TV remote: press one button | Fixed set of integer/char values |
while | Keep walking while path is clear | Unknown iteration count |
do-while | Take one step, then check | Must execute at least once |
for | Countdown timer | Known iteration count |
break | Emergency exit | Stop loop/switch immediately |
continue | Skip this floor, go to next | Skip one iteration, keep looping |
Interview Questions
if executes a block only when the condition is true. If false, nothing happens. if-else executes one of exactly two blocks: the if block when true, the else block when false. With if alone, the false case simply produces no action.break was present. To prevent it, add break; at the end of every case block. Intentional fall-through is occasionally useful (e.g. two cases sharing the same action) but should be clearly commented.while: checks the condition before the first iteration. If false from the start, the body never executes (0 times).do-while: executes the body first, then checks the condition. The body always runs at least once, regardless of the initial condition.
break: exits the loop (or switch) entirely. The code after the closing brace executes next.continue: skips only the remainder of the current iteration and jumps to the next iteration’s condition check. The loop itself keeps running.
while(1) super loop models this: it continuously reads sensors, processes data, and drives outputs forever. Exiting main() has no well-defined behavior on most bare-metal targets.switch when selecting among a fixed set of integer or character constants (e.g. a menu, day-of-week, state machine states). Use else-if when comparing ranges, using floating-point values, testing strings, or when conditions involve complex expressions. switch often compiles to a jump table, which is faster than a sequence of comparisons.0 is false. Any non-zero integer value is true — this includes negative numbers, large positive numbers, and non-NULL pointers. There is no dedicated boolean type in C89/C90, but C99 added _Bool and <stdbool.h> which provides bool, true (= 1), and false (= 0).goto creates “spaghetti code” — control flow that jumps unpredictably, making the program state at any point hard to reason about. Acceptable uses: (1) breaking out of multiple nested loops when the code would be clearer than a flag variable, (2) error-cleanup code in system-level C (the Linux kernel uses it for this). Always limit jumps to a clearly labeled exit point within the same function.Frequently Asked Questions
switch only works with integer and character types. Strings (which are char arrays) cannot be used as switch expressions because they are compared by pointer address, not by content. Use if(strcmp(str, "value") == 0) from <string.h> for string comparison.else block is entirely optional. A standalone if simply does nothing when the condition is false. Add else only when you need a specific action for the false case. Similarly, a default in switch is optional but recommended as a safety net for unexpected values.default) executes, the switch’s closing } is reached and execution continues naturally after the switch block. Adding a break to the last case is still good practice for consistency and makes it easy to safely reorder cases later without introducing accidental fall-through.Practice Programs
- Check whether a number entered by the user is positive, negative, or zero.
- Check whether a number is even or odd using if-else.
- Find the largest of two numbers using if-else.
- Build a simple day-name printer: user enters 1–7, program prints Monday–Sunday using switch.
- Check voting eligibility: user enters age, program prints “Eligible” or “Not eligible”.
- Print all even numbers from 2 to 50 using a for loop.
- Print all numbers from 1 to 100 that are divisible by 3 using a while loop.
- Build a basic calculator using switch inside a do-while loop that repeats until the user chooses Exit.
- Print the multiplication table of any number entered by the user.
- Calculate the sum of digits of a number (e.g. 1234 → 1+2+3+4 = 10) using while.
- Write a program to determine whether a year is a leap year (hint: use nested if for the three conditions).
- Print the Fibonacci series up to N terms using a for loop.
- Write a program to find all prime numbers between 2 and 100 (use a nested for loop with break).
- Print the following number triangle using nested loops:
1
12
123
1234
12345 - Check if a number is a palindrome (same forwards and backwards, e.g. 121). Use while loop to extract digits, then compare.
Chapter Summary
- Control statements give programs the ability to make decisions and repeat actions.
ifexecutes a block only when a condition is true. 0 is false; any non-zero is true.if-elseselects exactly one of two blocks based on a condition.- Nested
ifplaces a decision inside another decision for multi-level conditions. - The
else-ifladder tests multiple mutually exclusive conditions; only the first match executes. switchdispatches to a matching case label. Always usebreakto prevent fall-through.whilechecks its condition before each iteration; runs 0 or more times.do-whilechecks its condition after the body; always runs at least once.forbundles init, condition, and update in one line; best when count is known.- Nested loops: for every outer iteration, the inner loop runs completely.
breakexits the nearest loop or switch immediately.continueskips the rest of the current iteration and proceeds to the next.gotois valid but almost always replaceable with better structured code.- Avoid semicolons after
ifandwhileconditions — they create empty bodies. - The embedded super loop (
while(1)) is the heartbeat of every microcontroller program.