Files for this lab are located in: /stage/classes/archive/2011/summer/50101-1/lab/lab5/src
Compile and run the program cmdline.c, typing your first and last name at the shell prompt after the name of the executable.
You will see this output:
In main(), printf() is called to output these strings to the terminal:
You can read more about command line arguments in Ch. 11 of Prata.
In main(), you can see that malloc() is called to allocate memory for a new character array:
Why is it necessary to "+1" to the length of the string that is being copied? (Hint: what character ends every C-style string?)
Note that you need to include the stdlib.h header file when you call malloc():
You can read more about malloc() and dynamic memory allocation in Chapter 12 of Prata.
Your output should look like this:
Your program should call malloc() to allocate memory for the new string.
In this section you will learn the basics of the GDB debugger. You will learn some basic commands, and will then debug two programs with some very simple errors.
The program charcount2 doesn't even display any output; it ends in a Segmentation fault which is a runtime error.
Use the command break, or the shortcut b, as follows:
You can display all active breakpoints with the command info break.
next executes one line of source code.
Hint: Use the gdb debugger to step through the while-loop in function char_count().
Print the value of string str at each iteration of this loop.
If you call run without arguments, then GDB will use the arguments from the previous call (if any).
The
backtrace command can be called at any time in GDB, and shows which function is
associated with the current point of execution.
backtrace will display the current call stack,
with one line per frame.
(You may want to call list next to see code around line 39.)
Submit these files for Lab 5:
First, be sure that every source file you submit includes this information in comments
at the top of the file:
Also include a simple README file in your lab5 directory.
This file should include your first and last name, your username, the list of files that you are submitting, and any other comments or questions for the Lab TA.
Do not submit any compiled binary files
You will submit this lab with the hwsubmit program.
prepared by Sonjia Waxmonsky
Using command line arguments: argc and argv
Up to now, you have used scanf() and getchar() to prompt the user for input. As an alternative you can also use command line arguments
to get input from the user.
You have already used command line arguments with Linux commands; when you type "mkdir lab5", the string "lab5" is a command line argument.
$./cmdline Sally Jones
Number of args: 3
Arg 0: ./cmdline
Arg 1: Sally
Arg 2: Jones
Hello Sally!
Your first name starts with S.
Next look at the main() function:
int main (int argc, char * argv[])
{
printf("Number of args: %d\n",argc);
if(argc<3)
{
printf("Usage: %s <firstname> <lastname>\n",argv[0]);
return -1;
}
printf("Arg[0]: %s\n",argv[0]);
printf("Arg[1]: %s\n",argv[1]);
printf("Arg[2]: %s\n",argv[2]);
printf("Hello %s!\n",argv[1]);
printf("Your first name starts with %c.\n",argv[1][0]);
return 0;
}
Observe that in this program, the function main() has two arguments:
int main (int argc, char * argv[])
Here argument int argc of main() is the number of arguments at the command line.
Argument char * argv[] is an array of strings (i.e., an array of pointers to characters) that contains the values entered at the command line.
In the above example, the array argv contains the strings "./cmdline", "Sally", and "Jones".
printf("Arg[0]: %s\n",argv[0]);
printf("Arg[1]: %s\n",argv[1]);
printf("Arg[2]: %s\n",argv[2]);
Exercise 1
Copy cmdline.c to a new file, ex1.c. Next, modify main() so that the program also displays the following:
Your output should look like this:
$./cmdline Sally Jones
Number of args: 3
Arg 0: ./cmdline
Arg 1: Sally
Arg 2: Jones
Hello Sally!
Your first name starts with S.
Your first name ends with y.
Your last name starts with J.
Your last name ends with s.
Your program must work with multiple inputs (i.e., you can't hard-code the length of your own first and last name.)
Allocating memory with malloc()
malloc.c is a simple program that calls the function malloc() to allocate memory for a string dynamically, that is, at runtime.
Compile and run this program, using "cspp50101" as a command line argument:
$./malloc cspp50101
Argv[1]: cspp50101
New string: CSPP50101
You can see that this program creates a copy of the input string, and then modifies the copy by changing all characters to upper case. Note that the original string is unchanged when printf() is called:
/* now print new and old string */
printf("Argv[1]: %s\n",str);
printf("New string: %s\n",newstr);
/* compute size of buffer for new string */
int len=strlen(str)+1;
/* create buffer with space for new string */
char * newstr = (char*)malloc( len * sizeof(char) );
Here you can see that this program allocates a new string buffer, and the size of that buffer is computed at runtime.
That is, the size of this buffer is not known when the program is compiled,
and so malloc() is called at runtime after the new buffer size is known.
An alternative is to declare a local array of some arbitrary length to store the new string, but in some programs this may not be an efficient use of memory.
#include <stdlib.h>
Exercise 2
Copy malloc.c to a new file, ex2.c. Next, modify this file so that program creates and displays a new string that is equal to two copies of the input string (passed as a command line argument) back to back. So "cspp50101" becomes "cspp50101cspp50101".
$./malloc cspp50101
Argv[1]: cspp50101
New string: cspp50101cspp50101
The buffer for your new string (here: "csppp50101cspp50101")
should be exactly the size needed to store the new string.
Using the gdb debugger
A debugger is a tool that allows you to examine a program during execution. Debuggers can be very useful for finding runtime errors in your programs.
For example, you may have a function that always seems to return the incorrect value. With a debugger, you can stop the program as this function executes and examine the value of its arguments and local variables.
Help and references with GDB
As you go through the GDB section of today's lab, you can find help with the following references:
An
index is also available.
Compiling for GDB: The -g flag
In order to debug a C program, you must compile with the '-g' option.
This option generates debugging information,
such as datatype and line number information, and stores this in the executable file.
$ gcc -g -o charcount1 -Wall -std=c99 charcount1.c
$ gcc -g -o charcount2 -Wall -std=c99 charcount2.c
$ ./charcount1
Counting occurrences of 's' in string "Jackson, Mississippi"
Count: 20
$ ./charcount2 "Jackson, Mississippi" s
Counting occurrences of 's' in string "Jackson, Mississippi"
Segmentation fault
You will see a bug in each program. In charcount1, we see that the program indicates that the character 's' occurs 20 times in the string "Jackson, Mississippi". But clearly that is not correct; 's' only occurs 5 times in this string.
Starting GDB
After you have compiled a C source file with -g, you can run this program with GDB.
Use the command gdb <executable> at the command line.
$ gdb charcount1
GNU gdb 7.0.1-debian
...
(gdb)
(gdb) quit
$
Running a program in GDB:
run
You can execute a program within GDB with the command run
$ gdb charcount1
GNU gdb 7.0.1-debian
...
(gdb) run
Starting program: /home/username/cspp50101/lab5/charcount1
Counting occurrences of 's' in string Jackson, Mississippi
Count: 20
Setting a breakpoint:
break
Of course, just running the program isn't enough to find our error.
We now look at some of the GDB commands used to examine the program as it executes.
You can set a breakpoint so that GDB will pause execution at a certain line in the source code.
break <function>
break <linenumber>
break <filename:function>
break <filename:linenumber>
To delete a breakpoint, use :
delete <breakpoint number>
where <number> is obtained from info break.
You can add a breakpoint at any point before or after you have called run.
(gdb) break main
Breakpoint 1 at 0x40051f: file charcount1.c, line 25.
(gdb) run
Starting program: /home/username/cspp50101/lab5/charcount1
Breakpoint 1, main () at charcount1.c:25
25 char mystr[] = "Jackson, Mississippi";
(gdb)
Continuing execution after a breakpoint: continue
When a program is stopped at a breakpoint, you can call continue to proceed automatically
to the next breakpoint or until the program terminates.
(gdb) continue
Continuing.
Counting occurrences of 's' in string "Jackson, Mississippi"
Count: 20
Program exited normally.
(gdb)
Note that program output is still displayed to the terminal.
Stepping through and into a function, by line:
next
and
step
We can use the command next and step to examine a program.
As you see above, if the line includes a function call:
(gdb) run
Starting program: /home/username/cspp50101/lab5/charcount1
Breakpoint 1, main () at charcount1.c:25
25 char mystr[] = "Jackson, Mississippi";
(gdb) next
26 printf("Counting occurrences of \'s\' in string \"%s\"\n", mystr);
(gdb) next
Counting occurrences of 's' in string "Jackson, Mississippi"
27 int count = char_count('s',mystr);
(gdb) next
28 printf("Count: %d\n",count);
int count = char_count('s',str);
then a call to next will not enter that function. Instead, next will step over the function, to the next line of source code.
The command step is similar to next, but will step inside a function being called on the current line.
next N and step N are similar to calling next or step N times in a row.
Execution will still stop at breakpoints, even if N lines have not been run.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/username/cspp50101/lab5/charcount1
Breakpoint 1, main () at charcount1.c:14
25 char mystr[] = "Jackson, Mississippi";
(gdb) next
26 printf("Counting occurrences of \'s\' in string \"%s\"\n", mystr);
(gdb) next
Counting occurrences of 's' in string "Jackson, Mississippi"
27 int count = char_count('s',mystr);
(gdb) step
char_count (c=115 's', str=0xbfab8ee3 "Jackson, Mississippi") at charcount1.c:12
12 int count = 0;
The list command displays source code around your most recent listing.
list <number> displays code around a particular line.
next and step do not always
take you to the line that is next in the source code.
In particular, when you reach the last line of a while- or for-loop body,
you then return to the line that contains the the loop condition,
which actually comes earlier in the file.
(gdb) list
7 #include
18 iter++;
(gdb) next
14 while( *iter != '\0')
(gdb) next
Displaying data:
print
You can use the print command, or its shortcut p,
to display the value of a variable or expression.
print is very flexible; you can dereference pointers, display addresses of variables, and index into arrays with the
*, &, and [] operators.
(gdb) print c
$1 = 115 's'
(gdb) print *iter
$2 = 107 'k'
(gdb) print str
$3 = 0x7fffffffe140 "ssskson, Mississippi"
You might not see the exact same values as above, depending on where you are in the execution of the program.
However, you should notice that the string "Jackson, Mississippi" is being overwritten with the character 's' ... which is not what should happen when this program runs!
Exercise 3
Setting command line arguments
The command
run
can be called with the command line arguments that you want to send to the program (i.e., arguments to the main function).
For example, without GDB we might run program charcount2 with this command:
$ ./charcount2 "Hello World" l
Within GDB, we would run this program as follows:
(gdb) run "Hello World" l
Starting program: /home/username/cspp50101/lab5/gdblab/charcount2 "Hello World" l
...
Examining the call stack
When a program is running, the call stack is used to keep track of the active functions.
As an example, look at the file charcount1.c: during a call to function char_count(), there will be two frames on the stack: one for main() and one for char_count().
(A frame stores information about an active function, such as the value of arguments and local variables.)
After char_count() finishes but before the program ends, the stack will only have one frame, for the function main().
The commands
up and down
move "up" and "down" the call stack, that is, they will reset the current frame that is visible to you in GDB.
The commands where and info stack are equivalent to backtrace.
You will find that these commands are valuable for debugging segmentation faults, such as the one in charcount2.c.
A segmentation fault occurs when a program tries to access memory in a way that is not allowed, for example, writing to a memory address that is read-only
or dereferencing a NULL pointer.
Finding and fixing a segmentation fault
You will use the file charcount2.c for this exercise.
$ ./charcount2 "Jackson, Mississippi" s
Counting occurrences of 's' in string "Jackson, Mississippi"
Segmentation fault
You will see a "Segmentation fault" when you run this program. This is the bug that we want to fix.
$ gdb charcount2
Starting program: ...
...
(gdb) run "Jackson, Mississippi" s
Starting program: /home/username/cspp50101/lab5/charcount2 "Jackson, Mississippi" s
Counting occurrences of 's' in string "Jackson, Mississippi"
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400552 in char_count (c=115 's', str=0x0) at charcount2.c:13
13 while( *iter != '\0')
backtrace
#0 0x0000000000400552 in char_count (c=115 's', str=0x0) at charcount2.c:13
#1 0x00000000004005e8 in main (argc=3, argv=0x7fffffffe208) at charcount2.c:39
(gdb) print iter
$2 = 0x0
(gdb) up
#1 0x00000000004005e8 in main (argc=3, argv=0x7fffffffe208) at charcount2.c:39
39 int count = char_count(c,str1);
(gdb) print count
$2 = 0
(gdb) print c
$2 = 115 's'
(gdb) print str1
$3 = 0x0
Exercise 4
$ ./charcount2 "Jackson, Mississippi" s
Counting occurrences of 's' in string Jackson, Mississippi
Count: 5
Submit your updated file as part of this lab.
Submitting your assignment
Lab 5 is due: Saturday, July 30 at 11:59pm
$ hwsubmit cspp50101lab <path to your lab5 directory>
Submit to cspp50101lab, NOT cspp50101.