Reading on Two-dimensional Arrays
Overview
This laboratory exercise provides practice with the declaration and use of 2-dimensional arrays.
Introduction
The previous class session introduced a struct to combine several different pieces of data within one logical unit.
- Each piece of data has its own name.
- Different pieces of data may have the same or different data types.
In contrast, [one-dimensional] arrays provide a mechanism to access a sequence of data using a single index, such as item[0], item[1], item[2], ....
- One name, the array identifier (e.g., item) is used to designate the entire collection of data.
- An index, 0, 1, 2, ..., allows access to individual pieces of data.
- All pieces of data within an array must have the same type (e.g., int, double or a struct).
Some Basics of two-dimensional arrays
Expanding upon the concept of an array, C supports the organization of data in a table by specifying a row and a column.
As with one-dimensional arrays, two-dimensional arrays can contain data of any type, although each entry in a table must have the same type. Also, the size of a two-dimensional array must be declared when it is first defined:
int table [5][10];
When using a two-dimensional array as a parameter, the declaration of a procedure header must know the number of columns in the table (so the computer knows when one row stops and the next starts). As with 1-dimensional arrays, a procedure header does not need to specify the number of rows in the table. Thus, if the above table were passed to a printArray procedure, the header of printArray might be given as follows:
void printArray (int arr[][10])
The following example illustrates how to work with 2-dimensional arrays that conceptually store data of the same type within a table.

Example: Daily Precipitation for Six Cities
Consider a table that presents the amount of precipitation for six cities over an eight day period: December 18-25, 2014.
Chicago | Denver | Des Moines | Phoenix | San Jose | Seattle | |
---|---|---|---|---|---|---|
Date | Illinois | Colorado | Iowa | Arizona | California | Washington |
Dec. 18 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.51 |
Dec. 19 | 0.00 | 0.00 | 0.00 | 0.00 | 0.19 | 0.12 |
Dec. 20 | 0.00 | 0.00 | 0.00 | 0.00 | 0.06 | 0.77 |
Dec. 21 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
Dec. 22 | 0.28 | 0.14 | 0.34 | 0.00 | 0.00 | 0.00 |
Dec. 23 | 0.13 | 0.00 | 0.34 | 0.00 | 0.02 | 0.81 |
Dec. 24 | 0.12 | 0.00 | 0.09 | 0.00 | 0.04 | 0.21 |
Dec. 25 | 0.00 | 0.21 | 0.00 | 0.00 | 0.00 | 0.00 |
Totals | 0.53 | 0.35 | 0.77 | 0.00 | 0.31 | 2.42 |
This type of table arises frequently:
- Each row has a label (on the left)
- Each column has a label (at the top)
- Each cell (except the labels) has the same data type (e.g., doubles) and is stored in a in the two-dimensional array.
- Strings for row or column labels may be stored in separate one-dimensional arrays.
Annotated Program
Program city-precipitation.c stores the precipitation data for this table in a two-dimensional array and prints the table.
/* Program to print and process precipitation in six cities, based on totals recorded during the eight days December 18-25, 2014. */ #include <stdio.h> /* function returns total rainfall for the city with the given number */ double computeCityPrecip (double rain [8][6], int cityNum); int main () {
Program Notes
-
Within this typical program header, the program identifies a function prototype for a function to return the rainfall total in a column of the table.
- In declaring the 2-dimensional array procedure, the function must know the number of [rows and] columns for the table. This will allow the program to locate specific table entries, when needed. (More about 2-dimensional array parameters shortly.)
-
The cityNum indicates which column should be considered in finding a column sum.
char * cities [6] = {"Chicago", "Denver", "Des Moines", "Phoenix", "San Jose", "Seattle"}; char * states [6] = {"Illinois", "Colorado", "Iowa", "Arizona", "California", "Washington"};
-
The arrays, cities and states, represent an array of strings.
- A string is identified as the base address for an array of characters, terminated by a null character.
- Since a string is designated by a base address (a char *), an array of strings is designated by an array of base addresses (e.g., char * cities [6])/
double precip [8][6] = { /* Dec. 18 */ { 0.00 , 0.00 , 0.00 , 0.00 , 0.00 , 0.51 }, /* Dec. 19 */ { 0.00 , 0.00 , 0.00 , 0.00 , 0.19 , 0.12 }, /* Dec. 20 */ { 0.00 , 0.00 , 0.00 , 0.00 , 0.06 , 0.77 }, /* Dec. 21 */ { 0.00 , 0.00 , 0.00 , 0.00 , 0.00 , 0.00 }, /* Dec. 22 */ { 0.28 , 0.14 , 0.34 , 0.00 , 0.00 , 0.00 }, /* Dec. 23 */ { 0.13 , 0.00 , 0.34 , 0.00 , 0.02 , 0.81 }, /* Dec. 24 */ { 0.12 , 0.00 , 0.09 , 0.00 , 0.04 , 0.21 }, /* Dec. 25 */ { 0.00 , 0.21 , 0.00 , 0.00 , 0.00 , 0.00 } }; /* source: http://www.accuweather.com/en/us/chicago-il/60608/weather-forecast/348308 and similar pages from accuweather, accessed December 27, 2014. */
The city-recitation array has 8 rows (for 8 dates), with each row having data for 6 cities. This array size is reflected in the declaration
double precip [8][6]
-
C considers the precip array as an array of rows, in which each row is an array of double numbers for each of the 6 cities. Thus, initialization of precip is a list { ... , ... , - - - , ... } of rows, in which each row is a list of numbers (e.g., { 0.13 , 0.00 , 0.34 , 0.00 , 0.02 , 0.81 }
int row, col; /* print titles */ printf ("Precipitation at six cities over eight days in December 2014\n"); printf (" "); for (col = 0; col < 6; col++) printf ("%-11s", cities[col]); printf ("\n"); printf (" Date "); for (col = 0; col < 6; col++) printf ("%-11s", states[col]); printf ("\n\n");
-
The title of the table consists of cities in the first line and states in the second line.
-
To place these labels in columns, the format string for printf uses %-11s format.
- Each column will be 11 characters wide.
- The minus sign (e.g., -11) indicates each string should be left justified in its 11-character field. (Without the minus sign, %11s would right-justify the cities in the 11-character-wide fields.)
/* print precipitation table */ for (row = 0; row < 8; row++) { printf ("Dec. %2d", row+18); for (col = 0; col < 6; col++) { printf (" %5.2lf ", precip[row][col]); } printf ("\n"); } printf ("\n");
-
Printing the body of the table requires a nested loop.
- The outer loop specifies the successive rows to be printed.
-
Within a row,
- The first printf identifies the row label (e.g., "Dec. 18").
- The inner loop iterates column-by-column through a row of the table.
- An element in the 2-dimensional array is designated by providing both a row and column index: precip[row][col].
/* print total precipitation for each city */ printf ("Totals "); for (col = 0; col < 6; col++) { printf (" %5.2lf ", computeCityPrecip (precip, col)); } printf ("\n\n"); return 0; }
/* function returns total rainfall for the city with the given number */ double computeCityPrecip (double rain [8][6], int cityNum) { int k; double sum = 0.0; for (k = 0; k < 8; k++) sum += rain [k][cityNum]; return sum; }
-
The procedure header indicates the number of rows and columns for the array.
-
The loop iterates over rows, moving down the column with the designated column number.
Altogether, two dimensional arrays are a wonderful way to store lots of data which would otherwise require many arrays!
2D Array Storage: Row-major Order
Now that we have some basic experience with two-dimensional arrays, we need to examine more closely how these arrays are stored in main memory. Suppose an array is declared:
table [row][col];
where row and col have been previously specified as integer values. A schematic of the array is shown to the right.
In main memory, the two-dimensional array table is stored row-by-row. First, row 0 is stored, then row 1, then row 2, etc. Such a storage configuration is called row-major order. Note that no extra memory is used to separate one row from another; the elements of one row immediately follow the elements of the previous row.

More about 2-dimensional Arrays in C
The storage of 2-dimensional arrays in row-major order reflects several underlying concepts within C, including
-
The view of a 2-dimensional array as an array of 1-dimensional arrays,
-
The details for calculating the location of an individual array element within a 2-dimensional array, and
-
Requirements for declaring a 2-dimensional array.
The following notes explore each of these topics.
C's View of a 2-dimensional Array as an Array of 1-dimensional Arrays
In the initial introduction of 1-dimensional arrays, an array declaration a[10] specified several elements.
-
array a represents a block of 10 elements.
-
The variable a represents the base address of that block of main member.
-
In accessing an element a[i], the identified item is found by starting at base address a and then moving forward over i array elements. That is, the relevant data for a[i] may be found at the address a + i*(size of one array item).
This same perspective carries over to two-dimensional arrays. For the array double table[8][6],
-
The variable table provides the base address for a block of data.
-
The 2-dimensional table is considered as an array of rows:
- table[0] is the starting address for row 0.
- table[1] is the starting address for row 1.
- table[2] is the starting address for row 2.
- etc.
That is, table[i] designates the starting location for row i of the table.
-
Within a row, an index j designates the distance of an element from the row's starting address.
In summary: given a 2-dimensional array table[8][6]
-
table is the base address or starting address for the entire block of data allocated for this collection of data.
-
table[0], table[1], ... give the base addresses or starting addresses for the successive rows of the 2-dimensional array.
-
table[i][j] identifies data at the specific address of the designated element within the block.
In particular, for this example, table[0] specifies a base address and represents a 1-dimensional array.
Calculating the Location of an Array Element
Based upon this organization of main memory, the location of any array element can be computed very quickly. Suppose the variable row designates the number of rows for a 2-dimensional array, and suppose col designates the number of columns. To locate element table[i][j], one proceeds as follows:
- Start at the base address given by the table variable.
- Skip over i rows; this involves i * col elements.
- Skip over j elements in the given row.
Altogether, the element table[i][j] may be found at address table + i*col + j. That is, the element itself may be referenced with the computation
element = *(table + i*col + j);
2-dimensional Arrays as Function Parameters
One consequence of this computation is that the compiler must know how many columns are in a two-dimensional array into order to determine where an array element might be. In particular, the signature of a procedure involving a two-dimensional array must include the number of columns. As with one-dimensional arrays, the number of rows need not be specified, as the computation of an element is valid for any i and j, as long as the number col is known.
In practice, column information may be included in a procedure header in either of two ways:
-
If col is an int variable declared globally in the program, then a procedure may have the header:
void proc (int table [] [col]); // col declared globally
-
Alternatively, col can be passed to the procedure as part of the parameters:
void proc (int col, int table [] [col]); // col passed as parameter
The layout of a table in memory requires the computer to know the width of a table.
-
When the number of columns in a 2-dimensional array table is know, then the location of table[i][j] is straight forward to compute.
-
Without knowing the width, the location of entry table [i][j] cannot be computed.
created 2 August 2011 by Erik Opavsky revised 8 August 2011 by Erik Opavsky modest editing 23 October 2011 by Dilan Ustek and Henry M. Walker modest reformatting 6 November 2011 by Henry M. Walker minor editing 25 October 2013 by Henry M. Walker reformatting, modest editing, discussion of 2D storage 1-2 February 2014 by Henry M. Walker readings added 19 September 2014 by Henry M. Walker lab reworked 28 December 2014 by Henry M. Walker |
![]() ![]() |
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |