Static Multidimensional Arrays in C

Static multidimensional arrays in C are a form of two-dimensional or more dimensional arrays, which can be used to represent data in a tabular form, such as a matrix of numbers.

Being static, for multidimensional arrays it is necessary that the dimensions are known at compile time and cannot change during program execution.

In this lesson, we will explore how to declare and use static multidimensional arrays in C, providing practical examples to help readers better understand this functionality.

Static Multidimensional Arrays

In the previous lesson we studied single-dimensional static arrays. We saw that the dimension cannot be modified during program execution, that is, dynamically.

In this lesson we will focus on multidimensional static arrays.

An array can have more than one dimension. For example, the following declaration creates a two-dimensional array:

int x[4][6];

From a mathematical point of view, an array of this type can be seen as a matrix. In this case we have declared an integer array of 4 rows and 6 columns.

As with the one-dimensional case, also for the multidimensional case the indexing of row and column elements starts from zero. To clarify better we can observe the following figure, which represents an array with n rows and m columns:

Static Multidimensional Array
Picture 1: Static Multidimensional Array

To access an element located at row i and column i we must use the following syntax:

x[i][j];

In the above expression, x[i] indicates the entire row i. Therefore the expression x[i][j] selects element j in row i.

Note

A common error in using multidimensional arrays in C is to write the following expression to access an element:

/* Error: */
x[i, j];

Although this expression is syntactically correct and, in fact, the compiler does not report any error, in reality we are using the comma operator.

Therefore the above expression is interpreted as:

x[j];

So we are selecting the entire row j!

Arrays in C can also have more than two dimensions. For example, we can create 3-dimensional or 4-dimensional arrays as in the following example:

/* 3-dimensional array */
int x[3][3][4];

/* 4-dimensional array */
int y[2][2][3][4];
Definition

Static Multidimensional Arrays

A Static Multidimensional Array is a multi-dimensional array whose number of elements cannot vary during program execution.

The syntax to declare an array of this type is as follows:

type name[dimension_1][dimension_2][dimension_3];

Memory Layout

Although we can visualize a multidimensional array as a table or matrix, in reality in memory multidimensional arrays are not stored this way.

In fact, the C language stores arrays by row. This means that an array will always be stored as a sequence of values in memory where there will be first row 0, then row 1 and so on.

For example, let's take the following array:

int x[3][2];

The array x has 3 rows and 2 columns. Therefore its memory layout will be:

Memory layout of the multidimensional array from the example
Picture 2: Memory layout of the multidimensional array from the example

Using Loops

Similarly to the case of one-dimensional arrays, also for multidimensional arrays for loops are used to process the elements contained in them. In particular, nested for loops are used.

Let's take a simple mathematical example. We want to initialize a multidimensional array to use it as an identity matrix. An identity matrix is a matrix for which all elements are equal to zero except those on the main diagonal which instead are equal to 1.

To be able to initialize an array in this way and transform it into an identity matrix we must visit all the elements of the array and set them to zero if the row number is different from the column number, or set them to one otherwise.

To do this we can use two nested for loops: a first outer loop that iterates over the rows and a second inner loop that iterates over the columns.

We can write the code this way:

/* Constant representing the number
   of rows and columns */
const int N = 10;

/* The matrix to initialize */
double M[N][N];

/* Row and column indices */
int row;
int col;

/* Outer loop for rows */
for (row = 0; row < N; ++row) {
    /* Inner loop for columns */
    for (col = 0; col < N; ++col) {
        if (row == col) {
            M[row][col] = 1.0;
        }
        else {
            M[row][col] = 0.0;
        }
    }
}

Initialization

Also for multidimensional arrays we can use initialization lists. In this case, however, we must nest multiple initialization lists one inside the other.

For example, wanting to create an identity matrix of 4 rows and 4 columns we can write the following code:

double M[4][4] = {{1.0, 0.0, 0.0, 0.0},
                  {0.0, 1.0, 0.0, 0.0},
                  {0.0, 0.0, 1.0, 0.0},
                  {0.0, 0.0, 0.0, 1.0}};

Each inner initialization list provides the values for one row of the array.

Similarly to the one-dimensional case, the C language provides various ways to abbreviate the length of initialization lists.

If the initialization list contains fewer rows all remaining rows will have elements initialized to zero. For example:

/* The array is 3x3
   The initialization list is 2x3 */
int x[3][3] = {{1, 2, 3},
               {4, 5, 6}};

In this case the array will contain the following elements:

\begin{array}{ccc} 1 &amp; 2 &amp; 3 \\ 4 &amp; 5 &amp; 6 \\ 0 &amp; 0 &amp; 0 \end{array}

If an inner list has fewer elements all elements of that row will be initialized to zero. For example:

/* The array is 3x3 */
int x[3][3] = {{1, 2, 3}, /* Row 1: 3 elements */
               {4, 5},    /* Row 2: Only 2 elements */
               {6}};      /* Row 3: Only 1 element */

In this case the array will contain the following elements:

\begin{array}{ccc} 1 &amp; 2 &amp; 3 \\ 4 &amp; 5 &amp; 0 \\ 6 &amp; 0 &amp; 0 \end{array}

Finally, an initialization list can also omit the inner lists. In other words, the initialization list can also be a simple list. The compiler will fill a row with the available elements. After completing a row, it will fill the others with the remaining elements.

For example, let's take an array of 3 rows and 3 columns. In total it will have 9 elements, so we can initialize it this way:

int x[3][3] = {1, 2, 3,
               4, 5, 6,
               7, 8, 9};

In this case we used a simple initialization list which is completely equivalent to writing the following code:

int x[3][3] = {{1, 2, 3},
               {4, 5, 6},
               {7, 8, 9}};

Also in this case the same rules apply for the case where there are fewer elements. For example, if for the 3x3 array we use a list with 7 elements:

int x[3][3] = {1, 2, 3,
               4, 5, 6,
               7};

The array will contain the following elements:

\begin{array}{ccc} 1 &amp; 2 &amp; 3 \\ 4 &amp; 5 &amp; 6 \\ 7 &amp; 0 &amp; 0 \end{array}
Note

Avoid One-Dimensional Initialization Lists with Static Multidimensional Arrays

It is always advisable to avoid using one-dimensional initialization lists with multidimensional arrays. In fact, the insertion of extra or missing elements by oversight or error can cause the sliding of all other initialization values.

For this reason, some compilers produce a warning message in this situation.

Designated Initializers in C99

The C99 language standard allows the use of designated initializers also for multidimensional arrays.

Using designated initializers we can simplify the declaration of the identity matrix from the above example this way:

/* Creation of a 4x4 identity matrix
   with designated initializers */
double M[4][4] = {[0][0] = 1.0,
                  [1][1] = 1.0,
                  [2][2] = 1.0,
                  [3][3] = 1.0};

In Summary

In this lesson we studied multidimensional arrays, that is, with more than one dimension in C language. We focused on static ones for which the dimensions cannot vary during program execution.

We saw that it is possible to use nested initialization lists to initialize them and we saw nested for loops to be able to access the elements.

In common practice, however, static multidimensional arrays are not widely used in C language because there is often the need to be able to modify the dimension of such arrays at execution time. To be able to work with dynamic arrays, however, it is necessary to understand well how pointers work, which we will study in subsequent lessons.