A program is a list of instructions. Watch the computer follow this one, a line at a time, and keep an eye on total.
Running a program line by line
A loop adds 1 to 5 into total
#include <stdio.h> int main(void){ int total = 0; for (int i = 1; i <= 5; i++) total = total + i; printf("total = %d\n", total); return 0;}Output
The program starts in main. Its box total has no value yet: “?”.
Describe this picture
The 10 lines of sum.c with the line about to run marked, beside a picture of the program’s memory under the label “Stack”: one card titled main, holding one row of 8 small cells. i, an int of 4 cells at 0x7fe8, fills the left half of the row once the loop starts; total, an int at 0x7fec, fills the right half. Each box shows its type and name at the top left, its address at the top right, and its value in the middle, ”?” until a line writes one. Under the picture the program’s output appears once it prints, and under everything one sentence on the current step. The clip plays the program once, 15 steps in about 16 seconds: 1.6 seconds a line before and after the loop, 1 second a line inside it, and rests on the end. When a line writes a box, the old value fades out as the new one comes in, and the box is outlined. i’s box appears, showing ”?”, when the mark first reaches line 6, and disappears when the loop ends. The mark goes from line 7 back to line 6 five times; then it moves to line 8, and total = 15 appears under the picture. The transport plays and pauses, steps one line back or forward, and scrubs; a paused step is kept in the page address as line.step.
Reading the picture
If you have never written a loop in any language, go slowly here: pause the picture and step through it a line at a time with the transport. The picture shows every step, so you can check each sentence below against it.
The mark in the code sits on the line about to run. The picture of memory shows the state after every line before it. So when the mark is on line 7 for the first time, line 6 has run, and line 7 has not.
The picture holds the program’s two variables. A variable is a named box that holds one value, and total and i are the two boxes here. A box shows ”?” until a line puts a value in it. The small number in each box’s top right corner, such as 0x7fec, says where the box is in memory. Variables live in memory (1.2) is about those numbers, so you can leave them aside for now.
= stores a value
Line 7 reads total = total + i;. In maths that would be false for every i except 0. In C, = is not a statement that two things are equal. It is an instruction: work out the right side with the values the boxes hold now, then store the result in the box on the left.
The first time line 7 runs, total holds 0 and i holds 1. The right side is 0 + 1 = 1, and 1 goes into total. The old 0 is gone. Watch total’s box: it holds one number at a time, and it does not remember the earlier ones.
The mark goes round
Watch where the mark goes after line 7. It goes back up to line 6, not on to line 8. Line 6 is the loop’s header, and it runs before every turn of the loop, not only at the start. On the first visit it sets i to 1; on each later visit it adds 1 to i. Each time, it then checks that i is still at most 5.
So the mark visits line 7 five times, with i = 1, 2, 3, 4 and 5, and total becomes 1, 3, 6, 10 and 15. After the fifth turn, line 6 makes i 6, and the check 6 <= 5 fails. The mark jumps to line 8, and the program prints total = 15.
The box for i appears when the loop starts and disappears when it ends. i was made by the loop, and it belongs to the loop.
Key idea
= works out the right side, then stores the result in the box on the left. A box holds one value at a time.
The code and its cost
Here is what each part of sum.c does, line by line.
#include <stdio.h>(line 1) brings in the standard input and output functions,printfamong them.int main(void)(line 3) starts the function namedmain. Every C program begins running at the first line ofmain. Theintin front says thatmainhands back a whole number when it ends, and(void)says that it takes nothing in.- The braces on lines 4 and 10 group the lines between them into the body of
main. - Each instruction, called a statement, ends with a semicolon.
int total = 0;(line 5) makes a variable named total, of typeint, and stores 0 in it. Anintholds a whole number. In C a variable’s type is fixed: total is anintfor as long as it exists.printf("total = %d\n", total);(line 8) prints the text between the quotes.%dis replaced by the value of total, written as a whole number, and\nends the line.return 0;(line 9) endsmain, and with it the program. The 0 tells the system that started the program that all went well.
The four parts of a for loop
Line 6, for (int i = 1; i <= 5; i++), packs four parts into one line:
| Part | Code | When it runs |
|---|---|---|
| start | int i = 1 | once, before everything else |
| test | i <= 5 | before every turn; when it is false, the loop ends |
| body | line 7 | once per turn |
| step | i++ | after every turn, before the next test; it adds 1 to i |
The order is start, test, body, step, test, body, step, and so on, until the test is false. Start, test and step are all on line 6, which is why the mark keeps coming back to it. Without braces, the body of a for is the one statement that follows it; with braces it is every statement between them, as in the while loop of the worked example.
What the loop costs
The cost of a loop is how many times its lines run. Here line 7 runs 5 times, i++ runs 5 times, and the test runs 6 times: five times true, and once false, which ends the loop. Change the 5 in the test to 1000, and line 7 runs 1000 times. Nothing else in the program changes.
Change the test to i < 5 instead, and the loop stops after the turn with i = 4. The program prints total = 10.
Compiling and running
The computer cannot run sum.c itself. A program called a compiler reads the file and writes a machine program, and it is that machine program that runs. This course uses the compiler gcc:
sum.c the file you write
│ gcc
▼
sum the machine program
│ ./sum
▼
total = 15 what it prints
In a terminal, in the folder that holds sum.c, these two commands build the program and run it:
gcc -std=c11 -Wall -Wextra -g sum.c -o sum
./sum
-std=c11 picks the version of C this course uses. -Wall and -Wextra ask gcc to warn about anything that looks like a mistake. -g keeps the line numbers inside the program, so that a debugger can step it line by line; that is how the picture at the top of the page was made. -o sum names the machine program sum, and ./sum runs it.
The same as other languages, and different
If you have written programs in another language, most of this will look familiar. C has if and while, the arithmetic + - * / %, and the comparisons < <= > >= == !=. Two equals signs, ==, test whether two values are equal; one, =, stores. Dividing two ints drops the remainder: 7 / 2 is 3, and 7 % 2 is the remainder, 1.
Two things are different from many languages. The program is compiled before it runs, and every variable has a fixed type.
Worked example
Here is a while loop. It runs its body as long as its test is true, checking the test before every turn. Take a sheet of paper, write n and f at the top, and trace these lines by hand before you read on.
int n = 4;
int f = 1;
while (n > 1) {
f = f * n;
n = n - 1;
}
printf("f = %d\n", f);
Each time the test n > 1 is checked, the boxes hold:
| Check | Boxes | Test | What happens |
|---|---|---|---|
| 1 | n = 4, f = 1 | true | f becomes 1 × 4 = 4, then n becomes 3 |
| 2 | n = 3, f = 4 | true | f becomes 4 × 3 = 12, then n becomes 2 |
| 3 | n = 2, f = 12 | true | f becomes 12 × 2 = 24, then n becomes 1 |
| 4 | n = 1, f = 24 | false | the loop ends |
The test is checked four times and fails on the fourth. The program prints f = 24, which is 4 × 3 × 2. The order of the two lines in the body matters: f is multiplied by n before n goes down.
The replay below runs these lines inside main, as lines 5 to 11 of factorial.c. Step through it and compare it with your sheet.
Tracing a while loop
n counts down from 4 while f collects 4 × 3 × 2
#include <stdio.h> int main(void){ int n = 4; int f = 1; while (n > 1) { f = f * n; n = n - 1; } printf("f = %d\n", f); return 0;}Output
main has started. n and f have no values yet.
Describe this picture
The 13 lines of factorial.c with the line about to run marked, beside the card for main: one row of 8 cells, f, an int at 0x7fe8, in the left half and n, an int at 0x7fec, in the right half. Each box shows its type and name, its address and its value, ”?” until written. The replay runs one line every 1.9 seconds, 15 steps in about 27 seconds, and rests on the end. n takes 4 and f takes 1; then the mark goes round lines 7, 8 and 9 three times while f becomes 4, 12 and 24 and n becomes 3, 2 and 1. On the fourth visit to line 7 the test fails and the mark moves to line 11. The output, f = 24, appears under the picture when line 11 has run. The sentence under the picture says which check each visit to line 7 is. The transport plays and pauses, steps one line back or forward, and scrubs; a paused step is kept in the page address as while.step.
Reading a warning
Now a mistake. Delete , total from line 8 of sum.c, so that the line reads printf("total = %d\n");. The file is sum-missing-arg.c. Compile it with the same command, and gcc prints:
sum-missing-arg.c:8:22: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
8:22 is line 8, column 22, where the d of %d is. The message says that %d asks for an int to print, and the line gives it none. The [-Wformat=] at the end names the check, one of those -Wall turns on.
A warning does not stop gcc: the program is built, and it runs. But %d prints whatever number happens to be where the missing value should be. On my machine two runs printed −1263705800 and −2112275528. Treat every warning as a mistake to fix.
Where it’s used
Operating systems. The core of most operating systems, the part that runs programs and talks to the hardware, is written mostly in C.
Firmware. The small computers inside cars, washing machines and thermostats run programs written mostly in C, compiled for each chip.
Audio and signal processing. Audio and signal-processing libraries are often written in C for speed. Many instruments on this site’s signal processing pages run a small C library, compiled to run in your browser, that holds the fast Fourier transform of The FFT, convolution and filters.
Other languages. The standard interpreter of Python, the program that runs Python code, is itself a C program, and so are the runtimes of many other languages.
Reference card
| Code | Meaning |
|---|---|
int main(void) { … return 0; } | the program starts at main’s first line; return 0 ends it and reports success |
int total = 0; | makes an int box named total and stores 0 in it |
total = total + i; | works out the right side, then stores it in total; the old value is gone |
for (start; test; step) body | start once, then test, body, step, test, … until the test is false |
while (test) { … } | test, body, test, body, … until the test is false |
if (test) statement | runs the statement only when the test is true |
printf("total = %d\n", total); | prints the text, with total’s value in place of %d |
gcc -std=c11 -Wall -Wextra -g sum.c -o sum | compiles sum.c into the program sum; ./sum runs it |
| cost | a loop’s body runs once per turn; its test runs once more, and that last check fails |
Platform: C11, gcc 13 on x86-64 Linux. The addresses in the pictures are short stand-ins, by one rule for the whole course.