Multi-Dimensional Arrays

Tom Kelliher, CS18

Mar. 27, 1996

We will skip over some of this material.

Two-Dimensional Arrays

An array of 1-D arrays.

Declaration, Initialization

int r;   // row index
int c;   // column index
double x;
double data[2][3] = { { 0.0, 1.0, 2.0 },
                      { 3.0, 4.0, 5.0 }
                    };

// Example.  Same effect as initializer.
for (x = 0.0, r = 0; r < 2; ++r)
   for (c = 0; c < 3; ++c)
      data[r][c] = x++;

Parameter Passing

Recall that arrays are passed by pointer.

Consider for 1-D arrays:

void f(int array[], int len);

void g(void)
{
   int a[10];
   int b[30];

   f(a, 10);
   f(b, 30);
}
What is needed by f() to access array[i] in memory?

array[i] vs. *(array + i)

Consider for 2-D arrays:

const int COLUMNS = 20;

void f(int array[][COLUMNS], int rowsInUse, int columnsInUse);

void g(void)
{
   int a[10][COLUMNS];
   int b[30][COLUMNS];
   int c[10][10];

   f(a, 10, COLUMNS);
   f(b, 30, 10);
   f(c, 10, 10);   // Compile-time error.
}

void f(int array[][COLUMNS], int rowsInUse, int columnsInUse)
{
   int i;
   int j;

   for (i = 0; i < rowsInUse; ++i)
      for j = 0; j < columnsInUse; ++j)
         array[i][j] = 0;
}
Now, what does f need to access array[i][j] in memory?

array[i][j] vs. (*(array + i))[j] vs. *(*(array + i) + j)

Assume that array is allocated starting at address 5000 and an int occupies two bytes. What are the values of the following expressions?

int array[2][3] = { { 0, 1, 2},
                    { 3, 4, 5}
                  };

array;

*array;

**array;

array + 1;

*array + 1;

**array + 1;

*(array + 1);

*(*array + 1);

**(array + 1);

Programming Problem: Design a function to determine if a square matrix of reals is symmetric. Assume that the maximum number of columns is 30.

A matrix, M, is symmetric if M[i][j] equals M[j][i] for all valid values of i and j.

Design.

Solution:

const int COLUMNS = 30;
const int EPSILON = 1E-10;
int symmetric(double m[][COLUMNS], int side)
{
   int i;   // Column index.
   int j;   // Row index.

   // fabs() is in math.h
   for (i = 0; i < side; ++i)
      for (j = 0; j < i; ++j)
         if (fabs(m[j][i] - m[i][j]) > EPSILON)
            return 0;

   return 1;
}

Example.

Arrays of Structures/Classes

#include <iostream.h>

class complex
{
   double re;
   double im;

 public:
   inline complex(void) {}
   inline complex(double r) : re(r), im(0.0) {}
   inline complex(double r, double i) : re(r), im(i) {}
   void display(void);
   // ...
};

void complex::display(void)
{
   cout << "(" << re << ", " << im << ")\n";
}

int main()
{

   complex M[2][2] = { { complex(1.0), complex(2.0, 2.3) },
                       { complex(-2.3), complex(-100.0, 300.75)}
                     };

   M[0][0] = complex(1.0, 2.0);

   M[0][0].display();
   M[0][1].display();

   return 0;
}

Interrupt Handlers

What is an interrupt?

Some interrupts (most from the Unix world):

Default action (handler).

signal()

#include <signal.h>

void* signal(int signal, void (*func)(int));

Install your own signal handler for a particular interrupt (signal).

The function returns the pointer SIG_ERR if unable to install the new handler.

signal is the name of the signal to ``catch.''

func is the address (name) of the new interrupt handler. The new handler must have these properties (as indicated by its declaration):

  1. A return type of void.
  2. A single int parameter.

An Example

/**********************************************************************
 * sigint.cc
 * Tom Kelliher
 *
 * This program demonstrates a simple use of a SIGINT (ctrl-c) interrupt
 * handler.  A SIGINT handler is installed and then a loop controlled
 * by a global variable is entered.  When the signal is received, the
 * handler is called.  The handler changes the loop control variable so
 * that the loop is exited.
 **********************************************************************/


#include <signal.h>
#include <iostream.h>
#include <stdlib.h>


int go = 1;   // The loop control variable.


// Prototypes.
void intHandler(int);


/**********************************************************************
 * main
 **********************************************************************/

int main()
{

   // Try to install the handler.
   if (signal(SIGINT, intHandler) == SIG_ERR)
   {
      cout << "Couldn't install signal handler.\n";
      exit(1);
   }

   // Sit in a loop until the signal occurs.
   while (go)
      cout << "Still going...\n";

   cout << "Exiting.\n";

   return 0;
}


/**********************************************************************
 * intHandler --- An interrupt handler.  It will reset the global
 * variable go, causing main to leave its "infinite" loop.
 **********************************************************************/

void intHandler(int sig)
{
   if (sig == SIGINT)
      cout << "Caught SIGINT.\n";
   else
      cout << "Caught some other signal.\n";

   go = 0;
}

Conway's Game of Life

Example input file:

2 2
3 2
4 2
You should use something more ambitious.

First, second generations.

How many arrays?

What about edges and corners?

The program should prompt the user for input and output file names.

You should use symbolic constants wherever possible:

cellStatus world1[20][20];          // Wrong.
cellStatus world2[ROWS][COLUMNS];   // Right.



Thomas P. Kelliher
Tue Mar 26 09:45:50 EST 1996
Tom Kelliher