Goals for this week:

  1. Learn how to use printf statements to print out different types of variables in C, which you will need for Lab 2.

  2. Familiarize yourself with the read_file` library to read in values from a file, which you will need to use in Lab 2.

  3. 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 using gdb, introducing more commands and features.

  4. Introduce and get started on Lab 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 ~chaganti/public/cs31/s25/week02/* ./
$ ls
Makefile    readfile.h  testprog.c     values_float
readfile.c  testfile.c  types_printf.c  values_int

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 printf and scanf, the following formatting codes (also known as "conversion codes" or "conversion characters) are likely to be most useful to you:

%d

(signed) integer

%u

unsigned integer

%x

hexadecimal (lower case)

%X

hexadecimal (upper case)

%c

character

%f

floating point value

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

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

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

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

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.

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

The textbook has two chapters on debugging with GDB that are relevant: 3.1 and 3.2.

$ gcc -g -o testprog testprog.c

The Makefile already has this rule for us, so let’s just run make.

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.

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 0x1175: file testprog.c, line 21.

Next, we will enter the run command at the GDB prompt to tell GDB to start running our program:

(gdb) run
Starting program: /home/{myusername}/public/cs31/week02/testprog

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at testprog.c:21
21	  printf("This program adds two numbers and divides them by two\n");
(gdb)

The run command will start your program running, and GDB will only gain control again when a breakpoint is hit. If you are prompted to Enable debuginfod, you can type 'n'.

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 printf line we stopped at and display next line
21	  x = 10;
(gdb) next    # execute x = 10 and display the next line to run
22	  y = 11;

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.
average(10,11) returns 10.00
[Inferior 1 (process 597350) exited normally]
(gdb)

The result of that program does not look right for what the program is supposed to do. Let’s add break points to investigate.

Adding strategic breakpoints

Now let’s add a breakpoint in the function average, and rerun the program:

(gdb) break average
Breakpoint 2 at 0x5555555551f2: file testprog.c, line 44.

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/{myusername}/public/cs31/week02/testprog

Breakpoint 1, main () at testprog.c:21
21	  printf("This program adds two numbers and divides them by two\n");

(gdb) list 26

Let’s set a breakpoint at line 26, right before the call to average. Next, type cont to continue execution from breakpoint in main:

(gdb) break 26
Breakpoint 3 at 0x555555555192: file testprog.c, line 26.
(gdb) cont

The program continues running until it reaches the breakpoint we just set at line 26 (Breakpoint 3). We can examine the program’s execution state at line 26 by printing out the argument values before the call to average (using print),

Continuing.
This program adds two numbers and divides them by two

Breakpoint 3, main () at testprog.c:26
26	  z = average(x, y);


(gdb) print x
(gdb) print y

The where or bt command list the call stack (the next line to be executed):

(gdb) where
#0  main () at testprog.c:26
(gdb) cont

then type continue to run until the next break point is hit.

Stepping into a function

After continuing, the breakpoint in average is hit next (Breakpoint 2).

You can also step into a function of the line about to be executed using the command step. The step (or s for short) command tells GDB to step into the next function. For example, if we were paused at our breakpoint on line 26

Breakpoint 3, main () at testprog.c:26
26	  z = average(x, y);
(gdb) step

Breakpoint 2, average (a=10, b=11) at testprog.c:44
44	  sum = a + b;

Whether you got into the average function by the breakpoint that was set using ` break average` or by using the step command when paused on line 26

you can now run through the average function line by line using the n command:

Breakpoint 2, average (a=10, b=11) at testprog.c:44
44	  sum = a + b;

(gdb) list
39	*/
40	int average(int a, int b) {
41	  float result;
42	  float sum;
43
44	  sum = a + b;
45	  result = sum / 2;
46
47	  return result;
48	}


(gdb) n
45	  result = sum / 2;
(gdb) n
47	  return result;

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 average, 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 + b)  # print out the value of the expression (a + b)
(gdb) print result
(gdb) list

The where command returns more helpful information here, specifically, it tells you the call stack:

(gdb) where
#0  average (a=10, b=11) at testprog.c:47
#1  0x00005555555551a1 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  average (a=10, b=11) at testprog.c:47
#1  0x00005555555551a1 in main () at testprog.c:26
(gdb) frame 1
#1  0x00005555555551a1 in main () at testprog.c:26
26	  z = average(x, y);
(gdb) p x
$12 = 10
(gdb) p y
$13 = 11
(gdb) p z
$14 = 4.59163468e-41
(gdb) frame 0
#0  average (a=10, b=11) at testprog.c:47
47	  return result;
(gdb) p result
$15 = 10.5
(gdb)

Type next to continue through the program till you return to main. What do you notice about the value of result in average() versus the value that was returned and stored in z in main()?

Ending a GDB session

When you’re done using gdb, type the command quit.

(gdb) quit
$

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.

Lab 2 Intro

Lets talk through the next 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.

Handy Resources