Calculation¶
Now it’s time to make a program that does do something. Perhaps it
should calculate the answer to the age-old question, ‘What is \(2
+ 2\)?’. Put the following code in twoplustwo.c
.
int main()
{
int x = 2 + 2;
}
If you compile and run this program, you will find that it still apparently does nothing. In fact, it does calculate \(2+2\), it just doesn’t say anything about it. C is a very minimalist language, and as we’ve seen, even something like printing answers where a human can see them is left off for a library. For now, let’s use another way of seeing what’s going on inside this program: an interactive debugger.
As we will see in this course, you can do a lot using a debugger without
the program’s cooperation; however, it is more convenient to debug a program
when the compiler cooperates by including some additional information.
The default rules for make are adequate for you to
run make twoplustwo
and rely on it to figure out to use twoplustwo.c
and the C compiler, but we’re going to need a Makefile anyway in order
to tell it to pass some additional options when it runs the compiler.
This Makefile
can be a one-liner.
CFLAGS=-g -Wall
You can also set up phony all and clean targets if you wish, but
the options specified in the CFLAGS variable will be sufficient. The
-g
option is the crucial piece, which instructs the compiler to
include debugging symbols. The -Wall
option is something I always
prefer to use; it stands for ‘Warnings, all’ and makes the compiler a
little more verbose about things it thinks might be wrong with your code.
If you have already built the program, running make twoplustwo
will just tell you that everything is up-to-date. You can delete the
program with rm, or you can make it seem like there are changes
to the source file. Running touch twoplustwo.c
will update its
timestamp as though you had just edited it.
Now that we have configured the compiler correctly and rebuilt the
program, open it in the debugger by running gdb twoplustwo
. The
debugger is a command-prompt environment like the shell; to prompt you
for a command, gdb
will print (gdb)
. You type a command
and press enter, and when gdb
is ready for your next command it
will print the prompt again.
Start the program running with start
. It will pause at the
beginning of main
, and you can step through line by line with
next
. After x
has been calculated, when the program
is paused on the closing curly bracket, you can print out the value
of x
.
(gdb) start
Starting program: ...
Temporary breakpoint 1, main () at twoplustwo.c:2
2 {
(gdb) next
3 int x = 2 + 2;
(gdb) next
4 }
(gdb) print x
$1 = 4
The output $1 = 4
is telling us that if we want to use this
result again, we can call it $1
, but more importantly, that the
value is 4.
To continue experimenting with calculations and the debugger, put the
following in factorial.c
and build it. (The Makefile
we
already made will apply to anything you build in that directory, so there
is no need to duplicate that step to get the compiler options we want.)
int main()
{
int n = 1;
n = n * 2;
n = n * 3;
n = n * 4;
n = n * 5;
}
Step through the code in gdb with start
, next
,
and print n
to watch the program calculate the factorial.
One more example will show us another of C’s datatypes, double
,
which can handle fractions, and let us use the debugger to manipulate a
running program. Put this one in temperature.c
and step through
it in the debugger as we have been doing, printing celsius
and fahrenheit
to follow the calculation.
int main()
{
double celsius = 20.0;
double fahrenheit = celsius * 9.0 / 5.0 + 32.0;
}
Now, step through temperature
in the debugger again, but as
soon as celsius
has been initialized to 20.0
, change
it! What temperature would you like to convert? To convert, say, 100°,
tell gdb print celsius = 100.0
, then continue stepping
through and you will see the calculation proceed using your changed value.