Comp210 Lab 12: C intro

Recursive example, Mechanics of writing and running programs, Accumulator example with vectors, Loop examples

This lab will introduce you to the basics of C syntax through a series of Scheme and C examples. To ease this introduction, we use a Scheme-like style of C. Typical C programs do not follow this style. (Feel free to imagine a long diatribe about typical C style here.) Note that we aren't really using C, but ``Extended C'' (printable ps or browsable html) which is C with some of C++'s syntactic improvements, but not C++'s objects.


Recursive example

Here is a simple Scheme program following the natural number template, and a C equivalent.

; factorial : natnum -> natnum
;    returns the factorial of the argument
(define factorial
   (lambda (n)
      (cond [(zero? n) 1]
            [else      (* n (factorial (sub1 n)))])))
// factorial : unsigned int -> unsigned int
//    returns the factorial of the argument
unsigned int factorial(unsigned int n)
{
   if (n == 0)
      return 1;
   else
      return n * factorial(n-1);
}
A few things to notice:

Q: What is the natural number template in C?

To do: (if time allows) Write other C functions, for example computing the sum of 1..n.


Mechanics of writing and running programs

To run a Scheme program, you evaluate an expression that calls your main function, e.g.,

   (factorial 5)
In C, you must write a function called main that corresponds to this, e.g.,
   // main : void -> int
   //    computes and prints the factorial of a given number
   //
   //    usually takes some arguments, but we'll ignore that in COMP210
   //    returns an error code (to UNIX)
   int main()
   {
      unsigned int n;         // declare a local variable
                              //    here, not initialized

      cin >> n;               // = (set! n (read))
      cout << factorial(n);   // = (printf "~s" (factorial n))
      cout << "\n";           // = (printf "~n")

      return 0;               // error code 0 means no error
   }
Note that main often handles the input and output of your program. Unlike Scheme, nothing is printed by default.

There's one more thing you'll need for this C program to work. By default, the I/O primitives cin, cout, <<, and >> are not defined. To load their definitions, put at the top of your C file

   #include <iostream.h>   // load I/O definitions

To do: Using Emacs, type in this C program into a file called factorial.cc. You'll find it most convenient if you make sure the Emacs buffer is called factorial.cc before you start typing. This allows Emacs to notice that you're editing a C/C++ program, so that it can give you some syntactic DrScheme-like help. For example, it checks whether each closing paren matches an opening paren, and it helps with indentation.

To do: Before we can run this C program, we must compile it, i.e., translate it into a JAM-like code the computer can use. Do one of the following:

If you mistyped anything, go back and edit your program.

To do: Now run your program. At a UNIX prompt, type factorial. (This assumes you're in the directory where this file was created.) If there were any errors, go back and edit your program. If you do have any errors, ask the labbies on how to interpret the error messages. We will discuss some possible errors in next week's lab.

The basic process for writing a C program is as follows:

  1. Create/edit your program in an editor, e.g., emacs.

  2. Compile your program. If there are any errors, go back to editing your program.

  3. Run your program. If there are any errors, go back to editing your program.


Accumulator example with vectors

Here is another simple Scheme program following the natural number accumulator template, and a C equivalent.

;; sum : vector of number -> number
;;   Returns the sum of the numbers in the vector
;;
(define sum
   (lambda (vec)
      (local [; sum-helper : natnum number -> number
              ;    1st argument is position in vector
              ;    returns the sum of the numbers in the vector up to but
	      ;       not including the current position, plus the 2nd argument
              (define sum-helper
                  (lambda (posn accum)
                     (cond [(zero? posn) accum]
		           [else         (sum-helper
			                  (sub1 posn)
			                  (+ accum (vector-ref vec (sub1 posn))))])))]
            (sum-helper (vector-length vec) 0))))

(sum (vector 2 -4 8 1))      
#include <iostream.h>

// sum_helper : (array of int) (unsigned int) int -> int
//   2nd argument is position in array
//   Returns the sum of the numbers in the array up to but
//      not including the current position, plus the 3rd argument.
//
int sum_helper( int array[], unsigned int posn, int accum )
{
   if (posn == 0)
      return accum;
   else
      return sum_helper( array, posn-1, accum + array[posn-1] );
}

// sum : (array of int) (unsigned int) -> int
//   2nd argument is size of array
//   Returns the sum of the numbers in the array.
//
int sum( int array[], unsigned int arraySize )
{
   return sum_helper( array, arraySize, 0 );
}

int main()
{
   const unsigned int theArraySize = 4;
   int theArray[ theArraySize ] = {2, -4, 8, 1};

   cout << sum( theArray, theArraySize );
   cout << "\n";

   return 0;
}
A few things to notice:

To do: Compile and execute this C program. (Note: Don't name it sum.cc. There's already a UNIX program called sum.)

To do (if time allows): Write a similar C program, compile, and execute it.


Loop examples

Here is (most of) the equivalent programs using loops.

; sum : vector of number -> number
;   returns the sum of the numbers in the vector
(define sum
   (lambda (vec)
      (local [(define accum 0)]
             (begin
                (fori= 0
                       (vector-length vec)
                       (lambda (posn)
                           (set! accum (+ accum (vector-ref vec posn)))))
                accum))))
// sum : (array of int) (unsigned int) -> int
//   2nd argument is size of array
//   returns the sum of the numbers in the array
//
int sum(int array[], unsigned int arraySize)
{
   int accum = 0;
   for (unsigned int i = 0; i < arraySize; i = i+1)
       accum = accum+array[i];
   return accum;
}
Some things to notice:

To do: Finish the C program, compile, and execute it.

Here's one last example. Let's compute the average of the positive elements in a vector or array. We could write two functions: one for the sum of the positive elements and one for the number of positive elements. Here we'll write just a single function.

; avg-pos : vector of number -> number
;   returns the average of the position numbers in the vector
(define avg-pos
   (lambda (vec)
      (local [(define accum 0)
              (define count 0)]
             (begin
                (fori= 0
                       (vector-length vec)
                       (lambda (posn)
		           (if (positive? (vector-ref vec posn))
                               (begin
			           (set! accum (+ accum (vector-ref vec posn)))
				   (set! count (add1 count))))))
                (/ accum count)))))
// avg_pos : (array of int) (unsigned int) -> int
//   2nd argument is size of array.
//   Returns the sum of the numbers in the array.
//
int avg_pos( int array[], unsigned int arraySize )
{
   int accum = 0;
   int count = 0;
   for (unsigned int i = 0; i < arraySize; i = i+1)
       if (array[i] > 0) {
          accum = accum+array[i];
	  count = count+1;
       }
   return accum / count;
}
Some things to notice: