Due Wednesday, April 6th, 11:59pm

Goals for this Lab

  • Get your system set up for work this quarter
  • Familiarize you with most of the tools necessary for all steps in the C development cycle: edit, compile, and execute. We will learn debugging later.
  • Write a simple C program to get used to the syntax.

For this lab, you are welcome to get technical help from another student on how to use or install any of the tools involved. You may also get syntax help on C. You may not, however, get help on the algorithmic portion of the exercise outside of office hours or piazza questions to TAs or instructors.

This lab is broken down into several steps:

Homework 1, which includes warmup 1, will be collected from your subversion repository on Wednesday, April 6th, at 11:59pm.

Set up

First, starting next week, we will begin Duet Programming. In order to assign partners, all students (whether or not they are interested in Duet Programming) need to fill out the following form: Duet Programming Form Your personal repository is
https://phoenixforge.cs.uchicago.edu/svn/CNET-cs152-spr-16
with your own CNet ID in place of CNET.

To check out the repository to your current location:
$ svn checkout https://phoenixforge.cs.uchicago.edu/svn/CNET-cs152-spr-16

This will give you a working directory that you can commit to, storing it for retrieval from any location.

Now set up your working directory. I will not give this level of directions every lab - this is just to remind you of the unix commands that accomplish these tasks. You may refer back to this lab if you forget in the future.

$  cd CNET-cs152-spr-16
$  mkdir hw1
$ svn add hw1
$  cd hw1

Edit

We expect you to use vim (for editing) and clang (for compiling) this quarter, though there are many other tools available for code editing and C compilation. These two have the virtue of working entirely from the console. Among other desirable properties, this makes it painless to work on the CS network using vim and clang from a remote computer (such as your laptop, in your room, on a freezing winter night).

Here are a few words about each of these applications.

vim

Vim stands for "vi improved" and is an updated (though not recently) version of the classic editor vi. Vi (when you say this out loud, you say both letters individually: "v i") is a simple, lightweight, scriptable editor that has been in use since the 1970s. Please note that we will use the names vi and vim mostly interchangeably as we move forward (as is common practice in the world at large).

Vi has a modal interface, which is to say it has various modes you can be in (or not in) at any given moment. When you start vi, you are in command mode. To edit text, you need to switch to insert mode. You switch to insert mode by typing i, and you switch to command mode by typing the esc key.

You issue commands in command mode by typing a colon (:) and then one or more characters naming the command; for example, :q quits vi and :w writes the current file to disk (think of :w as "save").

At some point this week, but not during this lab period, you should go through vimtutor. This will take about 30 minutes and will give you practice with an assortment of vi tools and techniques. You can do so by opening a terminal and typing vimtutor.

An Example

In order to practice editing and compiling, let's begin with a small function that we have already written for you. Open a new file named functions.c, then copy and paste this function into the file. If the formatting is not retained, use your vi skills you just learned in vimtutor and edit the file. You can copy using the standard copy mechanism. Then paste in vi by going into insert mode (by pressing 'i') and using the standard paste mechanism. Because the mechanisms are dependent on the type of computer you are current sitting at, I am not describing the mechanisms.

To open a new file named functions.c:
prompt$ vi functions.c

This code has been chosen especially to introduce you to not only the syntax and semantics of C, but also norms we will expect in this class. Take note of:

  • The types in the input parameters and returns
  • What to do when erroneous input is received
  • Explanatory comment for each function
  • Inline comments for the useful function
  • Proper indentation
  • Examples of using printf with different types and different configurations

Click on this link to get the code. Then you can place yourself in insert mode in your file (press 'i'), copy the code from the screen, and paste it into your file. Then press to get out of insert mode (and into command mode). Then type :wq to write the file and quit vi. Then you will be ready to compile and run the program.

A few comments:

  • The return type unsigned long int is a nonnegative integer that is also "long" (that is, has a larger maximum value than a plain "int"). We will talk much more about this ando ther type distinctions in the coming weeks.
  • The argument type unsigned int is nonnegative. This is desirable in the present case since we don't have a definition of factorial on negative inputs anyway.
  • Note that C is entirely permissive when it comes to spacing and line breaks. Nevertheless, please follow the spacing and indentation conventions you see on this page and in K&R (that is, The C Programming Language, Second Edition by Kernighan and Ritchie). If there is ever any doubt about how to format code, we refer to K&R's body of examples as the authority.

Testing Methodology

There are several general principles we will follow for testing.
1. Place your main in a separate file from your implementation so that you can swap out the main easily.
2. Create a set of test cases for each function that exercise the code well.
3. Create one test function for each function you're testing that takes in a single test case and tests it.
We'll illustrate those three principles below.
Principle 1: Place the main in a separate file.

Now that you have code written, you need to test it. This requires a main file. All C programs begin in main. We are going to place this main function in a different file so that we can easily swap out what code is calling this function.

We also need a way for the function calls in the main code to get to the functions we wrote in functions.c. A header file is necessary to inform the code in main.c about what the function call names are, what the input arguments are, and what the return types are.

Whenever you call functions that are in one file from another file, you need a .h file. Make a new file called functions.h. Place the code found here in the file. This shows the format of the file, but it has only one function in it. You need the prototype of each function in the file, along with a comment stating the purpose.

Now we need the main function. Make a new file named "main.c". Click on this link, then copy and paste the code into your file.

Compile

clang

You will compile your programs using clang, a popular open-source C compiler that ships with current Macintoshes, like those we have in CSIL, and is furthermore available for free installation on other platforms.

Your program has three files - functions.h, functions.c, and main.c.
If a program has only one file, the compile line is the following:

$ clang myprogram.c
This is not the case for us, however. You have two choices - compile each part individually and combine at the end or compile in one step.
To compile in one step:
$ clang functions.c main.c

To compile separately, use the -c file. That tells the compiler that you're just compiling a single file, not the entire thing. Instead of creating an executable, it only creates a .o file containing the compiled information for that one file.
$ clang -c functions.c
$ clang -c main.c
$ clang functions.o main.o

If the compilation is successful, you'll see nothing at the terminal, but the compiler will have created an executable file named a.out. You can run the executable by typing ./a.out.

You can specify a different filename for the executable, other than the default name a.out, with the -o flag as follows:

$ clang -o go functions.c main.c
and then run it with ./go.

If your program includes math.h, depending on the system you're working on, you might need to include the compiler flag -lm to link in the math library. This flag appears as another option to the compile command, like so:

$ clang -lm -o go functions.c main.c
Since -lm is sometimes necessary and never harmful, you should make a habit of compiling with it whenever math.h is part of your program.

You can give command-line arguments to clang in standard UNIX style. We will spruce up the call to clang in this case to do two things: first, to be aggressive about warning the programmer about potential problems in the code, and second, to name the executable something other than the standard a.out. Try this command:

$ clang -Wall -o testprogram functions.c main.c
The option -Wall means "all warnings." You will come to appreciate these warnings; they save you a lot of time and toil.

Makefile

There is a compilation tool make that looks for a file named Makefile containing compilation instructions. You will now create a Makefile containing instructions on compiling the evidence tool in progress. Create Makefile and write the following into it:

testprogram: functions.h functions.c main.c
        clang -Wall -o tests functions.c main.c
Please note that on the second line (after the line that starts with testprogram:), the first character is a TAB: exactly one of them. This is important; the Makefile is not well-formed without it. Having created this Makefile, you can now compile the "tests" executable by simply typing
$ make testprogram
$ ./testprogram
Try it!

Dissecting the first Makefile line: The first word is the target. If you type that label into make, then it goes to that rule first. After the colon, it has a list of files. These are all of the files on which the executable depends. It looks at when those files were changed. If they were changed more recently than the target has been compiled, then it executes the compile line.

Make is a language-agnostic tool; it doesn't care if it's compiling C, Racket or anything else. It has many more capabilities than we will discuss today or even this quarter. It is a powerful assistant in software development of all stripes. The more code you write, the more you will have occasion to use and appreciate it.


Execute

Once you have compiled your code, you are ready to test it through executing it. If you did not name it anything special, then you will run it with:
$ ./a.out

To kill a program that is running for too long, because of an infinite loop or for any other reason, type Ctrl-c in the terminal.

Debug

Once you start executing, you may find that one of your test cases has a result that does not match the expected result. You are now ready to move on to debugging. Think of debugging as an investigation. Let's say you lost your keys. You know you had your keys this morning, and now, 8 hours later, you notice you do not have your keys. The beginning and end is not enough information. You need to figure out all the places you went in order to think about where those keys might be. Likewise, with bugs, you need more information than the beginning and the end. You need to think about what the expected result is partway through the execution of the program, and then see if it is that result there (with a print statement). You can, in essence, use a binary search of your code to find your bug. Print out intermediate results during the program, and gradually narrow down when the execution goes differently than you thought it should.

When you are done, save the file. Then add it to your svn repository. This would be a good time to commit your work!

Compile Errors

Often, you will make mistakes when you type in your code. The compiler will find many of these. Unfortunately, sometimes it is difficult to figure out what the problem is from the error messages. In order to familiarize yourself with some common error messages, you're going to intentionally introduce errors and then see what error messages result from them.

Before you start, copy your working files to new filenames so you don't ruin your working files.

$ cp main.c errormain.c
$ cp functions.c errorfunctions.c

Copy and paste the following list into a file named errors.txt. For each problem, it first tells you what error to introduce into one of the files. Once you do this, compile the code again. Remember to use these files names - errormain.c and errorfunctions.c. Then copy and paste the error message into the file on the line starting with #). Then correct the code before moving to the next one.


1. Remove the line #include "hw1.h" from errormain.c
1)

2. Declare the same variable name twice
2)

3. Remove a variable declaration
3)

4. Misspell a variable name or function name
4)

5. Remove a closed curly brace
5)

6. Remove the line #include from errormain.c
6)

7. Remove a semi-colon from the end of one of the lines
7)

When you are done, save the file. Then add it to your svn repository. This would be a good time to commit your work!

Submit

At this point, you should have done the following:
  • Checked out your repository
  • Created a folder named hw1 in your repository and run svn add hw1
    $ svn add hw1
  • Created five files and filled in the proper information: functions.h, functions.c, main.c, errors.txt and Makefile inside your hw1 directory.
  • $ svn add functions.c functions.h main.c Makefile errors.txt
  • Compiled your executable manually and with the Makefile
  • Executed your code to make sure it runs properly and inspected the results.
  • $ svn commit -m "hw1 warmup complete"
Now you're ready to write your own functions!!! Move on to the hw1 description.