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:
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.
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.
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.
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.
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.
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!
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
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.
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.
For this portion, you will write two array functions:
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.
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.
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:
findcharindex
:
Returns the index of the first instance of a specific character in a string.
If the string does not have that specific character in it, return -1
.
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”).
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
All content was graciously provided by Professor Diana Franklin, with slight formatting-related adaptation.