“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.