“Modern C”: Notes on chapter 3 “Everything is about control”
By Dmitry Kabanov
These are my notes taken while reading chapter 3
“Everything is about control”
from the book “Modern C” by Jens Gustedt.
This chapter explains five control block statements:
conditional statements with if, loops with for, while, and do-while,
and selection statements with switch.
The table of contents for all notes for this book are available in that post.
3.1 Conditional execution
The general form of the conditional execution is:
if (i > 25) {
printf("i is greater than 25\n");
} else {
printf("No, i is not greater than 25\n");
}
Takeaway 3.1. The value 0 represents logical false.
Takeaway 3.2. Any value different from 0 represents logical true.
In C, equality and inequality operators are == and !=,
respectively.
Because numerical values can be treated as true or false, we can
simplify conditions like this (assuming that i is a number):
if (i != 0) {
// Do something when i is not zero.
}
to more succint code
if (i) {
// Do something when i is not zero.
}
The latter version is preferred in C code.
Include header stdbool.h adds type bool that can be used
to emphasize that we have a boolean (logical) variable.
Also, this header adds words true and false.
Takeaway 3.3. Don’t compare to 0, false, or true.
Takeaway 3.4. All scalars have a truth value.
Table 1 shows C scalar types, their categories
and how they can be specified in printf calls.
| Name | Category | printf |
|---|---|---|
size_t (from stddef.h) |
Unsigned | "%zu" |
double |
Floating | "%e", "%f", "%g" |
signed (a.k.a. int) |
Signed | "%d" |
unsigned[ |
Unsigned | "%u" |
bool (from stdbool.h) |
Boolean | "%d" |
ptrdiff_t (stddef.h) |
Signed | "%td" |
char const * |
String | "%s" |
char |
Character | "%c" |
void * |
Pointer | "%p" |
unsigned char |
Unsigned | "%hhu" |
3.2 Iterations
To go over given finite set of values, for loop should be used:
for (clause1; condition2; expression3) {
loop body
}
Here, clause1 is normally iteration variable definition
and initialization that sets initial value in iteration;
condition2 determines when the iteration stops;
and expression3 updates the iteration variable.
The following example:
for (size_t i = 9, upper_bound = 42; i < upper_bound; --i) {
// do something
}
sets two variables: i to iterate and upper_bound that is used
in the condition on loop termination.
It looks actually as the loop will never stop but variables of type
size_t cannot be negative, so iterations will happen exactly 10
times, and when i = 0, after decrement its value will become
a large number, and the condition i < upper_bound will become
false.
Therefore, the loop body will execute only 10 times.
Another way of iterating is a while loop.
Typical example is some iterative computation procedure such as
Heron approximation of the square root:
#include <tgmath.h>
double const eps = 1e-9;
double const a = 34.0;
double x = 0.5;
while (fabs(1.0 - a * x) >= eps) {
x *= 2.0 - a * x;
}
The do-while loop is similar:
do {
x *= 2.0 - a * x;
} while (fabs(1.0 - a * x) >= eps);
The difference is that the do-while always has at least
one iteration as the condition is checked in the end of an iteration.
All loops can use keywords break to terminate the loop
and continue to start a new iteration of the loop.
3.3 Multiple selection
An alternative to multiple if-else statements is a switch block:
switch (expression) {
case1:
// do 1
break;
case2:
// do 2
break;
default:
// do default
break;
}
The keyword break is important as otherwise all statement until
the end of the loop execute: for example, if expression evaluates
to case2, then without the break keyword in case2,
all statements // do 2 and // do default would execute.
Takeaway 3.5 case values must be integer constant expressions.
Takeaway 3.6 case labels must not jump beyond a variable
definition.