Program Flow
The statements in a program are executed in sequence, except when directed to do otherwise by a for, while, do-while, if, switch, or goto statement or a function call.
- An if statement conditionally executes code depending on the truth value of an expression.
- The for, while, and do-while statements are used to form loops. In a loop, the same statement or group of statements is executed repeatedly until a condition is met.
- A switch statement chooses a set of statements to execute based on the arithmetic value of an integer expression.
- A goto statement is an unconditional jump to a labeled statement.
- A function call is a jump to the code in the function’s body. When the function returns, the program executes from the point after the function call.
These control statements are covered in more detail in the following sections.
if
An if statement conditionally executes code depending on the truth value of an expression. It has the following form:
if ( expression ) statement
If expression evaluates to true (non-zero), statement is executed; otherwise, execution continues with the next statement after the if statement. An if statement may be extended by adding an else section:
if ( expression ) statement1 else statement2
If expression evaluates to true (non-zero), statement1 is executed; otherwise, statement2 is executed.
An if statement may also be extended by adding else if sections, as shown here:
if ( expression1 ) statement1 else if ( expression2 ) statement2 else if ( expression3 ) statement3 ... else statementN
The expressions are evaluated in sequence. When an expression evaluates to non-zero, the corresponding statement is executed and execution continues with the next statement following the if statement. If the expressions are all false, the statement following the else clause is executed. (As with a simple if statement, the else clause is optional and may be omitted.)
Conditional Expression
A conditional expression is made up of three sub-expressions and has the following form:
expression1 ? expression2 : expression3
When a conditional expression is evaluated, expression1 is evaluated for its truth value. If it is true, expression2 is evaluated and the value of the entire expression is the value of expression2. expression3 is not evaluated.
If expression1 evaluates to false, expression3 is evaluated and the value of the conditional expression is the value of expression3. expression2 is not evaluated.
A conditional expression is often used as shorthand for a simple if statement. For example:
a = ( b > 0 ) ? c : d;
is equivalent to
if ( b > 0 ) a = c; else a = d;
while
The while statement is used to form loops as follows:
while ( expression ) statement
When the while statement is executed, expression is evaluated. If it evaluates to true, statement is executed and the condition is evaluated again. This sequence is repeated until expression evaluates to false, at which point execution continues with the next statement after the while.
You will occasionally see this construction:
while (1) { ... }
Since the constant 1 evaluates to true, the preceding is an infinite loop from the while’s point of view. Presumably, something in the body of the loop checks for a condition and breaks out of the loop when that condition is met.
do-while
The do-while statement is similar to the while, with the difference that the test comes after the statement rather than before:
do statement while ( expression );
One consequence of this is that statement is always executed once, independent of the value of expression. Situations where the program logic dictates that a loop body be executed at least once, even if the condition is false, are uncommon. As a consequence, do-while statements are rarely encountered in practice.
for
The for statement is the most general looping construct. It has the following form:
for (expression1; expression2; expression3) statement
When a for statement is executed, the following sequence occurs:
- expression1 is evaluated once before the loop begins.
- expression2 is evaluated for its truth value.
- If expression2 is true, statement is executed; otherwise, the loop ends and execution continues with the next statement after the loop.
- expression3 is evaluated.
- Steps 2, 3, and 4 are repeated until expression2 becomes false.
expression1 and expression3 are evaluated only for their side effects. Their values are discarded. They are typically used to initialize and increment a loop counter variable:
int j; for (j=0; j < 10; j++) { // Something that needs doing 10 times }
Any of the expressions may be omitted (the semicolons must remain). If expression2 is omitted, the loop is an infinite loop, similar to while( 1 ):
int i; for (i=0; ; i++) { ... // Check something and exit if the condition is met }
break
The break statement is used to break out of a loop or a switch statement:
int j; for (j=0; j < 100; j++) { ... if ( someConditionMet ) break; //Execution continues after the loop }
Execution continues with the next statement after the enclosing while, do, for, or switch statement. When used inside nested loops, break only breaks out of the innermost loop. Coding a break statement that is not enclosed by a loop or a switch causes a compiler error:
error: break statement not within loop or switch
continue
continue is used inside a while, do-while, or for loop to abandon execution of the current loop iteration. For example:
int j; for (j=0; j < 100; j++) { ... if ( doneWithIteration ) continue; // Skip to the next iteration ... }
When the continue statement is executed, control passes to the next iteration of the loop. In a while or do-while loop, the control expression is evaluated for the next iteration. In a for loop, the iteration (third) expression is evaluated and then the control (second) expression is evaluated. Coding a continue statement that is not enclosed by a loop causes a compiler error.
Comma Expression
A comma expression consists of two or more expressions separated by commas:
expression1, expression2, ..., expressionN
The expressions are evaluated in order from left to right, and the value of the entire expression is the value of the right-most sub-expression.
The principal use of the comma operator is to initialize and update multiple loop variables in a for statement. As the loop in the following example iterates, j goes from 0 to MAX-1 and k goes from MAX-1 to 0:
int j, k; for (j=0, k=MAX-1; j < MAX; j++, k--) { // Do something }
When a comma expression is used like this in a for loop, only the side effects of evaluating the sub-expressions are important. The value of the comma expression is discarded. In the preceding example a comma expression is also used to increment j and decrement k after each pass through a for loop.
switch
A switch branches to different statements based on the value of an integer expression. The form of a switch statement is shown here:
switch ( integer_expression ) { case value1: statement break; case value2: statement break; ... default: statement break; }
In a slight inconsistency with the rest of C, each case may have multiple statements without the requirement of a compound statement.
value1, value2, ... must be either integers, character constants, or constant expressions that evaluate to an integer. (In other words, they must be reducible to an integer at compile time.) Duplicate cases with the same integer are not allowed.
When a switch statement is executed, integer_expression is evaluated and the switch compares the result with the integer case labels. If a match is found, execution jumps to the statement after the matching case label. Execution continues in sequence until either a break statement or the end of the switch is encountered. A break causes the execution to jump out to the first statement following the switch.
A break statement is not required after a case. If it is omitted, execution falls through to the following case. If you see the break omitted in existing code, it can be either a mistake (it is an easy one to make) or intentional (if the coder wanted a case and the following case to execute the same code).
If integer_expression doesn’t match any of the case labels, execution jumps to the statement following the optional default: label, if one is present. If there is no match and no default:, the switch does nothing, and execution continues with the first statement after the switch.
goto
C provides a goto statement:
goto label;
When the goto is executed, control is unconditionally transferred to the statement marked with label:, as here:
label: statement
- Labels are not executable statements; they just mark a point in the code.
- The rules for naming labels are the same as the rules for naming variables and functions.
- Labels always end with a colon.
Using goto statements with abandon can lead to tangled, confusing code (often referred to as spaghetti code). The usual boilerplate advice is “Don’t use goto statements.” Despite this, goto statements are useful in certain situations, such as breaking out of nested loops (a break statement only breaks out of the innermost loop):
int i, j; for (i=0; i < MAX_I; i++) for (j=0; j < MAX_J; j++) { ... if ( finished ) goto moreStuff; } moreStuff: statement // more statements
Functions
Functions have the following form:
returnType functionName( arg1Type arg1, ..., argNType argN ) { statements }
An example of a simple function looks like this:
float salesTax( float purchasePrice, float taxRate ) { float tax = purchasePrice * taxRate; return tax; }
A function is called by coding the function name followed by a parenthesized list of expressions, one for each of the function’s arguments. Each expression type must match the type declared for the corresponding function argument. The following example shows a simple function call:
float carPrice = 20000.00; float stateTaxRate = 0.05; float carSalesTax = salesTax( carPrice, stateTaxRate );
When the line with the function call is executed, control jumps to the first statement in the function body. Execution continues until a return statement is encountered or the end of the function is reached. Execution then returns to the calling context. The value of the function expression in the calling context is the value set by the return statement.
Functions are sometimes executed solely for their side effects. This function prints out the sales tax but changes nothing in the program’s state:
void printSalesTax ( float purchasePrice, float taxRate ) { float tax = purchasePrice * taxRate; printf( "The sales tax is: %f.2\n", tax ); }
C functions are call by value. When a function is called, the expressions in the argument list of the calling statement are evaluated and their values are passed to the function. A function cannot directly change the value of any of the variables in the calling context. This function has no effect on anything in the calling context:
void salesTax( float purchasePrice, float taxRate, float carSalesTax ) { // Changes the local copy of carSalesTax but not the value of // the variable in the calling context carSalesTax = purchasePrice * taxRate; return; }
To change the value of a variable in the calling context, you must pass a pointer to the variable and use that pointer to manipulate the variable’s value:
void salesTax( float purchasePrice, float taxRate, float *carSalesTax) { *carSalesTax = purchasePrice * taxRate; // this will work return; }
Declaring Functions
When you call a function, the compiler needs to know the types of the function’s arguments and return value. It uses this information to set up the communication between the function and its caller. If the code for the function comes before the function call (in the source code file), you don’t have to do anything else. If the function is coded after the function call or in a different file, you must declare the function before you use it.
A function declaration repeats the first line of the function, with a semicolon added at the end:
void printSalesTax ( float purchasePrice, float taxRate );
It is a common practice to put function declarations in a header file. The header file is then included (see the next section) in any file that uses the function.