<-- back to index...

Keunwoo Lee : CSE 142 : 18 November 1999

Notes on software engineering

PROBLEMS

Not enough comments.
More points were lost for this reason than any other. Do not code first and comment later, because you won't. Comment each declaration, function, and parameter as you write it.

 
No testing.
When I started testing, it was immediately obvious that many students had not even bothered to run their programs. Or, if they had, then they certainly had not tested more than one or two cases.
    If I run your program and it vomits on me, I am much less inclined to look at it favorably. Test your program through a variety of cases-- not only when you are finished, but continuously as you are writing pieces of code.

STRATEGIES

  1. If you're typing something twice, make it into one function and call it twice. If you're using copy-and-paste, it's often a sign that you're doing something wrong.

  2.  
  3. Spiral development: build outwards from a "core" of working code. Recall the NAW server example we've been doing in class. By the way, code that compiles is not "working" code.

  4.  
  5. Read the sample solutions for each assignment. Sample code demonstrates exemplary style and clarity. Reading other people's code makes you a better programmer.

Arrays in 1 and 2 dimensions

The overarching theme of the following: an array element can be used all the places a variable of its type could be used.

Declaring arrays

As a variable:
int a[5] = {0, 0, 0, 0, 0};

As a parameter:
void foo(int a[5]); OR
void bar(int a[]);

Using arrays

Assigning to an array element:
a[0] = 5;

Assigning to an entire array:
CAN'T BE DONE.

Getting an array element's value:
x = a[0]

Getting an array's value:
CAN'T BE DONE. (What would it mean, anyway?)

Passing an element as a parameter:
baz(a[0]); (just like passing a variable of the element type)

Passing an entire array as a parameter:
bif(a);

Note that arrays are (annoyingly) not like other variables when you pass them as parameters. The array is not copied; rather, you gain access to the original array elements directly. Therefore, what does the following do?

    void bof(int array_param[], int length) {
        int index;
        for (index = 0; index < length; index++)
            array_param[index] = index;
    }
    int main(void) {
        int main_array[5] = {0, 0, 0, 0, 0};
        bof(main_array, 5);
    }
    

2-dimensional arrays

    #include <stdio.h>
    #define ROWS = 2;
    #define COLS = 3;

    int main(void) {
        int arr[ROWS][COLS] = { { 0, 1, 2 },
                                { 3, 4, 5 } };
        int i, j;
        for (i = 0; i < ROWS; i++) {
            for (j = 0; j < COLS; j++)
                printf("%d ", arr[i][j]);
            printf("\n");
        }
        return 0;
    }
    
prints the following:
    0 1 2
    3 4 5
    

Array exercises

1. Write code that declares a 2-dimensional array of height and width 5, and initializes all the elements to zero.

There were two ways to do this; one by sticking to the strict definition of initialization, which means doing it in the declaration:

    int my_array[5][5] = { {0, 0, 0, 0, 0},
                           {0, 0, 0, 0, 0},
                           {0, 0, 0, 0, 0},
                           {0, 0, 0, 0, 0},
                           {0, 0, 0, 0, 0} };
    

The other way was to set all the values to 0 in a nested loop:

    int my_array[5][5];
    int row, col;
    for (row = 0; row < 5; row++)
        for (col = 0; col < 5; col++)
            my_array[row][col] = 0;
    

2. Write a function that, given an array, a start row, an end row, and an array width, sums all numbers between those two rows (inclusive).

    int sum_rows(int a[][], int startrow, int endrow, int width) {
        int sum = 0;
        int row, col;
        for (row = startrow; row <= endrow; row++)
            for (col = 0; col < width; col++)
                sum += a[row][col];
        return sum;
    }
    

Minor note: I changed the return type of this and the next exercise from the versions on the handout, because it doesn't make much sense to compute the sum and then do nothing with it.

3. Write a function that, given an array and a rectangle (upper-left coordinates and lower-right coordinates), sums all numbers in that rectangle.

    int sum_rectangle(int a[][], 
                      int top, int left,
                      int bottom, int right) {
        int sum = 0;
        int row, col;
        for (row = top; row <= bottom; row++)
            for (col = left; col <= right; col++)
                sum += a[row][col];
    }
    

To do at home: Write a function that, given an array and a rectangle, copies the values in that rectangle to another rectangular region in the array. The header should look like this:

    void copy_rect(int a[][], 
                   int top, int left, int bottom, int right,
                   int change_x, int change_y);
    

So, for example, given an array of integers on the left, copy_rect(array, 1, 1, 2, 3, 1, 2) will make it look like the one on the right:

    1 1 1 1 1 1                    1 1 1 1 1 1
    1 0 0 0 1 1                    1 0 0 0 1 1
    1 0 0 0 1 1       ====>        1 0 0 0 1 1
    1 1 1 1 1 1                    1 1 0 0 0 1
    1 1 1 1 1 1                    1 1 0 0 0 1
    

There are a few different ways to do this. I'm going to introduce a little cleverness, rather than doing it the most obvious way, because I want you to think about how the code works. Another note: I'm writing this late at night and the copy_rect operation is notoriously tricky. If you test my code and find that it doesn't work, I apologize. (Hint: I am suggesting you test the code to make sure it works, in the hope that it will help you understand 2-d arrays better..)

    void copy_rect(int a[][], 
                   int top, int left, int bottom, int right,
                   int change_x, int change_y) {

        /* First of all, notice that we're going to have to change the
           starting corner depending on the values of change_x and
           change_y.  This is because we don't want to overwrite values
           before they're copied in the case of overlapping start and end
           regions.  These next 6 variables allow you to keep track of
           that. */
        int x_start, x_end, x_step;
        int y_start, y_end, y_step;

        int row, col; /* Loop indexes */

        /* Set vertical traversal method */
        if (change_y > 0) {
            /* If we're moving down, we need to start at the
               bottom row. */
            y_start = bottom;
            y_end   = top;
            y_step  = -1;
        } else {
            /* Otherwise, start at top. */
            y_start = top;
            y_end   = bottom;
            y_step  = 1;
        }

        /* Set horizontal traversal method */
        if (change_x > 0) {
            /* If we're moving right, we need to start copying at the right. */
            x_start = right;
            x_end   = left;
            x_step  = -1;
        } else {
            /* Otherwise, start at left */
            x_start = left;
            x_end   = right;
            x_step  = 1;
        }

        /* Do traversal */
        for (row = y_start; row <= y_end; row += y_step) {
            for (col = x_start; col <= x_end; col += x_step) {

                /* Can you explain in a sentence what the following line
                   does?  Can you draw a picture of it?  If not, you need
                   to study more about 2-d arrays. */
                a[row+change_y][col+change_x] = a[row][col];

            }
        }

    }
    

Structured data : struct

An array gives us a collection of uniform data, accessed by number. A struct, by contrast, gives us a collection of possibly nonuniform data, accessed by name:

    /* Define structured type */
    typedef struct {
        int id;
        double gpa;
    } Student;

    /* Declare variables of that type. */
    Student alice, bob;

    /* Use the structure "members" or "fields" anywhere you use
       variables of the appropriate type. */
    alice.id  = 9523672898;
    alice.gpa = 4.0;
    printf("%d", alice.id);
    scanf("%lf", &(bob.gpa)); /* Parenthesis not necessary, but aids clarity */
    double avg_gpa = (alice.gpa + bob.gpa) / 2.0;
    

Structures and pointers

    void initialize(Student * my_student) {
        my_student->id  = 0;     /* same as (*my_student).id = 0; */
        my_student->gpa = 4.0;
    }

    int main(void) {
        initialize( &my_student );
    }
    

Keunwoo Lee
Last modified: Wed Dec 1 15:18:49 PST 1999