Edits / Updates:
  • Return values are being specified for error conditions for all functions. Make sure you use the specified return value as well as print out the error message.
Due 5:00pm Wednesday.

Goals for this homework

  • Practice the C development cycle
  • Write a small C program to get used to the syntax.
  • Write programs that exercise functions and control.

You are expected to complete this assignment individually. If you need help, you are invited to come to office hours and/or ask questions on piazza. Clarification questions about the assignments may be asked publicly. Once you have specific bugs related to your code, make the posts private.

Another note about Piazza. Piazza is your community resource - please use those as discussions amongst yourselves. We are not monitoring it all day, rather we each have check-in times once a day. Therefore, you need to start early enough to wait for feedback and/or build a vibrant, supportive community that helps each other while we are completing other necessary tasks (research, developing assignments, preparing for lecture, performing advising tasks, etc.).

Make sure you filled out the Student Information Form

This homework has several exercises. We are also providing you with some resources on printf and error handling for this assignment.

Homework 1, which includes warmup 1, will be collected from your subversion repository at 5:00pm on Wednesday, just prior to your next lab session.

You should submit four files for this assignment (hw1.h, hw1.c, hw1_main.c, and Makefile), in addition to ones from your warm-up (functions.c, main.c, functions.h, errors.txt), in your subversion repository as directed below. Your subversion repository is named CNET-cs152-win-18 and you can check it out with the following command:

  $ svn checkout https://phoenixforge.cs.uchicago.edu/svn/CNET-cs152-win-18 
(where CNET is your cnet). Do not commit any executable, a.out or any other, to your repository; we will (in fact, must) recompile your code on our own machines on our end.

printf

The printf() function takes a string as its first argument. The contents of this string are printed to the screen as-is, but with the same kinds of escape conventions as we saw in Racket (e.g., \n is the newline character). The function can be used with a string and no other parameters:

  printf("Hello, world!\n");

If you want to include a representation of the value of one or more expression in the string printed by printf, you need to include one or more format codes in printf's string argument, and you need to pass correspondingly many expressions as additional arguments to printf, separated by commas. Some examples follow.

Format codes begin with the character % and the next character(s) specify the type of the value to be printed. For now, it suffices to know that d specifies an int, and lf specifies a double. To be clear, put together, we write %d as a placeholder when we want to write an int, %ld for a long int, and %lf for a double.

For example,

  printf("The number is %d.\n", 42);
prints this:
  The number is 42.

If we want more than one expression included in the same string, we use multiple format codes and pass arguments in lockstep with those codes:

  int ndigits = 6;
  double approx_pi = 3.14159;
  printf("Pi to %d digits is %lf, and pi squared is %lf\n",
         ndigits, approx_pi, approx_pi * approx_pi);

Error Handling

Error handling capabilities vary by language, and what you want to do in an error varies by the situation. When an error occurs in a function, the question becomes, what should you do, and how do you notify the caller that an error occurred?

In C, there is no good way to distinguish error conditions from normal execution. This is unfortunate because if you write a function, and it's used in a variety of different circumstances, it is bad programming practice to determine within the function what will be done. For example, one program might want to exit, whereas another might want to notify the user that there was bad input and to try again.

In this course, we will print the error message in a special way (see below). If there is an opportunity, we will designate a specific return value for an error condition. If there is no available return value, then we will exit from within the function.

When we print error messages, it is helpful and appropriate for them to be routed through a special error mechanism. This gives them priority, increases their likelihood of being printed in the case of a program crash, and allows them to be separated from other output. To accomplish this, we use a variant of printf called fprintf. This function works the same as printf, but takes in an extra argument at the beginning: the "file" to which to write the message. Passing in stderr as that argument will send the output to the screen, but using the special error mechanism:

  fprintf(stderr, "error: too many widgets for the number of grommets\n");
  fprintf(stderr, "error: need ten boondoggles, but only have %d\n", num_bds);

Sometimes, these lines will be followed by exit(1); This immediately exits the program and returns a code. If you were writing a large program, you might assign a different code to each type of error that would result in an exit.

Types

The homework exercises follow. We use the type int for integers (except, in certain cases made explicit below, unsigned int), double for floating-point numbers (i.e., non-integers), and int for Booleans (noting that C has no Boolean type).

Makefile

Because you are adding a second set of files to your directory, you need to add a second target to your Makefile. In your makefile, add another two lines (with a space between these and the ones already there).
hw1: hw1.h hw1.c hw1_main.c
	clang -Wall -o hw1 hw1.c hw1_main.c
To compile, type:
$ make hw1 

Set Up - Skeleton project

The first thing to do is to create a skeleton project that will minimally execute. This week, I provided it for you. In the future, you will be expected to create it for yourself. These are the steps you will take during future pre-labs to set up your project:
  • Step 1: hw1.h. Add prototypes for all functions in this assignment. For example:
    double surface_area_cylinder(double height, double radius);
    
  • Step 2: Create hw1.c. For each function, print to stderr the fact that it is not yet implemented and return with the right type. For example:
    double surface_area_cylinder(double height, double radius)
    {
    	fprintf(stderr,"surface_area_cylinder not yet implemented\n");
    	return 0.0;
    }
    
  • Step 3: Create hw1_main.c. For each function, put in a single function call. For example:
    int main()
    {
    	double dval;
    
    	dval = surface_area_cylinder(2,1.5);
    }
    

First get this compiling and running. It won't print out anything, but this will mean that your code will compile and execute with our infrastructure. This must work in order to get any points in this course. Do this first, not last.

Exercise 1: Geometric Shapes

This exercise pertains to geometric shapes. You are going to write two functions, in increasing difficulty (from a programming perspective). You may use the math library to calculate square root. To use the math library, you must both add #include <math.h> to your program and place -lm on the compile line (with clang).

double surface_area_pyramid(double edge_length); 

Write a function that calculates the surface area of a pyramid. Each side is length edge_length. Once you have written the calculation in code, make sure you also identify what values of edge_length would not be valid.

double sides_to_surface_area(int num_sides, double side_length);

Write a function that calculates the surface area of a 3-d shape with all equal-length sides and faces. Support pyramid, cube, and regular dodecahedron.

For both functions, if the user enters an invalid input, print out an error with the format "error (function_name): [description of error]". Remember, as described above, to use fprintf rather than printf and send the output to stderr. Return -1.0 to indicate that an invalid input was received. Do not exit.

Don't forget to complete this portion, test it, and commit it before moving on! This means you need to write a test function and develop a suite of test cases!

Exercise 2: Exponentiation

In this exercise, you will implement an exponent function two different ways. First, you will implement the naive implementation - to raise the number a to the nth power, simply perform n multiplications. Next, you will use a method that reduces the number of multiplications you need to perform. That technique is described below.

For both of these functions, you must solve them recursively. Do not use a loop. You may not use the math library.

Linear Time Exponentiation

Write a function expt to compute through simple multiplications of a. Its header should be

long int expt(int a, unsigned int n)

Fast Exponentiation by Successive Squaring

The technique of successive squaring provides a more efficient alternative. The idea is to square as often as possible. To illustrate this idea, let's consider how you could computer a^4. The linear implementation entails three multiplications:

        b = a*a (a^2)
        c = b*a (a^3)
        d = c*a (a^4)
        

With successive squaring, two multiplications suffice:

        b = a*a (a^2)
        c = b*b (a^2)
        

To raise a number to the eighth power, with successive squaring, three multiplications suffice:

        b=a*a (a^2)
        c=b*b (a^4)
        d=c*c (a^8)
        

Let's consider a power that is not one of the powers of 2. To compute a^9, four multiplications suffice, as follows:

        b=a*a (a^2)
        c=b*b (a^4)
        d=c*c (a^8)
        e=d*a (a^9)
        

In general, for integer a and natural number n, we compute a^n with successive squaring according to the following recursive algorithm:

        if n is 0, a^n = 1
        if n>0 and n is even, a^n = (a^(n/2))^2
        if n>0 and n is odd, a^n = a*(a^(n-1))
        

Implement exponentiation by successive squaring as a function with the following header:

long int ss(int a, unsigned int n)

Note that == is the testing operator for integer equality. % is the "mod" operator and gives the remainder when the first operand is divided by the second. For example, 5%2 is 1 and 6%2 is 0.

Also, take care that recursive calls within ss are calls to ss itself, not to any other function.

Submit

At this point, you should have done the following:
  • Completed the warmup and submitted functions.c, functions.h, main.c, errors.txt, and Makefile.
  • Created five files and filled in the proper information: hw1.h, hw1.c, hw1_main.c inside your hw1 directory.
  • $ svn add hw1.h hw1.c hw1_main.c 
    
  • Added the hw1 compile lines to Makefile.
  • Implemented all of your functions.
  • Implemented your test cases
  • Compiled your executable manually and with the Makefile
  • Executed your code
  • Debugged your code
  • Submitted your code:
    $ svn commit -m "hw1 complete"