CSPP 50101 - Lab 5

In this Lab: Lab 5 is due: Saturday, July 30 at 11:59pm

Files for this lab are located in: /stage/classes/archive/2011/summer/50101-1/lab/lab5/src

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.

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:

$./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".

In main(), printf() is called to output these strings to the terminal:

   printf("Arg[0]: %s\n",argv[0]);
   printf("Arg[1]: %s\n",argv[1]);
   printf("Arg[2]: %s\n",argv[2]);

You can read more about command line arguments in Ch. 11 of Prata.

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

In main(), you can see that malloc() is called to allocate memory for a new character array:

  /* 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.

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():

#include <stdlib.h>

You can read more about malloc() and dynamic memory allocation in Chapter 12 of Prata.

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

Your output should look like this:

$./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.

Your program should call malloc() to allocate memory for 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.

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.

Help and references with GDB

As you go through the GDB section of today's lab, you can find help with the following references:

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.

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.

Running a program in GDB: run

You can execute a program within GDB with the command run

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.

Use the command break, or the shortcut b, as follows:

 break <function>
 break <linenumber>
 break <filename:function>
 break <filename:linenumber>


You can display all active breakpoints with the command info break.

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.

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.

Stepping through and into a function, by line: next and step

We can use the command next and step to examine a program.

next executes one line of source code.

As you see above, if the line includes a function call:
 
   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.

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.

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.

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

If you call run without arguments, then GDB will use the arguments from the previous call (if any).

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

Exercise 4

Submitting your assignment

Lab 5 is due: Saturday, July 30 at 11:59pm

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.

$ hwsubmit cspp50101lab <path to your lab5 directory>
Submit to cspp50101lab, NOT cspp50101.

prepared by Sonjia Waxmonsky