CS24 Week 1 Lab: Bug Hunting and a Review of C Arrays

Due Date: Tuesday, July 1st at 11:59:59 PM

This lab covers very basic use of UNIX, C, and debugging techniques, all of which should be review. After completing this lab, you should be able to:

Pair Programming

You may work with a partner for this lab. If you choose a partner, both members must contribute to the same work. That is, you cannot simply divide the lab in half, as everyone is responsible for all the content. Instead, you should use a pair programming style. In pair programming, one person acts as a driver, who takes control of the keyboard. The other person acts as a navigator, who reviews what the driver writes and offers advice. Oftentimes, the driver is occupied with details of the problem (e.g., variables, control flow, etc.), whereas the navigator is concerned with bigger-picture problems (e.g., functions, design, interactions). This works well for problems which are simply too large to fit into one person's head. If you're unfamiliar with the concept of pair programming, you may wish to watch this video on the topic.

With pair programming, oftentimes it works best if both participants are of approximately the same skill level. Generally, if the disparity in skill is large, the end result is frustrating for both parties - it really only works when both parties operate at approximately the same speed. With this in mind, you should select someone of approximately the same skill level.

Reading

Because these are review concepts, there are more resources this week than in a normal week. If you already understand the concept, you may skip the reading. All of these concepts are in your CS16 textbook, which you should still possess for reference in this class.

Part A: Bug Hunting

The purpose of this part is to get back into the swing of things by finding the bugs in a piece of code, not through code inspection, but by choosing test cases in a methodical manner. This is an invaluable skill.

Step A-1: Log in

If you already have a College of Engineering account, sit at a computer. If you do not have such an account, partner with someone who does. For those with an account, you may have to partner with someone without an account, even if you wish to work alone. By the next lab, everyone should have an account.

Step A-2: Create Directories and Copy Files

First create the directories for this class and this lab, and use the chmod command to prevent other students from looking at your code. The use of chmod here is important - if someone steals your code, it is generally impossible to determine who stole from who, so this is for your own protection. The instructions are below:

-bash-4.2$ cd
-bash-4.2$ pwd

/cs/student/yourusername
-bash-4.2$ mkdir cs24
-bash-4.2$ chmod 700 cs24
-bash-4.2$ cd cs24
-bash-4.2$ pwd

/cs/student/yourusername/cs24
-bash-4.2$ mkdir lab1
-bash-4.2$ cd lab1
-bash-4.2$ pwd

/cs/student/yourusername/cs24/lab1

Now copy over the files for this week. They are kept in the directory: /cs/student/kyledewey/cs24/labs/1

-bash-4.2$ cp /cs/student/kyledewey/cs24/labs/1/* .

Note that there is a '.' at the end of that line. cp means copy. /cs/student/kyledewey/cs24/labs/1/* means all of the files contained in the directory /cs/student/kyledewey/cs24/labs/1. The '.' means to copy from that location to the current location.

Now check to make sure the copy worked correctly:

-bash-4.2$ ls

You should see a set of files listed that will be used for this warm-up.

Step A-3: Read Debugging Introduction

Before we get started with programming, we will first teach you how to perform debugging tasks. Debugging is easy to overlook - until you can't figure out what is wrong with your code.

There are two high-level approaches to testing code. I'll call one the code coverage approach and the other a black-box approach.

The code coverage approach is based on the code itself, making sure you have proper testing coverage. This means that there must be a test that covers every path through the code (assuming your code has several if statements / loops / etc.) This requires you to look at the code.

The other approach is to treat the code as a black box, meaning that you cannot look into it. This means all of your focus is on the inputs and how you think it will exercise different aspects of functionality.

In this exercise, we have provided the code, but you are not allowed to look at it. Therefore, we are presenting approaches for black-box testing.

Testing should always be methodical, never just randomly trying out test cases. For each input, make sure you think of all of the different, meaningful ways that input could change. For example, if you have an integer, think of testing with a normal size integer (5), then a large integer (10000), then zero, then a negative integer (-5), then a small integer (-10000). If there are multiple inputs, try all combinations of interesting inputs (both positive, 1st positive and 2nd negative, etc.).

If there is a range of acceptable inputs (1 to 10, for example), then you need to test at the borders and just across the border. For example, you would test values 0, 1, 10, and 11.

For any code based on user input, there is another category of inputs - ill-formed inputs. So make sure that you also think about inputs that are not what the program expected.

Now there's only one thing left to do - try to find the bugs in this code!

Step A-4: Read Debugging Excercise

In this lab you are given an executable named find_bugs. This is a buggy version (intentionally) of a program to evaluate a single math expression in infix-notation entered via standard input. The program operates only on base10 integers, and only performs addition (+), subtraction (-), and division (/). The operator and the numbers must be separated by a single space.

For example:

./find_bugs
>Enter arithmetical expression
>1 + 2
3

Step A-5: Plan Test Cases

Note that you have three inputs - two numbers and an operator. Make sure you take a methodical approach. First, write down the different interesting choices for each input (number, operator, number). Second, write down different ill-formed inputs (incorrect number of inputs, incorrect order of inputs). Then write down a set of test cases that methodically go through and test all of the different combinations of interesting and ill-formed inputs. This will form your test suite.

Step A-6: Test, Find, and Record Errors

Now copy and paste this set of inputs into the test program as it is running. Some of the inputs will produce the right answer. However, some of the inputs might break the normal operation of the executable. In this case you will see the output:

BUG FOUND: Save as input_[n].txt

What this is telling you is to create a text file named input_[n].txt. The program will tell you the name of the file. Whatever input you used that caused that error should be placed into that input file. For example, if the input was 3 + 2, then 3 + 2 should be put into the specified file.

When you are done, you should have a sequence of inputs files, input_1.txt, input_2.txt, ..., input_9.txt. (There are nine bugs to find in all.) If you do not find them all, just create empty input files for the missing ones. Empty files in UNIX can be created most easily using the touch command, like so:

-bash-4.2$ touch filename

...where filename is the name of the empty file you want to create.

Part B: Arrays and Strings in C

Step B-1: Array Functions

For this portion, you will write two array functions:

  1. findmaxindex: Return the index that contains the maximum number held within an array. If the array is size 0, then return -1. Note the similarity to the findmax function you wrote in class.
  2. findsecondlargest: Return the value of the second largest number in an array. If the array has two instances of the largest number, then the second largest is also the largest number. If the array has fewer than two items, return -1, (even though this is not a good solution).

For example, consider an array containing the following numbers in the specified order:

12 5 6 8 10 3 -5 15 18 16 20

The above will return 10 as the maximum index and 18 as the second largest number.

Now consider an array containing the following numbers in the specified order:

12 5 6 8 10 3 -5 16 18 16

The above will return 8 as the maximum index and 16 as the second largest number.

Now consider an array containing the following numbers in the specified order:

12 5 6 8 10 3 -5 16 16 16

The above will return 7 as the maximum index and 16 as the second largest number.

To compile the code, type:

-bash-4.2$ clang -g arrays.c

Note that the -g argument to the clang compiler (this is consistent with gcc as well) means to build the executable (a.out in this case) with debugging information. We are getting you used to the habit so that you are ready to use gdb later to help you debug your program.

If the compilation fails, make sure you read the error message completely. It often includes the line number and a hint as to the problem. Look at that line and the lines immediately preceding it to find the error. Make the necessary changes (clang should be more useful than gcc at reporting potential problems -- that's why we are using clang rather than gcc) and then repeat until your code compiles successfully.

If the compilation succeeded but with warnings, you should treat these as errors. Warnings are often statements that you are doing something strange that is likely to be incorrect. This goes double for warnings you do not understand.

Assuming compilation succeeded, you can run your code like so:

-bash-4.2$ ./a.out

Make sure you run your code with several test cases, as you learned in the warm-up exercise. The two above are not sufficient. Think about the fact that the input is an array with different sizes, and for each size, values in different locations. There are far too many possible combinations to test. Instead, you need to figure out which different values representing different test cases to your code. For example, for sizes, 0, 1, and many are all that matter. For values, positive and negative values matter, as well as how many maximum or next to maximum duplicates there are. For positions, it matters whether the index is at the beginning, end, or somewhere in the middle.

Step B-2: String Functions

The next task is to complete one string function. This is to practice two things. The first is to treat strings as nothing more complex than an array of characters ending in '\0'. Instead of being given the size of the array, you have to keep checking until you find the '\0' character. This requires a restructuring of your loop. The second purpose of this part is to reinforce how to use pointers vs arrays. If you aren't sure, make sure you read this arrays vs pointers vs strings tutorial. You should definitely read the tutorial if you aren't sure how to treat the input arguments because they are declared as strings, and you are used to handling only arrays.

In arrays.c, the skeletons are already provided. Make sure you do not call any string functions - you must implement these yourself. The function you must implement is detailed below:

To compile and run the code, type the following:

-bash-4.2$ clang -g arrays.c
-bash-4.2$ ./a.out

If you run into a segmentation fault that you do not know how to find, you can run the program with gdb like so:

-bash-4.2$ gdb ./a.out

At the prompt for gdb, type run to start it. Then, test it with the input that triggers the segmentation fault. Once it hits the segmentation fault, type bt (short for “back trace”).

Submitting your Work

First, fill out the required fields in the provided README.txt file. Once you have filled this in, you may submit everything with the following command:

-bash-4.2$ turnin lab1@cs24 README.txt arrays.c input_1.txt input_2.txt input_3.txt input_4.txt input_5.txt input_6.txt input_7.txt input_8.txt input_9.txt

Acknowledgements

All content was graciously provided by Professor Diana Franklin, with slight formatting-related adaptation.