Homework 2: A Little Code


If you have a question, just send email to 354-help@cs.wisc.edu and we'll try to get back to you quickly. Don't worry, it's just to the TAs and professor.

How to send a good email: Put a lot of information in it! For example, cut and paste what you typed on the screen, and what was printed on the screen as a result. Don't just say that something didn't work!

Relevant Book Chapters

Relevant reading is all from K+R . Chapter 1 is probably enough; Chapter 5.7 covers multi-dimensional arrays; Chapter 7 has more on I/O.


The main purpose of this project is for you to write some C programs; they are each very short. It's also a good time to think a little bit about testing, which will become increasingly important.

Due Date: Sunday 2/5 at some point

Part 1: A Simple Program

In this assignment you will write some real C programs. If you haven't written in C before, these will be your first! So remember them well; one day you may be showing them to your grandchildren, should you have any, or someone else's grandchildren, should you not.

The first program is very simple; it's called uppercase.c . This program should use getchar() to read in letters from standard input. If the character read in is a lowercase letter, the program should print out the uppercase version of that letter. For example, if you read in 'a', you should print out 'A'.

Because we didn't quite get to it in class, you may wish to learn more about getchar() . If so, here is a simple overview.

If you read a character that is not a lowercase letter, you should just print it directly without changing it at all.

To print out a letter, you can use putchar() or printf() . Don't forget to include stdio.h at the top of your program; without it, the compiler will complain.

Because we didn't learn about printf(), you may wish to read more about that, too. Click here for that.

A simple skeleton of uppercase.c is available here . It has a few pieces that we have not talked about in class yet (like the #include at the top, and the exact arguments to main(), argc and argv). We'll talk more about that stuff next time, but for now you should be able to just use the skeletal code as a starting point.

To compile your program, you should type:

prompt> gcc -o uppercase uppercase.c -Wall

This line produces an executable (a.k.a. a binary) that you can then run. To run your program, you should type:

prompt> ./uppercase

At this point, your program will start running, and, as you type into it, it should mirror back what you are typing, except when you type a lowercase letter, in which case it will return the uppercase version of the input.

Tricky: If you don't know your way around Unix yet, you may find it hard to quit this program. To do so, type Control-d (i.e., hold down control, and while still holding it, press the d on the keyboard). Doing so sends an end-of-file ( EOF ) notification to the program, which, if you wrote the program correctly, should cause it to terminate. If things really aren't working, you can press Control-c , which is a way to terminate most any program you are currently running (by sending it a Unix signal - don't worry, we haven't learned about stuff like this yet, though you might want to on your own).

Make sure to return the value 0 at the end of your main() function. If you don't, your program will not pass our tests. It is considered good form to return 0 when the program terminates successfully.

Example input/output: If the input for the program is

the output should be

Part 2: Counting Words And Such

Your second program isn't too much more complex; it's a simple word counting utility. It should be called wordstats.c , and compile into a binary called wordstats naturally.

The wordstats program should count each character and keep track of how many characters it sees before EOF. It should also count how many whitespaces it sees along the way; we defined whitespace as either a space ' ' or a tab (special character '\t'). Your wordstats should also count how many different words it sees; words are separated by whitespace or newlines. Wordstats should also track how many uppercase and lowercase letters it sees, as well as how many numeric digits it sees. Finally, it should also count how many lines of text it reads; a newline indicator is '\n' (basically, you just count these).

When wordstats is finished with the input, it should print a single line of output, as follows, likely using printf(), in this order:

chars words lines whitespaces uppercase lowercase digits
Of course, the output should just be a bunch of numbers, not the words above.

Example input/output: If the input looks like this:

1234 56
hello this is a word
Finally this line !
you should output:
49 11 3 8 1 30 6

Part 3: A Slightly More Complex Program

In this last part, we will create a slightly more complex program. This program is called reverse.c and it takes in input line by line, stores all these inputs, and then, when the input stream is finished, print out all the lines in (you guessed it) reverse order.

You'll have to make a few assumptions to make this program work. The first is probably the most important: your program does not need to work if more than 100 lines are input. Specifically, if you read in 100 lines and the input stream is not finished, you should print the following message and return from main (exit the program):

error: too many lines.
In this case, you should return 1 from main (and not 0), to indicate something has gone wrong. Otherwise, print out all the lines in reverse and return 0.

To read the input, you should use fgets() ; more information on fgets() is available here . Here you can make another assumption: that no line will be longer than 128 characters in length (including the '\0' character that marks the end of a string).

To store your input, you can use an two-dimensional array of characters. Here's an example:

char a[10][128];
This would declare room for 10 strings; each string can be accessed as such: a[0], a[1], etc. Each string can be 127 printable characters with this array and a '\0' to mark the end.

Compile your program as above:

prompt> gcc -o reverse reverse.c -Wall

Example input/output: If the input looks like this:

your output should look like this:


An important part of programming is testing your code to make sure it works. This means writing more code usually!

At first, you'll probably just run your program, type some stuff, and see if it does the right thing. This is not a bad place to start.

However, as you want to automate testing, this is not so great. Fortunately, in Unix-based systems, you can actually create files with input in them and feed them to your program using something called a unix pipe - a cool feature for sure. For example, let's say you created a file called test1.txt to test your uppercase program, with a mix of lower and uppercase letters in it. To automatically feed this to your uppercase program, you would type:

prompt> cat test1.txt | ./uppercase
in the directory where your two files (test1.txt and uppercase) reside. The vertical bar is called a pipe, and it is an operating system feature provided by all Unix OSes. What it does, in a nutshell: takes the output of the left part (what cat is printing to the screen) and arranges it to be sent directly to the input of the right part. It's like you typed whatever is in test1.txt into uppercase. Neat!

Play around with this if you like; it's certainly how we will test your code.

Handing It In

To hand in these programs, you just have to put the C source code files into your handin directory, under the subdirectory hw2/ (naturally).

To do so is quite easy. One way to do this is simply to cd into your handin directory and create (and test) these programs there. If you were user remzi for example you would do this:

prompt> cd ~cs354-3/handin/remzi/hw2/
prompt> emacs -nw uppercase.c

And then type away.

If you created the files somewhere else in your home directory, for example, in your private/ directory in a sub-directory called hw2 or something like that, you could simply copy it to the final location using the cp program:

prompt> cp uppercase.c ~cs354-3/handin/remzi/hw2/

Note: if the destination (second argument) to cp is a directory, the cp program just creates a file of the same name as the source (first) argument in the destination directory, as we do above. You could also specify the name of the file exactly (more useful if you are changing the file's name while copying it):

prompt> cp u.c ~cs354-3/handin/remzi/hw2/uppercase.c

At the end of copying, you should check that all the files are there:

prompt> ls -l ~cs354-3/handin/remzi/hw2/

The program ls lists files in a directory and thus should show all the above source files therein.