1. Goals for this week:
-
Learn how to use
printf
statements to print out different types of variables in C, which you will need for Lab 2. -
Familiarize yourself with the
read_file`
library to read in values from a file, which you will need to use in Lab 2. -
Learn how to use
gdb
for basic debugging. You should start using GDB to help debug your Lab 2 code. Over the course of the semester we will revisit usinggdb
, introducing more commands and features. -
Introduce and get started on Lab 2.
2. Copy Over Example Code
Create a week02
directory in your cs31/WeeklyLabs
subdirectory
and copy over some files:
$ cd ~/cs31/WeeklyLabs
$ mkdir week02
$ cd week02
$ pwd
/home/you/cs31/WeeklyLabs/week02
$ cp ~sukrit/public/cs31/week02/* ./
$ ls
Makefile readfile.h testprog.c values_float
readfile.c testfile.c types_scanf.c values_int
3. Formatting in C
The types_printf.c
program shows examples of using the printf
function
from the C stdio library. It demonstrates printing and formatting output for
different types in C.
String format codes
For both
|
3.1. Division and Multiplication in C
In C, that mathematical operations like division and multiplcation can result in different outputs based on the types of the operands. C will rely on implicit type conversion to determine the type of the result. Division and multiplication with a float type operand results in an output of type float.
Explicit type conversion, called type casting, is explained in the textbook in Ch. 16.1.3 (C Types).
3.2. Width Specifiers in C
Width specifiers allow you to specify the maximum number of characters
printed for a given output. For example, the width specifier %5d
will limit
decimals to 5 digits. Similarly, %0.2f
will limit the number of decimals to
2 digits. More information is in the textbook in Ch. 2.8.1 (Standard Input/Output).
3.3. Characters and Strings in C
In C, strings are represented as arrays of characters. The printf
function
uses the %c
format code to print a single character and the %s
format code
to print a string. We’ll talk much more about strings and how they are stored in memory later in the semester. More information can be found in Ch. 1.5
(Arrays and Strings).
4. Running a program with command line arguments
The file read by the program is given as a command line argument when
the program is run. To run the program testfile
, specify the name
of the input file on the command line like this:
$ ./testfile <inputfile> # specify the name of an inputfile on the command line
# examples:
$ ./testfile values_int # run testfile with values_int as its inputfile
$ ./testfile values_float # run testfile with values_float as its inputfile
You can list the contents and see the format of the values_int
and values_float
files, using the cat
command:
$ cat values_int
$ cat values_float
Refering to the library interface commented in readfile.h
, you can
add calls to readfile
library functions in the testfile.c
program to
read in the the next few values from an input file, which include both
integers and floats. Then, compile and try running.
$ vim testfile.c # add code to read in some of the floats
$ make
$ ./testfile values_int
$ ./testfile values_float
5. Reading from a File using the read_file
library
In testfile.c
are examples of reading values from a file using the C
read_file
library that the CS31 instructors wrote
(implemented in read_file.c
and read_file.h
).
Information about using the library is documented in comments in
the .h
file. Let’s open those both these in your favorite editor:
$ vim read_file.h # or use emacs, etc.
$ vim testfile.c
testfile.c
has some code to open an input file and to read in
an int value from the file. And read_file.h
describes the functions
provided by the library, some of which are called in testfile.c
.
We are going to focus on two read_file
library functions (read_float
and read_int
) that read in two different types values from a file once the
file is open.
Note that read_int
and read_float
need to know where to put the values
read in. To do so, we use the &
(ampersand) operator before a variable
name for the read_int
and read_float
functions. We’ll talk much more
about what that ampersand means as we build up our C programming skills
in future assignments, but for this next lab assignment you will need
to use &
with these two functions.
6. GDB intro
Next, we’ll look at the GNU Debuggger (GDB), whose command is gdb
. GDB helps
programmers debug C and C++ programs.
Over the course of the semester, we’ll explore gdb
features in more
depth, but today we’ll cover just a few basics so that you can start
using gdb
to help you debug your C lab assignments.
To use the debugger, you usually want to compile your C program with the -g
flag to add debugging information to the a.out
file (this allows gdb
to map
binary machine code to C program code that the programmer understands).
$ gcc -g -o testprog testprog.c
The Makefile already has this rule for us, so let’s just run make
.
6.1. Running GDB
Next, we will run the executable file inside the GDB debugger:
$ gdb ./testprog
The first thing we get is the GDB prompt. At this point
testprog
has not yet started running.
6.2. Example GDB session
We usually begin a debugging session by setting a break point at main
before
starting the program running in GDB. A breakpoint tells GDB to grab control at
a certain point in the execution, in this case right before the first
instruction in main is executed:
(gdb) break main
Breakpoint 1 at 0x1155: file testprog.c, line 20.
Next, we will enter the run command at the GDB prompt to tell GDB to start running our program:
(gdb) run
Starting program: /home/sukrit/public/cs31/week02/testprog
Breakpoint 1, main () at testprog.c:20
20 x = 10;
The run command will start your program running, and GDB will only gain control again when a breakpoint is hit.
6.2.1. Orienting Yourself: list
, layout src
, next
, print
, where
, and cont
There are a few other primary GDB commands we will learn today. The first is
the list
command displays the C source code around the point where we
are in the execution:
(gdb) list
list
with a line number lists the source code around that line:
(gdb) list 30
layout src
is a command that tells GDB to display the source code in a split-
screen layout with the source code on the top and the GDB command prompt on the
bottom. This can be useful when you want to see the source code and the GDB
prompt at the same time.
(gdb) layout src
The next
command (or just n
as a shortcut) tells GDB to execute the next
instruction and then grab control again:
(gdb) next # execute the x = 10 line we stopped on when entering main
21 y = 8;
(gdb) next # execute y = 8 and display the next line to run
22 z = y / x;
The print
command can be used to print out the value of a program
variable or expression:
(gdb) print x
$1 = 10
cont
tells GDB to let the program continue running. Since we have no more
breakpoints, it will run until termination.
(gdb) cont
Continuing.
x = 10 y = 8 z = 0.00
x = 10 y = 8 z = 53.00
[Inferior 1 (process NUM) exited normally]
(gdb)
6.2.2. Adding strategic breakpoints
Now let’s add a breakpoint in the function mystery
, and rerun the program:
(gdb) break mystery
Breakpoint 2 at 0x555555555206: file testprog.c, line 36.
The run
command starts the program’s execution over from the beginning.
When re-run, the breakpoint at the beginning of the main
function will be hit
first (and list
displays the code around the breakpoint).
(gdb) run
Starting program: /home/sukrit/public/cs31/week02/testprog
Breakpoint 1, main () at testprog.c:20
20 x = 10;
(gdb) list 25
Let’s set a breakpoint at line 25, right before the call to mystery. Next,
type cont
to continue execution from breakpoint in main:
(gdb) break 25
Breakpoint 3 at 0x5555555551a6: file testprog.c, line 25.
(gdb) cont
The program continues running until it reaches the breakpoint we just
set at line 25 (Breakpoint 3). The where
or bt
command list the
call stack (the next line to be executed):
(gdb) where
#0 main () at testprog.c:25
We can examine the program’s execution
state at line 25 by printing out the argument values before the call
to mystery
(using print
), and then type cont
to continue
the program’s execution:
Continuing.
x = 10 y = 8 z = 0.00
Breakpoint 3, main () at testprog.c:25
25 z = mystery(x, y);
(gdb) print x
(gdb) print y
(gdb) cont
6.2.3. Stepping into a function
After continuing, the breakpoint in mystery
is hit next (Breakpoint
2), let’s step through some of the mystery
function’s execution, and
print out some of its parameters and locals.
The step
(or s
for short) command tells GDB to step into the next
function, were you can run through it line by line using the n
command:
(gdb) step
Breakpoint 4, mystery (a=10, b=8) at testprog.c:37
37 c = b;
(gdb) list
32 // a mystery function
33 int mystery(int a, int b) {
34 int i;
35 float c;
36
37 c = b;
38 for(i=0; i < a; i++) {
39 c += i;
40 }
(gdb) n
38 for(i=0; i < a; i++) {
We can use the print
command (p
for short) to print out expressions in the program, so let’s print out the values of the arguments passed to mystery,
and type cont to run until the next break point is hit:
(gdb) print a # print out the value of the variable a
(gdb) print (a - 4) # print out the value of the expression (a - 4)
(gdb) list
The where
command returns more helpful information here, specifically,
it tells you the call stack:
(gdb) where
#0 mystery (a=10, b=8) at testprog.c:38
#1 0x00005555555551b5 in main () at testprog.c:26
Notice the #0 and #1 above? By using the frame <frame_num>
command, can switch between different functions' stack frames:
(gdb) where
#0 mystery (a=10, b=8) at testprog.c:45
#1 0x00005555555551b5 in main () at testprog.c:26
(gdb) frame 1
#1 0x00005555555551b5 in main () at testprog.c:26
26 z = mystery(x, y);
(gdb) p z
$7 = 0
(gdb) frame 0
#0 mystery (a=10, b=8) at testprog.c:45
45 return c;
(gdb) p c
$8 = 26.5
Type next
to continue through the program till you return to main. What do you notice about the value of c
in mystery()
versus the value that was returned and stored in z
in main()
?
6.3. Ending a GDB session
When you’re done using gdb
, type the command quit
.
(gdb) quit
$
6.4. GDB and command line arguments
If you use GDB to help you debug a program that expect command line arguments,
you’ll need to pass the program’s arguments to the run
command:
$ gdb ./testfile
(gdb) break main
(gdb) run values_int
In general, for programs with command line arguments,
simply list the arguments after the run
command, for example
to run with 3 command line arguments (6, 4, and hello), do the
following:
(gdb) break main
(gdb) run 6 4 hello
We’ll learn more about C and GDB over the course of the semester, but these GDB basics are enough to start using GDB to help you debug your C programs.
7. Lab 2 Intro
Lets talk through the next Lab 2 Assignment, where you will implement a C program that, among other things, uses arrays, command line arguments, and reads values in from a file.
8. Handy Resources
-
Chapter 3 on gdb