Control Flow Statements in the C Language

Lecture



Control Flow Statements

Control flow statements let you organize loops and branching in programs. C has only a few of them, and half of them can be left unused (they can be expressed through the others).

Control Statement - Curly Braces

Curly braces let you combine several elementary statements into one compound statement, or block. In all syntactic constructs, a compound statement can be used in place of a simple one.

In C, declarations of local variables can be placed at the beginning of a block. Local variables declared inside a block are created on entering the block and destroyed on leaving it.

In C++, local variables can be declared anywhere, not only at the beginning of a block. Nevertheless, just as in C, they are automatically destroyed when leaving the block.

Here is a program fragment that swaps the values of two real variables:

double x, y;
. . .
{
    double tmp = x;
    x = y;
    y = tmp;
}

Here, to swap the values of the two variables x and y, we first store the value of x in the auxiliary variable tmp. Then the value of y is written to x, and the previous value of x saved in tmp is written to y. Since the variable tmp is needed only inside this fragment, we enclosed it in a block and declared the variable tmp inside that block. On leaving the block, the memory occupied by the variable tmp is released.

Control Statement - The if Statement

The if statement lets you organize branching in a program. It has two forms: the "if" statement and the "if...else" statement. The "if" statement has the form

if (condition)
    action;

the "if...else" statement has the form

if (condition)
    action1;
else
    action2;

Any expression of Boolean or integer type can be used as the condition. Recall that when an integer expression is used, any nonzero value corresponds to "true". When the "if" statement is executed, the conditional expression after if is evaluated first. If it is true, the action is executed; if it is false, nothing happens. For example, in the following fragment the variable m is assigned the maximum of the values of the variables x and y:

double x, y, m;
. . .
m = x;
if (y > x)
    m = y;

When the "if...else" statement is executed, if the condition is true, the action written after if is executed; otherwise the action after else is executed. For example, the previous fragment can be rewritten as follows:

double x, y, m;
. . .
if (x > y)
    m = x;
else
    m = y;

When several actions must be performed depending on whether a condition is true, you should use curly braces, combining several statements into a block, for example,

double x, y, d;
. . .
if (d > 1.0) {
    x /= d;
    y /= d;
}

Here the variables x and y are divided by d only if the value of d is greater than one.

Curly braces can be used even when only a single statement follows if or else. They improve the structure of the program text and make it easier to modify later. Example:

double x, y;
. . .
if (x != 0.0) {
    y = 1.0;
}

If you later need to add one more action performed under the condition " x is not equal to zero", you simply add a line inside the curly braces.

Control Statement - Choosing Among Several Possibilities: if...else if...

Several conditional statements of the "if...else" type can be written in sequence (that is, the action after else can itself be a conditional statement). The result is a choice among several possibilities. The selection construct is used very often in programming. Example: given a real variable x, we need to write the value of the function sign(x) into a real variable y:

sign(x) = -1, if x < 0
sign(x) = 1,  if x > 0
sign(x) = 0,  if x = 0

This is done using the selection construct:

double x, s;
. . .
if (x < 0.0) {
    s = (-1.0);
}
else if (x > 0.0) {
    s = 1.0;
}
else {
    s = 0.0;
}

When this fragment is executed, the condition x < 0.0 is checked first. If it is true, the statement s = (-1.0) is executed; otherwise the second condition x > 0.0 is checked. If it is true, the statement s = 1.0 is executed; otherwise the statement s = 0.0 is executed. The curly braces are added here to improve the structure of the program text.

In any case, executing the selection construct results in only one of the statements (possibly compound) being executed. The conditions are checked sequentially from top to bottom. As soon as a true condition is found, the corresponding action is performed and the selection ends.

Control Statement - The while Loop

The "while" loop construct corresponds to the while loop in C:

while (condition)
    action;

The while loop is called a pre-test loop, because the condition is checked before the loop body is executed.

The while loop is executed as follows: first the condition is checked. If it is true, the action is executed. Then the condition is checked again; if it is true, the action is repeated again, and so on indefinitely. The loop ends when the condition becomes false. Example:

int n, p;
. . .
p = 1;
while (2*p <= n)
    p *= 2;

As a result of executing this fragment, the variable p will hold the largest power of two not exceeding the positive integer n.

If the condition is false from the very beginning, the action is not executed even once. This greatly simplifies programming and makes the program more reliable, since exceptional situations are handled correctly and automatically. Thus, the fragment above works correctly for n = 1 (the loop is not executed at all).

Because of a programming error, a loop may never terminate. To avoid this, the program should be written so that some bounded quantity, on which the loop condition in the header depends directly or indirectly, monotonically decreases or increases after each execution of the loop body. This guarantees that the loop terminates. In the fragment above, such a quantity is the value of p, which doubles after each execution of the loop body.

The loop body may consist of one or several statements. In the latter case they must be enclosed in curly braces. We advise enclosing the loop body in curly braces even when it consists of just one statement - this makes the program text clearer and easier to modify later. For example, the fragment above would be better written like this:

int n, p;
. . .
p = 1;
while (2*p <= n) {
    p *= 2;
}

Deliberate use of the "while" loop is always associated with an explicit formulation of the loop invariant, see section 1.5.2.

Exiting a loop with break, jumping to the end of a loop with continue

If you need to interrupt the execution of a loop, use the statement

break;

The break statement is used inside a loop body enclosed in curly braces. Example: we need to find a root of an integer function f(x) defined for integer arguments.

int f(int x);   // Function prototype declaration
. . .
int x;
. . .
// Search for a root of the function f(x)
x = 0;
while (true) {
    if (f(x) == 0) {
        break;  // Found the root
    }
    // Move on to the next integer value of x
    //    in the order 0, -1, 1, -2, 2, -3, 3, ...
    if (x >= 0) {
        x = (-x - 1);
    }
    else {
        x = (-x);
    }
}
// Assertion: f(x) == 0

Here an infinite loop " while (true) " is used. The loop is exited with the " break " statement.

Sometimes you need to skip the execution of the loop body for certain values of the variables that change in the loop, moving on to the next set of values and the next iteration. The following statement is used for this:

continue;

The continue statement, like break, is used only when the loop body consists of more than one statement and is enclosed in curly braces. It should be understood as a jump to the closing curly brace of the loop body.

Control Construct - The goto Jump Statement

The goto jump statement lets you change the natural order of program execution and jump to another part of the program marked with a label. A jump can occur only within a function, i.e. the goto statement can neither leave a function nor enter another function. The goto statement looks as follows:

L: ...;
. . .
goto L;

Any name that is valid in C can be used as a label (i.e. a sequence of letters, digits and underscores " _ " that does not begin with a digit). The label can be placed before or after the goto statement. The label is marked with a colon " :". It is better to put a semicolon " ; " right after it, thereby marking an empty statement - this is a common programming practice, according to which labels are placed between statements rather than on statements.

You should not get carried away with the goto statement - it always makes a program confusing. Most programmers consider the use of goto to be bad programming style. Instead of goto, when necessary, you can use the loop exit statement break and the loop iteration skip statement continue (see section 3.5.7). The only situation in which using goto is justified is exiting from several nested loops:

while (...) {
    . . .
    while (...) {
        . . .
        if (...) {
            goto LExit; // Exit from two
                        // nested loops
        }
        . . .
    }
}
LExit: ;
  • In the object-oriented language Java, whose syntax is based on the C language, the use of the goto statement is prohibited. Instead, to exit from several nested loops, a form of the break statement with a label is used. The label marks the loop that must be exited:
Loop1:
while (...) {
    . . .
    while (...) {
        . . .
        break Loop1; // Exit from the loop
                     // marked with the label Loop1
        . . .
    }
    . . .
}

Control Construct - The for Loop

The arithmetic loop, popular in other programming languages, is implemented in C with the for loop. It looks as follows:

for (initialization; continuation condition; iterator)
    loop body;

The initialization is executed once, before the first check of the continuation condition and the first execution of the loop body. The continuation condition is checked before each execution of the loop body. If the condition is true, the loop body is executed; otherwise the loop terminates. The iterator is executed after each execution of the loop body (before the next check of the continuation condition).

Since the continuation condition is checked before the loop body is executed, the for loop, like the while loop, is a pre-test loop. If the continuation condition is false from the start, the loop body is never executed, which is good both from the standpoint of program reliability and from the standpoint of simplicity and elegance (since there is no need to treat exceptional cases separately).

Consider an example of summing an array using the for loop:

double a[100]; // Array a holds at most 100 elements
int n;         // Actual length of array a (n <= 100)
double sum;    // Variable for the sum of array elements
int i;         // Loop variable
. . .
sum = 0.0;
for (i = 0; i < n; ++i) {
    sum += a[i]; // Increase the sum by a[i]
}

Here the integer variable i is used as the loop variable. In the initialization statement, the variable i is assigned the value 0. The loop continuation condition is the condition i. The iterator ++i increases the variable i by one. Thus, the variable i successively takes the values 0, 1, 2,..., n-1. The loop body is executed for each value of i.

In most other programming languages, the arithmetic loop is rigidly tied to the use of a loop variable that must take values from an arithmetic progression. This is not so in C: here the initialization, the continuation condition and the iterator can be arbitrary expressions, which gives the program much more flexibility. The for loop construct can be implemented using the while loop:

for (initialization; condition; iterator;)
{
    loop body;
}
initialization;
while (condition) {
    loop body;
}

For example, the array summation fragment is implemented using the while loop as follows:

for (i=0; i < n; ++i)
{
    sum += a[i];  
}
i = 0;
while (i < n) {
    sum += a[i];
   ++i;
}

In principle, the for loop construct is not needed: it can be implemented with the while loop, which is simpler and clearer. However, most programmers continue to use the for loop. This is most likely due to tradition and habit, since in earlier programming languages, for example in the first versions of Fortran, the arithmetic loop was the main one, and the while loop had to be implemented with the if and goto statements.

Control Construct - The "Comma" Operator and the for Loop

In the for loop

for (initialization; continuation condition; iterator)
    loop body;

any expressions can be used as the initialization and the iterator, in particular the assignment operator = and the operator ++ that increases the value of a variable by one. What if you need to perform several actions in the initialization or in the iterator? You could, of course, use the while loop, but fans of the for loop do it differently. For this, C provides the " comma " operator, which lets you combine several expressions into one. The " comma " operator has two operands, which are evaluated sequentially from left to right. The result of the operation is the last evaluated, i.e. the right, value. Example:

int x, y, z;
x = 5;
z = (y = x + 10, ++x);  // y = 15, x = 6, z = 6

Here, when the expression in parentheses is evaluated, the first subexpression y = x+10 is evaluated first, as a result of which the value 15 is written to y; the value of the first subexpression is also 15. Then the second subexpression after the comma, ++x, is evaluated, as a result of which the value of x increases and becomes 6; the value of the second subexpression is also 6. The value of the " comma " operation is the value of the second subexpression, i.e. 6. As a result, the value 6 is assigned to the variable z.

The presence of the " comma " operator reflects the aesthetic side of the original version of the C language from the 1970s: in it almost any notation had some meaning. Later, programmers came to understand that program reliability is more important than brevity and elegance, and adopted the stricter ANSI standard of the C language of 1989, which somewhat limited the freedom of creativity in C programs.

Nevertheless, the " comma " operator can still be used in the header of a for loop when you need to perform several actions in the initialization or in the iterator. For example, the array summation fragment

sum = 0.0;
for (i = 0; i < n; ++i) {
    sum += a[i];
}

can be rewritten in the following "aesthetic" way:

for (sum = 0.0, i = 0; i < n; sum += a[i], ++i);

Here the loop body is completely empty, and all the actions are moved into the loop header! It is better to avoid this programming style: it adds nothing in terms of the efficiency of the finished program, but makes the text less understandable and thus increases the likelihood of errors.

Control Constructs You Had Better Not Use

In programming it is preferable to avoid solutions that are aesthetically beautiful but not very clear. Recently the requirement of program reliability has come to the fore, so from several solutions it is better to choose the simpler one, which, where possible, minimizes the probability of errors. This also implies some self-restraint of the programmer's freedom.

The constructs listed below have existed in the C language since its earliest versions. Nevertheless, you can do without them by replacing them with other constructs that are potentially more reliable.

The do...while Loop

The do...while loop has the form

do
    action;
while (condition);

It is better to always enclose the action in curly braces, even when it consists of only one statement, for example,

do {
    x *= 2;
} while (x < n);

The do...while loop is a post-test loop. First the loop body is executed, and only after that is the loop's continuation condition checked. If the condition is true, the loop body is repeated, and so on indefinitely, until the condition becomes false. Thus, the loop body is always executed, even if the condition is false from the very beginning. This is a potential source of errors. It is better to always use the pre-test loop while (look before you leap!).

Let us give an example of erroneous use of the do...while loop. Let the variable n hold a positive integer. We need to write into the integer variable p the largest power of two not exceeding n. This fragment was already implemented earlier using the while loop (section 3.5.5):

int n, p;
. . .
p = 1;
while (2*p <= n) {
    p *= 2;
}

An attempt to use the do...while loop can lead to an error:

int n, p;
. . .
p = 1;
do {
    p *= 2;
} while (2*p <= n);

The program works incorrectly when n = 1 (two is written to the variable p instead of one), because the body of the do...while loop is always executed once regardless of whether the condition is true, since the condition is checked only after the loop body is executed. Errors of this kind in "edge" cases are the most dangerous in programming: the program works correctly in almost all situations except a few exceptions. But it is known that most disasters happen precisely as a result of an exceptional combination of circumstances!

Control Construct - The switch Statement (a Computed goto)

The switch statement has the following form:

switch (expression) {
    case value_1:
        fragment_1;
    case value_2:
        fragment_2;
    case value_3:
        fragment_3;
    . . .
    default:        // Optional fragment
        fragment_N;
}

The expression must be of a discrete type (an integer or a pointer). The values must be constants of the same type as the expression in the header. The switch statement works as follows:

  1. first, the value of the expression in the switch header is evaluated ;
  2. then a jump is made to the label " case L:" where the constant L matches the computed value of the expression in the header;
  3. if there is no such value among the labels inside the switch body, then
    • if there is a " default:" label, a jump to it is made;
    • if the " default:" label is absent, nothing happens.

Note that after the jump to the label " case L:" the program text is executed sequentially. For example, when the following program fragment is executed

int n, k;
n = 2;
switch (n) {
    case 1:
        k = 2;
    case 2:
        k = 4;
    case 3:
        k = 8;
}

the variable k will be assigned the value 8, not 4. The point is that after the jump to the label " case 2:" the line

k = 4;

will be executed first, and then the line

k = 8;

which makes the fragment completely pointless (an optimizing compiler will simply remove the lines " k = 2; " and " k = 4; " from the code of the finished program!). To fix this fragment, use the statement

break;

As in the case of a loop, the break statement causes an exit from the curly braces enclosing the body of the switch statement. The fragment above must be rewritten as follows:

int n, k;
n = 2;
switch (n) {
    case 1:
        k = 2;
        break;
    case 2:
        k = 4;
        break;
    case 3:
        k = 8;
        break;
}

As a result of executing this fragment, the variable k will be assigned the value 4. If the value of n were 1, then k would be assigned the value 2; if n were 3, then 8. If n is equal to neither 1, nor 2, nor 3, nothing happens.

The switch statement is sometimes quite unjustifiably called a selection statement. In fact, for selection you should use the if...else if... construct, see section 3.5.3. For example, the fragment above is better implemented as follows:

if (n == 1) {
    k = 2;
}
else if (n == 2) {
    k = 4;
}
else if (n == 3) {
    k = 8;
}

In essence, the switch statement is a goto jump statement with a computed label. It suffers from many of the drawbacks of goto, for example, problems with initializing local variables when entering a block. In addition, switch does not allow conditions to be written as logical expressions, which limits its scope of application. It is recommended never to use the switch statement: selection in the if...else if... style is better in every respect!

See also

  • Representing a C Program as Functions

  • Memory management in C
  • Structures in the C language
  • C operators
  • C syntax
  • [[b4304]]

See also

created: 2021-06-17
updated: 2026-09-29
149



Was this answer useful?
Choose a quick rating so we can improve the next answer for you.
How satisfied are you?


Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Algorithmization and programming. Structural programming. C language"

Terms: Algorithmization and programming. Structural programming. C language