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); return 0; }The output of this program looks like this:
* *************** **** **** *** *** ** ** ** ** ** ** ** ** * * * * * * * * * * ** ** ** ** ** ** ** ** *** *** **** **** *************** *
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, stdin); /* 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.
Here is another C program that uses two-dimensional arrays. It plays "The Game Of Life":
/* * The Game of Life * * a cell is born, if it has exactly three neighbours * a cell dies of loneliness, if it has fewer than two neighbours * a cell dies of overcrowding, if it has more than three neighbours * a cell survives to the next generation, if it does not die of loneliness * or overcrowding * * In my version, a 2D array of ints is used. A 1 cell is on, a 0 cell is off. * The game plays 100 rounds, printing to the screen each time. 'x' printed * means on, space means 0. * */ #include <stdio.h> /* dimensions of the screen */ #define BOARD_WIDTH 79 #define BOARD_HEIGHT 24 /* set everthing to zero */ void initialize_board (int board[][BOARD_HEIGHT]) { int i, j; for (i=0; i<BOARD_WIDTH; i++) for (j=0; j<BOARD_HEIGHT; j++) board[i][j] = 0; } /* add to a width index, wrapping around like a cylinder */ int xadd (int i, int a) { i += a; while (i < 0) i += BOARD_WIDTH; while (i >= BOARD_WIDTH) i -= BOARD_WIDTH; return i; } /* add to a height index, wrapping around */ int yadd (int i, int a) { i += a; while (i < 0) i += BOARD_HEIGHT; while (i >= BOARD_HEIGHT) i -= BOARD_HEIGHT; return i; } /* return the number of on cells adjacent to the i,j cell */ int adjacent_to (int board[][BOARD_HEIGHT], int i, int j) { int k, l, count; count = 0; /* go around the cell */ for (k=-1; k<=1; k++) for (l=-1; l<=1; l++) /* only count if at least one of k,l isn't zero */ if (k || l) if (board[xadd(i,k)][yadd(j,l)]) count++; return count; } void play (int board[][BOARD_HEIGHT]) { /* (copied this from some web page, hence the English spellings...) 1.STASIS : If, for a given cell, the number of on neighbours is exactly two, the cell maintains its status quo into the next generation. If the cell is on, it stays on, if it is off, it stays off. 2.GROWTH : If the number of on neighbours is exactly three, the cell will be on in the next generation. This is regardless of the cell's current state. 3.DEATH : If the number of on neighbours is 0, 1, 4-8, the cell will be off in the next generation. */ int i, j, a, newboard[BOARD_WIDTH][BOARD_HEIGHT]; /* for each cell, apply the rules of Life */ for (i=0; i<BOARD_WIDTH; i++) for (j=0; j<BOARD_HEIGHT; j++) { a = adjacent_to (board, i, j); if (a == 2) newboard[i][j] = board[i][j]; if (a == 3) newboard[i][j] = 1; if (a < 2) newboard[i][j] = 0; if (a > 3) newboard[i][j] = 0; } /* copy the new board back into the old board */ for (i=0; i<BOARD_WIDTH; i++) for (j=0; j<BOARD_HEIGHT; j++) { board[i][j] = newboard[i][j]; } } /* print the life board */ void print (int board[][BOARD_HEIGHT]) { int i, j; /* for each row */ for (j=0; j<BOARD_HEIGHT; j++) { /* print each column position... */ for (i=0; i<BOARD_WIDTH; i++) { printf ("%c", board[i][j] ? 'x' : ' '); } /* followed by a carriage return */ printf ("\n"); } } /* read a file into the life board */ void read_file (int board[][BOARD_HEIGHT]) { int i, j; char s[100]; for (j=0; j<BOARD_HEIGHT; j++) { /* get a string */ fgets (s, 100, stdin); /* copy the string to the life board */ for (i=0; i<BOARD_WIDTH; i++) { board[i][j] = s[i] == 'x'; } } } /* main program */ int main (int argc, char *argv[]) { int board[BOARD_WIDTH][BOARD_HEIGHT], i, j; initialize_board (board); read_file (board); /* play game of life 100 times */ for (i=0; i<100; i++) { print (board); play (board); sleep (1); /* clear the screen using VT100 escape codes */ puts ("\033[H\033[J"); } return; }This "game" is played by the computer in a two-dimensional array and the results printed to the screen. Different initial input files lead to different games being played. Let's see the result of using this input file.