Control flow and loops
How a C program chooses and repeats: if and else (and the dangling else), while, do-while and for, break and continue, switch with its fall-through, short-circuit evaluation of && and ||, the pitfalls (assignment in a condition, a stray semicolon, off-by-one), tracing a loop by hand, and counting how many times a body runs.
Why it exists
Control flow decides which statements run and how often. In GATE a C question is very often a loop to trace by hand (what is printed, what is the final value), a switch with a missing break, a condition with a side effect that may or may not be evaluated, or a count of how many times a statement runs. The rules are few and exact; the mistakes come from assuming a language that behaves differently (a switch that does not fall through, a && that evaluates both sides, a do-while that tests first).
Prerequisites
You need the operators and the conversions of types and operators: integer division, the meaning of a true condition (any non-zero value), and the difference between = and ==.
Precise definitions
Conditions. In C there is no boolean in a condition: 0 is false, every other value is true. The relational and logical operators produce 0 or 1.
if and else.
if (condition) statement
if (condition) statement else statement
The statement is a single statement, usually a block { ... }. An else belongs to the nearest preceding if that has no else: this is the dangling else; braces change the binding. The conditional expression c ? a : b evaluates a if c is non-zero and b otherwise.
Loops.
| loop | tests | runs the body |
|---|---|---|
while (c) body | before each iteration | zero or more times |
do body while (c); | after each iteration | at least once |
for (init; c; update) body | init once; then c before each iteration; update after each | zero or more times |
for (init; c; update) body is the same as init; while (c) { body update; } except for continue. Any of the three parts may be empty; an empty condition means true (for (;;) is an endless loop). The comma operator a, b evaluates a, then b, and has the value of b.
break and continue. break leaves the innermost enclosing loop or switch. continue skips the rest of the body and goes to the next iteration: in a for loop it jumps to the update, in a while or do-while to the test.
switch.
switch (integer expression) {
case constant1: statements
case constant2: statements
default: statements
}
The expression must have integer type (including char and enumerations); the case labels are distinct integer constants. Control jumps to the matching label, or to default if none matches (or leaves the switch if there is no default), and then continues through all following statements, crossing the next labels, until a break or the end of the switch: this is fall-through. Several labels in a row (case 3: case 4:) share the code that follows. The default label may stand anywhere and also falls through.
Short-circuit evaluation. a && b evaluates a; if it is 0 the result is 0 and b is not evaluated at all. a || b evaluates a; if it is non-zero the result is 1 and b is not evaluated. There is a sequence point after the left operand, so a side effect in a is complete before b. Side effects in the skipped operand (a function call, i++) do not happen. The result is always 0 or 1. The ordinary operators such as + do not short-circuit, and the order of evaluation of their operands is not specified.
goto. goto label; jumps to a label in the same function. Loops and switch compile into conditional and unconditional jumps (see control flow translation); goto is rarely needed.
Counting iterations. For for (i = a; i < b; i += c) with the body runs times; with i *= 2 from 1 to below it runs times, with i /= 2 from down to above 1 it runs times; nested loops multiply, or add up when the inner bound depends on the outer index (for example ). These counts are what an analysis of the running time (algorithm analysis) uses.
How it works
Four programs, compiled and run for real; the tables are their output, one line per step.
A while loop that peels off the digits of a number: the test n > 0 before every iteration.
A while loop that peels off the digits of 90713: start
Computed by a script, not typed| moment | d | n | sum | rev |
|---|---|---|---|---|
| before the loop | ||||
| after iteration 1 | ||||
| after iteration 2 | ||||
| after iteration 3 | ||||
| after iteration 4 | ||||
| after iteration 5 |
Step 0
The loop runs while n > 0. Each pass takes the last digit, adds it to a sum and appends it to a reversed number. digit sum 20, reversed 31709.
A do-while loop: Euclid's algorithm by repeated subtraction. The body runs first, then x != y is tested.
A do-while loop: Euclid's algorithm by subtraction on 84 and 36: start
Computed by a script, not typed| moment | x | y | x != y |
|---|---|---|---|
| start | |||
| after step 1 | |||
| after step 2 | |||
| after step 3 | |||
| after step 4 |
Step 0
The common value at the end is the greatest common divisor, because subtracting the smaller number from the larger does not change the gcd. gcd = 12 after 4 steps.
switch and fall-through.
switch and fall-through: what is printed for each value: start
Computed by a script, not typed| input | printed |
|---|---|
| c = 1 | |
| c = 2 | |
| c = 3 | |
| c = 4 | |
| c = 0 | |
| c = 9 |
Step 0
Execution enters at the matching case label and runs on, through the following labels, until a break (or the end of the switch). default is entered when no case matches; it can be anywhere in the switch and falls through too.
Short-circuit evaluation: which calls happen.
Short-circuit evaluation of && and ||: start
Computed by a script, not typed| expression | functions that were called | result |
|---|---|---|
| f(0) && f(1) | ||
| f(1) && f(2) | ||
| f(1) || f(0) | ||
| f(0) || f(3) | ||
| for j = -2..2: if (j > 0 && i++) count += j; |
Step 0
&& and || evaluate their left operand first and skip the right one when the left already decides the result. The right operand's side effects (calls, i++) are skipped too.
Counting iterations: the formulas against real runs.
How many times does the body run?: start
Computed by a script, not typed| loop | times | formula |
|---|---|---|
| for (i = 0; i < n; i++), n = 10 | ||
| for (i = 0; i < n; i += 3), n = 10 | ||
| for (i = 1; i < m; i *= 2), m = 100 | ||
| for (i = m; i > 1; i /= 2), m = 100 | ||
| for i < n: for (j = 0; j < i; j++), n = 10 | ||
| for i < n: for (j = i; j < n; j++), n = 10 | ||
| for i < n: for j < m, n = 10, m = 100 | ||
| do { } while (i > 0), i = 0 | ||
| while (i > 0), i = 0 |
Step 0
Counted by running each loop. The formulas are the ones to remember: a loop with step c over a range n runs about n/c times (rounded up), a doubling or halving loop about log2 n times, and nested loops multiply or sum.
Worked examples
EX 1Tracing a while loop
EasyTrace the loop by hand with a table of the variables after each iteration, then compare with the run.
#include <stdio.h>
int main(void) {
int n = 90713, sum = 0, rev = 0;
printf("start: n=%d sum=%d rev=%d\n", n, sum, rev);
while (n > 0) {
int d = n % 10;
sum += d;
rev = rev * 10 + d;
n /= 10;
printf("d=%d n=%d sum=%d rev=%d\n", d, n, sum, rev);
}
printf("digit sum %d, reversed %d\n", sum, rev);
return 0;
}start: n=90713 sum=0 rev=0
d=3 n=9071 sum=3 rev=3
d=1 n=907 sum=4 rev=31
d=7 n=90 sum=11 rev=317
d=0 n=9 sum=11 rev=3170
d=9 n=0 sum=20 rev=31709
digit sum 20, reversed 31709Reading it. The test n > 0 is evaluated before each of the five iterations (n = 90713, 9071, 907, 90, 9) and fails when n has become 0. In each iteration d is the last digit, sum accumulates the digits () and rev appends each digit on the right, so the last digit taken becomes the first digit of rev: 31709 is 90713 reversed. Note rev = 3170 after the fourth iteration (the digit 0 is appended) and the final 31709. A trace table with one row per iteration is the reliable method.
EX 2A do-while loop and the greatest common divisor
Medium#include <stdio.h>
int main(void) {
int x = 84, y = 36, step = 0;
printf("start: x=%d y=%d\n", x, y);
do {
if (x > y) x = x - y;
else y = y - x;
step++;
printf("step %d: x=%d y=%d\n", step, x, y);
} while (x != y);
printf("gcd = %d after %d steps\n", x, step);
return 0;
}start: x=84 y=36
step 1: x=48 y=36
step 2: x=12 y=36
step 3: x=12 y=24
step 4: x=12 y=12
gcd = 12 after 4 stepsReading it. Each step replaces the larger number by the difference; the gcd does not change (any common divisor of and divides , and conversely), so when the common value is the gcd: 12. With a while (x != y) loop and at the start, the body would not run and the answer would be at once. With the do-while form and the body would run once (), after which and the if branch (x = x - y with ) subtracts 0 for ever: an endless loop. Check which loop form a question uses before trusting a quick answer.
EX 3switch and fall-through
Medium#include <stdio.h>
static void classify(int c) {
printf("c=%d:", c);
switch (c) {
case 1:
printf(" one");
/* fall through */
case 2:
printf(" two");
break;
case 3:
case 4:
printf(" three-or-four");
/* fall through */
default:
printf(" other");
}
printf("\n");
}
int main(void) {
int inputs[6] = {1, 2, 3, 4, 0, 9};
for (int k = 0; k < 6; k++) classify(inputs[k]);
return 0;
}c=1: one two
c=2: two
c=3: three-or-four other
c=4: three-or-four other
c=0: other
c=9: otherReading it. For c = 1 the code of case 1 runs, has no break, and falls through into case 2: "one two". For c = 2 only "two". The labels case 3: and case 4: together share the statements after case 4:, which print "three-or-four" and fall through into default: "three-or-four other" for both 3 and 4. For 0 and 9 no label matches and control goes to default: "other". The rule to apply: find the entry point, then run forward until a break.
EX 4Short-circuit evaluation and side effects
Medium#include <stdio.h>
static int f(int v) {
printf("f(%d) ", v);
return v;
}
int main(void) {
int r, i = 0, count = 0;
r = f(0) && f(1);
printf("-> %d\n", r);
r = f(1) && f(2);
printf("-> %d\n", r);
r = f(1) || f(0);
printf("-> %d\n", r);
r = f(0) || f(3);
printf("-> %d\n", r);
for (int j = -2; j <= 2; j++)
if (j > 0 && i++) count += j;
printf("i=%d count=%d\n", i, count);
return 0;
}f(0) -> 0
f(1) f(2) -> 1
f(1) -> 1
f(0) f(3) -> 1
i=2 count=2Reading it. f(0) && f(1) stops after f(0) (false): f(1) is never called, which the output shows. f(1) || f(0) stops after f(1) (true). f(1) && f(2) and f(0) || f(3) must evaluate both. In the loop, j > 0 && i++ evaluates i++ only for and : at the old value of i (0) is the result, so nothing is added, and i becomes 1; at the old value 1 is true: count += 2, and i becomes 2. A side effect on the right of && or || is conditional: do not count it unless the left operand lets it run.
EX 5How many times is printf executed?
MediumA mixture of integer and floating-point arithmetic, a prefix decrement, a compound assignment and a switch with fall-through inside a loop. The counter shown counts the calls of say.
#include <stdio.h>
static int shown;
static void say(int v) {
printf(" %d", v);
shown++;
}
int main(void) {
int j = 5 / 2 + 7 / 2.0 + 3 / 4; /* 2 + 3.5 + 0 = 5.5 is converted to int: 5 */
int k = 4;
printf("j = %d\n", j);
k -= --j; /* j becomes 4, then k = 4 - 4 */
printf("j = %d, k = %d\n", j, k);
for (int i = 0; i < 5; i++) {
printf("i = %d, i + k = %d:", i, i + k);
switch (i + k) {
case 0:
case 1:
say(i + k);
/* fall through */
case 2:
say(i + k);
break;
case 3:
say(i + k);
/* fall through */
default:
say(i + k);
}
printf("\n");
}
printf("printf was executed %d times by say\n", shown);
return 0;
}j = 5
j = 4, k = 0
i = 0, i + k = 0: 0 0
i = 1, i + k = 1: 1 1
i = 2, i + k = 2: 2
i = 3, i + k = 3: 3 3
i = 4, i + k = 4: 4
printf was executed 8 times by sayReading it. The expression 5 / 2 + 7 / 2.0 + 3 / 4 is (the divisions of two integers truncate, the one with 2.0 is floating point), and assigning to an int truncates to 5. k -= --j first decrements j (to 4) and then subtracts it: . The loop then switches on 0, 1, 2, 3, 4: 0 and 1 enter at case 0/case 1, print, fall into case 2, print and break (two prints each); 2 enters case 2, prints once and breaks; 3 prints in case 3 and falls into default (two prints); 4 goes to default (one print). Total . Write the value of the switch expression for each iteration, find the entry label, and add the statements until the break.
EX 6Iteration counts
GATE level#include <stdio.h>
int main(void) {
int n = 10, m = 100, c, i, j;
c = 0; for (i = 0; i < n; i++) c++;
printf("for (i = 0; i < n; i++), n = 10 | %d | n\n", c);
c = 0; for (i = 0; i < n; i += 3) c++;
printf("for (i = 0; i < n; i += 3), n = 10 | %d | ceil(n/3) = %d\n", c, (n + 2) / 3);
c = 0; for (i = 1; i < m; i *= 2) c++;
printf("for (i = 1; i < m; i *= 2), m = 100 | %d | ceil(log2 m) = 7\n", c);
c = 0; for (i = m; i > 1; i /= 2) c++;
printf("for (i = m; i > 1; i /= 2), m = 100 | %d | floor(log2 m) = 6\n", c);
c = 0; for (i = 0; i < n; i++) for (j = 0; j < i; j++) c++;
printf("for i < n: for (j = 0; j < i; j++), n = 10 | %d | n(n-1)/2 = %d\n", c, n * (n - 1) / 2);
c = 0; for (i = 0; i < n; i++) for (j = i; j < n; j++) c++;
printf("for i < n: for (j = i; j < n; j++), n = 10 | %d | n(n+1)/2 = %d\n", c, n * (n + 1) / 2);
c = 0; for (i = 0; i < n; i++) for (j = 0; j < m; j++) c++;
printf("for i < n: for j < m, n = 10, m = 100 | %d | n * m\n", c);
c = 0; i = 0; do { c++; } while (i > 0);
printf("do { } while (i > 0), i = 0 | %d | a do-while runs once before it tests\n", c);
c = 0; i = 0; while (i > 0) c++;
printf("while (i > 0), i = 0 | %d | a while may not run at all\n", c);
return 0;
}for (i = 0; i < n; i++), n = 10 | 10 | n
for (i = 0; i < n; i += 3), n = 10 | 4 | ceil(n/3) = 4
for (i = 1; i < m; i *= 2), m = 100 | 7 | ceil(log2 m) = 7
for (i = m; i > 1; i /= 2), m = 100 | 6 | floor(log2 m) = 6
for i < n: for (j = 0; j < i; j++), n = 10 | 45 | n(n-1)/2 = 45
for i < n: for (j = i; j < n; j++), n = 10 | 55 | n(n+1)/2 = 55
for i < n: for j < m, n = 10, m = 100 | 1000 | n * m
do { } while (i > 0), i = 0 | 1 | a do-while runs once before it tests
while (i > 0), i = 0 | 0 | a while may not run at allReading it. The loop i += 3 over runs times. The doubling loop runs 7 times for (); the halving loop runs 6 times (and stops when ). The first nested loop sums , the second ; independent bounds multiply: . A do-while with a false condition still runs once; a while with a false condition runs zero times.
EX 7The two classic traps
GATE levelAn assignment = where a comparison was meant is legal and its value is the assigned value, so if (x = 5) is always true (and sets x); and an else binds to the nearest if, whatever the indentation says. The compiler warns about both.
#include <stdio.h>
int main(void) {
int x = 0, a = 1, b = 0;
if (x = 5) printf("assignment in the condition: taken, x is now %d\n", x);
if (a)
if (b)
printf("inner if taken\n");
else
printf("the else belongs to the NEAREST if (the one with b), not to the one with a\n");
return 0;
}assignment in the condition: taken, x is now 5
the else belongs to the NEAREST if (the one with b), not to the one with aReading it. if (x = 5) assigns 5 to x and the value of the assignment, 5, is true: the message is printed and x is changed (a comparison would be x == 5). In the nested if, the indentation suggests that the else belongs to if (a), but it belongs to if (b), the nearest one: since a is 1 and b is 0, the inner if fails and the else of the inner if runs. Braces around the inner if would attach the else to the outer one.
Cheat box
Cheat box
- Condition: 0 is false, anything else true.
=assigns,==compares. while: test first.do-while: body first (at least once).for: init, test, body, update.breakleaves the innermost loop or switch;continuegoes to the update (for) or the test.switch: jump to the matching label (ordefault), then fall through untilbreak.&&and||skip the right operand when the left decides; its side effects do not happen.elsegoes with the nearestif.- Counts: step : ; doubling: ; halving: ; dependent nested loops sum, independent multiply.
Traps and GATE patterns
Connections
Across subjects
Evaluation order inside one condition (i++ && i++) is the subject of sequence points and undefined behaviour; && is the one operator with a guaranteed order. A loop is the iterative twin of recursion; counting its iterations is the first step of analysing an algorithm; and the compiler translates every loop and switch into tests and jumps (control flow translation). Functions that contain loops are in functions and parameter passing.
PYQs
6 previous year questions from your book. I re-solved each one myself (5 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
#include < stdio.h >
int gate(int n) {
int d, t, newnum, turn;
newnum = turn = 0; t=1;
while(n >= t) t *= 10;
t /= 10;
while(t > 0) {
d = n / t;
n = n % t;
t /= 10;
if(turn) newnum = 10 * newnum + d;
turn = (turn + 1) % 2;
}
return newnum;
}
int main () {
printf("%d", gate(14362));
return 0;
}
The value printed by the given C program is ________ .
(Answer in integer)int x=126,y=105;
do {
if(x>y) x=x-y;
else y=y-x;
} while(x!=y);
printf("%d",x);
The output of the given C code segment is________ .
(Answer in integer)Consider the following ANSI C code segment:
z = x + 3 + y->f1 + y→f2;
for (i = 0; i< 200; i = i + 2)
{
if (z >i) {
p = p + x + 3:
q = q + y→f2;
} else {
p = p + y→f2:
q = q + x + 3:
}
}
Assume that the variable y points to a struct (allocated
on the heap) containing two fields f1 and f2, and the
local variables x, y, z, p, q, and i are allotted registers.
Common
sub-expression
elimination
(CSE)
optimization is applied on the code. The number of
addition and dereference operations (of the form y->f1
or y->f2) in the optimized code, respectively, are:- (a)303 and 102
- (b)303 and 2
- (c)403 and 102
- (d)203 and 2
Consider the following ANSI C program.
#include <stdio.h>
int main()
{
int i, j, count;
count = 0;
i = 0;
for (j = –3, j <= 3; j++)
{
if ((j >= 0) && (i++))
count = count + j;
}
count = count + i;
printf("%d", count);
return 0;
}
Which of one of the following options is correct?- (a)The program will compile successfully and output 10 when executed.
- (b)The program will not compile successfully.
- (b)The program will compile successfully and output 8 when executed.
- (d)The program will compile successfully and output 13 when executed.
Consider the following C program:
# include <stdio.h>
int main( )
{
int i, j, k = 0;
j = 2 * 3 / 4 + 2.0 / 5 + 8 / 5;
k - = --j;
for (i = 0; i< 5; i++)
{
switch(i + k)
{
case 1:
case 2: printf("\n%d", i + k);
case 3: printf("\n%d", i + k);
default: printf("\n%d", i + k);
}
}
return 0;
}
Total number of times printf() executed ?What will be the output of the following C program
segment?
char inchar = 'A';
switch (inchar)
{
case 'A' :
printf ("choice A ") ;
case 'B' :
printf ("choice B ") ;
case 'C' :
case 'D' :
case 'E' :
default:
printf ("No Choice") ;
}- (a)No Choice
- (b)Choice A
- (c)Choice A Choice B No Choice
- (d)Program gives no output as it is erroneou
Self-check
Question 1
int x = 3; do { x--; } while (x > 5); How many times does the body run, and what is x?
Show answerHide
Once (a do-while runs its body before testing); .
Question 2
switch (2) { case 1: printf("a"); case 2: printf("b"); case 3: printf("c"); break; default: printf("d"); } prints?
Show answerHide
"bc": enter at case 2, fall into case 3, break.
Question 3
int i = 0; if (i++ && i++) ; printf("%d", i); prints?
Show answerHide
1: i++ yields 0 (false) and increments i to 1; the right operand is not evaluated.
Question 4
How many times does for (i = 1; i <= 100; i *= 3) body run?
Show answerHide
: 5 times (the next, 243, is above 100).
Question 5
for (i = 0; i < 4; i++) for (j = i; j < 4; j++) count++; ends with count?
Show answerHide
.
Revision card
One-screen revision
- 0 false, non-zero true.
whiletests first,do-whileruns once,forhas init, test, update. break: innermost loop or switch.continue: next iteration (the update in afor).switch: entry at the matching label, falls through untilbreak;defaultwhen nothing matches.&&,||skip the right operand; side effects too.elsegoes to the nearestif.- Iteration counts: , , , sums and products for nested loops.
References
- ·Kernighan, Ritchie, The C Programming Language (2nd edition), Chapter 3 Control Flow
- ·King, C Programming: A Modern Approach (2nd edition), Chapters 5 Selection Statements and 6 Loops
- ·Forouzan, Gilberg, Computer Science: A Structured Programming Approach Using C, Chapters 5 and 6 (selection and repetition)
Verification
Every program on the page is compiled and run by npm run verify:c (the two trap programs with their compiler warnings allowed, as marked), and the tables are the output of the same programs run by scripts/gen-pds-control-traces.py (the sources are shared with scripts/pds_control.py, and scripts/enum-checks/pds_control.py asserts that the page prints exactly those sources). The same check recomputes every output with a model written in Python, compiles a batch program that runs the digit loop for every n up to 5000, Euclid's subtraction loop for every pair up to 60, the switch for every value from -50 to 50 and the loop trip counts for n up to 200, and compares them with Python, so the formulas for the number of iterations are confirmed on the whole range. tests/pds-control-traces.test.ts recomputes the tables in TypeScript. The behaviour shown (short-circuit, fall-through, the else binding to the nearest if, assignment in a condition) is defined by the C standard; the order in which the arguments of one call are evaluated is NOT defined and the programs never depend on it. Textbook chapter titles are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand (long C programs were not re-read line by line: they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). One wrong word fixed (in the endless-loop example the branch that subtracts 0 is the if branch, not the else branch). Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)