Comp 210 Lab 11: From Scheme to C

For an introduction to the C language, be sure to see the Extended C Primer (html or ps), available through the "Readings" link of the main class web page.

  • A Sample Program

  • The Process of Creating a C Program

  • Practice Make Perfect

  • Throughout the lab, be sure to ask any ``what if/why'' questions which occur to you about C syntax.

    A Sample Program

    Consider the following Scheme code:
    ;; (f n)
    ;; given an integer n,
    ;; return 3n2 + 4n + 2
    ;;
    (define f
      (lambda (n)
         (+ (* 3 n n)
            (* 4 n)
            2)))
    
    (f 7)	
    
    Here's the
    corresponding program written in C:
    #include <iostream.h>         // Include the input/output library header,
                                  //   to be able to use "cin", "cout"
    
    /* f( n )
     * return 3n2 + 4n + 2
     */
    int f( int n ) {             // A function which takes int n, returns an int.
      int result = 3*n*n + 4*n + 2;
      return result;             // You have to "return" explicitly.
      }
    
    
    int main() {                 // Every program needs one function named "main"
      cout << "f at 7 is " << f(7) << "." << endl;
                                 // Nothing prints by iteslf; endl is a newline.
    
      return 0;                  /* Return a value...to where?  Who called main?
                                    Always return 0 from main, to UNIX. */
      }
    
    Copy this file (via UNIX) from /home/comp210/Labs/lab11/prog.cc to your own directory.

    You've seen most of these constructs in lecture already, but ask about anything you don't recognize. A few things to note:

    The Process of Creating a C Program

    Compile and run the above program. The procedure is repeated here. Unlike Scheme, where program development is fairly interactive, writing C programs is a little more rigid. There are several steps:
    1. Type your program into a text file. Important: The filename should end with .cc, for example silly.cc. You can use whatever text editor you like to create this file; we'll use emacs since it has some nice features for using C.

      Select emacs from your window manager menu, and then inside emacs be sure to select ``Open File'' and open a new file silly.cc (alternately, type emacs silly.cc & from a UNIX shell).

      Recall that you can run an emacs tutorial from the "Help" menu. For further commands, see the emacs reference card, or review some basic emacs commands from a previous lab. Also, check out the new menu labelled ``C++'' with some handy functions.

    2. Compile your file, that is translate it from a text file to a binary format the machine can understand directly.

      Translating your program to machine code would be tedious by hand, but fortunately there is a program which will do it automatically.

         owl%  g++ silly.cc
      
      If you are using emacs, you can compile the file with M-c (meta-c), or select "compile" under the C menu. You'll see that emacs invokes g++ for you, but with some useful extra arguments, which your lab instructor will go over.

      If emacs doesn't invoke g++ when you type M-c, or there isn't a C++ menu, make sure that you are editing a file whose name ends in .cc. If that still doesn't fix the problem, type "M-x c++-mode" and that should set everything aright.

    3. If your program had any syntax errors (i.e., it wasn't legal C), then g++ will inform you. You must go back to Step 1, and fix your source file.

    4. If you didn't have any syntax errors, g++ will create a new executable file, named silly if you compiled with those extra arguments, or a.out (booo) if you didn't. To run your program, realize it's the same as any other UNIX program: just type in the name of the executable file.

      Test your program on different inputs. If you have any mistakes...you guessed it: back to Step 1.

    5. Celebrate!
    Just for your perusal, here is another example, income.cc, which declares a function of two arguments.
    #include <iostream.h>
    
    bool biggern( int a, int b ) {      /* "bool" is C's Boolean type */
      return (a > b);
      }
    
    int main() {
      int income = 7;
      int outgo  = 9;
    
      if (biggern( income, outgo ))
        cout << "Woo-hoo!  Made a profit!" << endl;
      else {
        cout << "Uh-oh, in the red." << endl;
        cout << "Must raise $" << outgo-income << "quickly!" << endl;
        }
        
      return 0;
      }
    
    The curly-braces around the else block are required. what would happen if they were omitted? Adding curly-braces around the single-line of the if consequence wouldn't hurt, and I would recommend adding them in to avoid the subtle missing-brace bug.

    Practice Makes Perfect

    Exercise: Write a function which converts Fahrenheit to Celsius, and a main function which prints what 212o Fahrenheit is. (What about -40o F?) Your function must subtract 32, then multiply by 5.0/9.0.

    Remember that in C, 5.0/9.0 is different thatn 5/9. Dividing int 5 by int 9 insists that the answer must be an int, regardless of what is done with the result (such as assigning it to a double).

    Group Exercise (thanks to Dan): Go back and change one single character in your program (at random, or cunningly). Re-compile, and see what error message you get. Is it similar to the error message your neighbor got?

    Warning: Programs are intended to be read by humans as well as machines. Emacs does some indenting for you, but also notice how spaces and blank lines are used judiciously (in the original sense of the word) in the above sample programs. Your exact C coding-style is something you'll settle into, but some issues are non-negotiable.

    Otherwise the TA and labbies will hiss at you, and (if that doesn't work) on homeworks points may be deducted. (Poor formatting can lead to obfuscated code.)

    Exercise: Write the function factorial in Scheme. Transliterate this to C, compile and run. (You are not allowed to forget what we've taught you using Scheme, about having recursive programs which match the recursive nature of the data definition (natural numbers, in this case)).

    Write a main function which prints 0 through 30 factorial (remember C's while loop from lecture; write the corresponding Scheme while-fn first).

    Hey, this starts giving weird answers starting around 14! There are even some negative results?! What gives? (Hint: Remember that ints can overflow, if they only have a fixed number of digits.) How can you fix this problem? How good a fix is it?

    One common error to beware of: Just as in Scheme you defined a variable only once but may set! it many times, in C you want to only specify a variable's type once even if you assign to it repeatedly. For example:

    int r = 3;
    r = r + 1;    // right
    
    int w = 0;
    int w = w + 1; // wrong: this declares a second variable w
    
    Usually g++ will catch this error, but not if the re-declaration is the first line of a for-loop!

    Optional Exercise: Write the function Stirling, which is similar to factorial except that it doesn't use recursion but instead uses Stirling's approximation for factorial:

    n = (n/e) n sqrt (2 pi n) + err (n)
    where err (n) ≤ (1/12n) n!. If you #include <math.h>, you will get the declarations of the functions sqrt(n) and pow(x,y), which each take and return doubles. How good is the error estimate of 1/12 of the whole answer?
    Back to Comp 210 Home