Other Topics

Two dimensional arrays

The arrays we have seen so far are one-dimensional. To uniqely identify any element of an array, we need only one piece of data: its index. This helps us model many kinds of things, for instance, a list of student ID numbers, sound (as we saw in program 6), and character strings. Some data are inherently two-dimensional and have a natural representation in the computer as a two-dimensional array, where we use two indices instead of one to refer to an array element. Some examples are:

In C, two-dimensional arrays are represented by arrays of arrays. For example, if we want to have a two-dimensional array of integers, we would declare them like this:

	int	v[10][20];
This object v is now an array of size 10 of arrays of size 20 of integers. It's easier to think of it as 10 rows, where each row has 20 integers. Indexing the array takes two integers. The following code initializes each element of the array to 0:
	int	i, j;

	for (i=0; i<10; i++) for (j=0; j<20; j++) v[i][j] = 0;
So, v[i] is a single row of the array, or a single one-dimensional array that's part of the 2D array. In terms of pointers, v[i] is the same as &v[i][0].

Let's look at a program that uses a 2D array of characters like a graphics frame buffer to draw and print a circle on the screen (a more robust version of this program is available in the C examples as circle.c .

#include <stdio.h>
#include <math.h>

/* height and width (minus 1 to be safe) of a VT320 terminal screen */

#define WIDTH  79
#define HEIGHT 24

/* clear the screen buffer by putting blanks in each array element */

void clear (char screen[][HEIGHT]) {
	int	i, j;

	for (i=0; i<WIDTH; i++) for (j=0; j<HEIGHT; j++) screen[i][j] = ' ';
}

/* output the screen buffer to the standard output */

void print (char screen[][HEIGHT]) {
	int	i, j;

	for (j=0; j<HEIGHT; j++) {
		for (i=0; i<WIDTH; i++) putchar (screen[i][j]);
		putchar ('\n');
	}
}

/* draw a circle by placing stars in the array using the formula
 * for a unit circle y = sqrt (1 - x*x) 
 */
void circle (char screen[][HEIGHT], int i, int j, int radius) {
	float	x, y;
	int	di, dj;

	/* x will go from 0 through 1 in steps of 0.01 */

	for (x=0.0; x<=1.0; x+=0.01) {

		/* y is the y coordinate of the point (x,y)
		 * on the unit circle
		 */
		y = sqrt (1.0 - x*x);

		/* multiply by the radius (factor of 1.7 makes it look more
		 * circular on some monitors
		 */
		di = (int) (x * radius * 1.7);
		dj = (int) (y * radius);

		/* put a star in each quadrant for this point,
		 * offset from the center of the circle
		 */

		screen[i + di][j + dj] = '*';
		screen[i + di][j - dj] = '*';
		screen[i - di][j + dj] = '*';
		screen[i - di][j - dj] = '*';
	}
}

int main () {
	char	screen[WIDTH][HEIGHT];

	/* clear (initialize) the screen buffer */

	clear (screen);

	/* put a circle of radius 10 at (x,y)=(40,12) */

	circle (screen, 40, 12, 10);

	/* print the screen buffer to standard output */

	print (screen);
}	
The output of this program looks like this:

                                                                               
                                                                               
                                        *                                      
                                 ***************                               
                              ****             ****                            
                            ***                   ***                          
                           **                       **                         
                          **                         **                        
                         **                           **                       
                        **                             **                      
                        *                               *                      
                        *                               *                      
                        *                               *                      
                        *                               *                      
                        *                               *                      
                        **                             **                      
                         **                           **                       
                          **                         **                        
                           **                       **                         
                            ***                   ***                          
                              ****             ****                            
                                 ***************                               
                                        *                                      
                                                                               

Arrays of Strings

We can use 2D arrays to act as arrays of strings, since a string is in an array of char and a 2D array of char is an array of arrays of char. We can then read in a bunch of strings from a file into the 2D array and work on them in memory to do editing, spell-checking, or whatever the program is supposed to do, e.g.:
int main () {
	char	stuff[100][80]; /* 100 strings of up to 79 characters */
	int	i;

	i = 0;
	while (!feof (stdin)) 
		fgets (s, 80, &stuff[i++]);
	...
}

This approach is limited. Since array dimensions have to be specified at compile time, we have to know two things in advance: what is the maximum number of strings we will allow in a file, and what is the maximum string length we will allow? If the text file is typical, it will have many short strings, some with length 1 or 2, and a few long strings exceeding 80 characters. But each string in our 2D array must allow for the maximum number of characters, wasting a lot of storage. Even with memory sizes the way they are today, this is a real issue because if a file has just one 1000 length string, an array of 1000 of these strings will consume an entire megabyte of storage.

Another approach is to use an array of pointers instead of a 2D array of characters. There is a function called malloc that returns a pointer to an array of as many bytes as you want; we can use this function to allocate dynamically only the amount of storage that we need:

int main () {
	char	*stuff[100],	/* 100 pointers to char */
		s[1000];	/* one string of length 1000 */
	int	i;

	i = 0;
	while (!feof (stdin)) {

		/* get a string */

		fgets (s, 1000, s);

		/* allocate storage for the length of the string
		 * plus one extra byte for the null
		 */
		stuff[i] = (char *) malloc (strlen (s) + 1);

		/* copy the string we read in to the array */

		strcpy (stuff[i++], s);
	}
	...
}
This approach can also be used with other data types to allocate, say, a matrix with variable-length rows.