Try Firefox!

Lab 3: C Basics


Part 0: Compiling a simple program
Instead of writing, compiling, testing and debugging within some IDE like Eclipse or Visual Studio, we'll be using a text editor (emacs) to create source code files. Then we'll be calling the compiler ourselves to compile and link programs. Then we'll call our new programs from the command line, just like any other program. Make a lab03 directory, and create the following files using emacs.

ex1.cfib.hfib.c
#include <stdio.h>
#include "fib.h"

int main()
{
  int n;
  fscanf(stdin,"%i",&n);
  fprintf(stdout,"f(%i) = %i\n",n,fib(n));
  return 0;
}
int fib(int n);
#include "fib.h"

int fib(int n)
{
  if (n < 2)
    return 1;
  else
    return fib(n-1) + fib(n-2);
}

Most of your programs for this class will be single file programs, but we're starting out with multiple files because it's important that you understand the process. Creating a program consists of two phases: compiling and linking.

bash$ gcc -c ex1.c compiles source code file ex1.c to object code file ex1.o
bash$ gcc -c fib.c compiles source code file fib.c to object code file fib.o
bash$ gcc -o lab3p0 fib.o ex1.o links ex1.o and fib.o into the executable file lab3p0
	  
Each source code file gets compiled into an object code file, which is a compiled chunk of an actual program. This is the compilation phase. In the link phase, all these chunks of compiled code get put together to create an actual program. This includes linking in object code for standard libraries, like stdio or stdlib.

From the perspective of a little programmer, it might seem like a pain that each file gets compiled separately. However, imagine you're a developer for MS Word ... there are thousands of source files, and you just fixed a type in one. Do you want to wait hours for everything to compile? No, just recompile the one file you changed when you fixed the typo, and leave the rest unchanged. Then relink --- which is lots faster than compiling --- and you're done. This is referred to an incremental compilation.

After than pep talk about incremental compilation, here's a trick to speed things up. The one line

bash$ gcc -o lab3p0 ex1.c fib.c	    
	  
is exactly equivalent to the three lines above. So for small programs, you usually just create the program this way.

Part 1: Simple I/O
You're going to write a simple program that's a bit like grep, except you're choosing lines because some number falls in a range. In particular, we'll be using some temperature data, and our program will grab out lines that fall in a particular range. This website has average daily temperature readings from 1995 to now for a collection of cities. I've put a simple utility "httpcat" in my homedirectory for you so you "cat" the temperature data for a city (i.e. print it to stdout). For example, to get Chicago's average daily temperatures for the past 15 years, just do this:
~wcbrown/bin/httpcat www.engr.udayton.edu/faculty/jkissock/gsod/ILCHICAG.txt
	
for Baltimore do this:
~wcbrown/bin/httpcat www.engr.udayton.edu/faculty/jkissock/gsod/MDBALTIM.txt
	
Your ultimate job is to write a program that takes a temperature range as command-line arguments and reads data in this average-daily-temperature format. For example, here are the days in LA with an average temp above 80:
bash$ ~wcbrown/bin/httpcat www.engr.udayton.edu/faculty/jkissock/gsod/CALOSANG.txt | lab3p1 80 100
9       3       1998    84.600000
9       29      2005    80.500000
10      24      2007    80.800000
4       27      2008    81.500000
6       22      2008    80.300000
10      1       2008    81.100000
	

  1. Create a program lab3p1 that reads text from standard input that follows our average daily temperature format, i.e.
    month (whitespace) day (whitespace) year (whitespace) temperature (newline)
    	      
    and prints out to standard output all lines whose temperature is between 10 and 20 degrees.
    bash$ ~wcbrown/bin/httpcat www.engr.udayton.edu/faculty/jkissock/gsod/MSJACKSO.txt | lab3p1
    2       3       1996    17.900000
    2       4       1996    15.100000
    2       5       1996    19.800000
    	      
    Make sure you compile and test this! This part doesn't take much more than fprintf, fscanf, and a loop!

  2. Next modify lab3p1 so that it takes two command line arguments defining the range we keep, rather than having range 10-20 hardcoded. The first argument is the lower bound of the range, the second is the upper bound of the range. (The bounds are included in the range for this problem.)
    bash$ ~wcbrown/bin/httpcat www.engr.udayton.edu/faculty/jkissock/gsod/MSJACKSO.txt | lab3p1 10 22
    2       3       1996    17.900000
    2       4       1996    15.100000
    2       5       1996    19.800000
    1       24      2003    21.100000
    1       9       2010    21.100000
    	      
    Command line arguments in a C program are, of course, totally new. So here's a word of explanation. If, instead of defining main as "int main()" you define it like this:
    int main(int argc, char **argv)
    	      
    the argument argc gets set to the number of elements in the process's argv vector and argv is ... well, it's the processes argv vector. Remember, if you call the program like this:
    bash$ lab3p1 10 20
    	      
    then argv[0] = "lab3p1", argv[1] = "10", and argv[2] = "20". So argv is an array of strings, and in C strings are arrays of chars, thus the type of argv is char**. It's an array of arrays of char.

    Now, argv[1] and argv[2] are strings, but they're strings that represent numbers. What we need is a way to convert a string representing the number to the actual number. For type int, this funciton is atoi. For type double it is atof. Use man and you should be able to figure out how they work. Don't forget to actually test this!

  3. Finally, to do this thing right, we need to make sure that it behaves gracefully when someone runs it without the two command-line arguments we need. Modify lab3p1 so that it prints the following message to stderr if there are not enough command-line arguments:
    lab3p1: Error! Not enough arguments!
    	      
    and exits with return value 1. Remember, this is an error, so it has to be printed to stderr, not stdout. [Note: when your program does this, if you are running it with its input piped from httpcat's output, you'll also get some error messages from httpcat.] Assuming you saved the MSJACKSO.txt data in a file named tmp, here are some example runs:
    bash$ cat tmp | lab3p1
    Error: too few arguments!
    bash$ cat tmp | lab3p1 10
    Error: too few arguments!
    bash$ cat tmp | lab3p1 10 22
    2       3       1996    17.900000
    2       4       1996    15.100000
    2       5       1996    19.800000
    1       24      2003    21.100000
    1       9       2010    21.100000
    	      

Part 2: A silly little program for practicing strings and malloc
The website we used to get average daily temp data in the previous part has a funny naming convention: for a city, build a name by starting with the state abbreviation, followed by the first 6 characters of the city name, followed by .txt. Oh, by the way, that's all caps!

Your job in this part is to create a little utiliy that builds that name for you. Here's an example in action:

bash$ lab3p2 Chicago IL
9
ILChicago
ILCHICAGO
ILCHICAG.txt
bash$ lab3p2 Los Angeles CA
12
CALosAngeles
CALOSANGELES
CALOSANG.txt
bash$ lab3p2 New York NY
9
NYNewYork
NYNEWYORK
NYNEWYOR.txt
	  

  1. First write a program lab3p2 that returns the sum of the lengths of its command line arguments (skipping argv[0]). For example:
    bash$ lab3p2 Las Vegas NV
    10		
    	      
    To compute the length of a C-style string, use the strlen function. Do a "man" to find out more about the function, including which library to include.

  2. Next extend lab3p2 so that it allocates an array big enough to hold the concatenation of the command-line arguments, and concatenate the arguments together, with the last argument first, and then the other arguments in order. For example:
    bash$ lab3p2 Los Angeles CA
    12
    CALosAngeles
    CALOSANGELES
    CALOSANG.txt
    	      
    To do the concatenation, use the strcat function. Don't know how? Read the man page!

  3. Now change the string to all upper-case. Instead of thinking, just iterate through the characters in the string, replacing them with the result returned for them by toupper. Don't know it? "man" it!
    bash$ lab3p2 Los Angeles CA
    12
    CALosAngeles
    CALOSANGELES
    

  4. Finally, chop off everything after the 8th character of the string. Hint: this is actually really easy! Then print out the string followed by .txt ... and you're done!
    bash$ lab3p2 Los Angeles CA
    12
    CALosAngeles
    CALOSANGELES
    CALOSANG.txt
    

Submission
Make sure that your lab3p1 and pab3p2 solutions are in the same directory (lab03) with a README file that contains your name and states how far you got with the two solutions: i.e. they are properly functioning implementations of which versions? Also make sure that the directory contains a file ans.txt with answers to the following questions:
1.   When you call lab3p1 with too few arguments, like this:

bash$ ~wcbrown/bin/httpcat www.engr.udayton.edu/faculty/jkissock/gsod/MSJACKSO.txt | lab3p1

You not only see your error message, you also see some error messages
from the httpcat script.  How could you change the command line so
that those error messages (httpcat's, that is) did not appear?







2.   In the Part 1 solution lab3p1, you print out the "average daily
temperature".  When you read it in, there was only one digit to the
right of the decimal place.  How would you call fprintf to print it
out with just one digit to the right of the decimal place?








3.   There is some missing data for most cities in Part 1.  Missing
data has temperature of -99.  Suppose we decided to print out a 
warning message for each date with missing temperature data.  Should
that be printed to stdout or stderr?  Give a convincing argument to
back up your answer.







4.   What is the type of argv[1]?






5.   Do a "man" on strdup.

     a.  Explain what strdup does?





     b.  How does strdup indicate that it has failed?





     c.  If you make the call foo = strdup(bar), what type
         does foo have?





     d.  If you make the call foo = strdup(bar), explain why
         you eventually should call free(foo)?




	      
Submit lab03 with the submit script. It is due, 8 February, immediately prior to your lab period.


Dave Stahl
Last modified: Mon Jan 9 11:08:19 EST 2008