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.
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); } |
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.
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:
Within emacs type Ctrl-c c (or use the compiling menu option in emacs) to compile, without exiting the editor.
At the UNIX prompt, type g++ -Wall factorial.cc -o factorial (Don't kill the editor program -- you'll probably want it again later.)
g++ is the GNU C++ Compiler, -Wall tells it to show all warnings, and -o says the next argument is the file name for the resulting program. Without the -o factorial, the resulting program will be given the filename a.out.
This can be less convenient, but you have more control with the compiler options.
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:
Create/edit your program in an editor, e.g., emacs.
Compile your program. If there are any errors, go back to editing your program.
Run your program. If there are any errors, go back to editing your program.
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; } |
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.
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; } |
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; } |