Comp210 Lab 13: More C

Overview, Arrays, Errors, Debugging


Basics

First, let's have a quick overview of some C basics which you've seen. Here's a chart of rough equivalences:

Scheme C
(define x 3) int x = 3;
(set! x 3) x = 3;
(if (= x y) #f #t) if (x == y) {false;} else {true;}
(define foo (lambda (x) (+ x x))) int foo(int x) {return x+x;}
(set! x (read)) cin >> x;
(printf "~s~ x) cout << x;

What does the following program do?

#include <iostream.h>

int main() {
    int i;
    int n;
    int x = 0;

    cin >> n;
    for (i=0; i<n; i=i+1) {
        x = x + i*n;
    }
    cout << x;

    return 0;   /* 0 indicates no error, to UNIX */
}

Write a program that reads two integers as input and prints the sum of all the numbers between (inclusive) them. First try this assuming the first input is less than or equal to the second input. Next, allow the second input to be less than the first.


Arrays

Here's some more Scheme/C rough equivalences:

Scheme C
(define v (vector 1 2 3 4)) int v[4] = {1,2,3,4};
(vector-ref v 2) v[2];
(vector-set! v 2 5) v[2]=5;

The following code fragment illustrates how to pass an array to a C function. Note that the main trouble is that the called function doesn't have an automatic way to know how big the array is. I.e., there is no direct equivalent of Scheme's vector-length. One solution is to also pass the size the function.

bool func(int v[],int vsize) {
    ...
}

int main() {
    ...
    int v[10];
    ...
    func(v,10);
    ...
}

Write a function that takes an array of integers and an integer indicating the array's size. Return a boolean indicating whether the array is sorted, i.e., if each element is bigger than the previous one.


Errors

As in DrScheme, you can encounter errors at two times:

At least in principle, C/C++ can find more mistakes than Scheme during compile-time, as it looks for type-errors. However, C/C++ run-time errors are generally very uninformative. During this section of lab, we will give you a quick introduction to diagnosing and fixing some of your errors.

Compile-time errors

We already saw some compile-time errors in the first C lab, but here are some more examples.

Copy the program /home/comp210/public_html/Labs/lab13/error1.cc into your directory and compile it. It complains

error1.cc: In function `float c2f(float)':
error1.cc:6: parse error before `}'
A number just after the file name indicates the line number in the file where this error occurs. I.e., in the function c2f, there is a syntax error just before the closing brace on line 6. Look at the file -- can you fix the problem?

Any time it complains about a "parse error", that means you've left something out that is necessary (like a comma or semicolon or keyword), put something extra in, or mistyped something (like misspelling a keyword).

Try more examples.

Run-time errors

All of the previous errors have been found during compilation. Give what we have seen of C/C++ so far, it's pretty hard to have a run-time error, assuming you follow your program templates. (This ignores the possibility that your program may compute something correctly, just not the right thing.)

Probably the easiest way to get a run-time error so far is to misuse arrays. The most important thing is to always index an array with a value in the range from zero to its declared size minus one. Otherwise, C will quietly allow a bad indexing, and strange things can happen. Look at, compile, and run /home/comp210/public_html/Labs/lab13/badarray.cc.

For the curious

Let's look at some possible run-time errors not using arrays. Look at and compile /home/comp210/public_html/Labs/lab13/sum.cc. This will sum the squares of all integers in an interval (like you should have written earlier in this lab). Run the compiled program:

Look at and compile /home/comp210/public_html/Labs/lab13/ack.cc. This is a funky function called Ackerman's function. The result of this can easily be a really big number, so we use floats instead of ints. Run the compiled program:


Debugging

Debugging a C program can be more difficult that debugging a Scheme program because we don't have the equivalent of Donkey to step through our programs. In this part of the lab, we will find and fix some program errors. These will be examples of some of the common C programming mistakes. We also demonstrate a common technique for debugging.

What does this program in /home/comp210/public_html/Labs/lab13/debug1.cc do? Can you fix it so it does the intended calculation? Edit, compile, and run it until it does what you expect.

For small programs like this, it's often not too difficult to step through the evaluation either in your head or on paper. But errors can be hard to find, especially when you "know" that your program is "obviously" right. One technique for effectively stepping through the interesting bits of the computation is to print some interesting values while your program is running. We saw this before for Scheme also.

Copy, compile, and run /home/comp210/public_html/Labs/lab13/debug1print.cc, which is like the previous program, except that we print out some values along the way. Observe that, for readability, we label what we print, so that we don't confuse ourselves with all of this output.

Did you find the uninitialized accumulator and the incorrectly ordered side-effects?

Good places to print out values include

However, you can easily get carried away. In larger programs, it's generally a good idea to start with the beginning and end of functions, to first track down which function your problem is in. Then pay more attention to the body of that particular function. I.e., break down the problem of debugging into manageable chunks, just like you do for programming.

There are ways to extend this technique, e.g., having multiple "levels" of debugging which print out different amounts of information. Also, there really are some tools that help you step through C programs (e.g., gdb, adb). With practice, these can be very helpful, but they aren't as easy to use as Donkey.

Try more examples.