Multidimensional Arrays
An array with more than one dimension is called a multidimensional array.Building up multidimensional arrays:int matrix[5][10]; /* array of 5 arrays of 10 int; a 5x10 array of int */
int a; /* int */ int b[10]; /* array of 10 int */ int c[5][10]; /* array of 5 arrays of 10 int */ int d[3][5][10]; /* array of 3 arrays of 5 arrays of 10 int */ int e[10][5][3]; /* array of 10 arrays of 5 arrays of 3 int */
Given this declaration of points:Storage order: Arrays in C are stored in row major order. This means that the rightmost subscript varies the most rapidly.
double points[3][4]; /* points[ROWS][COLUMNS] */
With details:
Or draw it contiguously (as it really is in memory):
Or horizontally:
Giving concrete values to the 2D array of doubles will help visualize the arrays. Note how the initialization syntax helps us visualize the "array of arrays" notion:
or even formatted as a 3x4 matrix:double points[3][4] = {{1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0}, {9.0, 10.0, 11.0, 12.0}};
Diagram:double points[3][4] = { {1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0, 8.0}, {9.0, 10.0, 11.0, 12.0} };
Some expressions involving points (on a 64-bit computer):
C code to display above tables.Addresses Type -------------------------------------------------------------------------------- points = 0x7fffa50f0200 An array of 3 arrays of 4 doubles &points = 0x7fffa50f0200 A pointer to an array of 3 arrays of 4 doubles points[0] = 0x7fffa50f0200 An array of 4 doubles &points[0] = 0x7fffa50f0200 A pointer to an array of 4 doubles *points = 0x7fffa50f0200 An array of 4 doubles &points[0][0] = 0x7fffa50f0200 A pointer to a double Contents ------------------------ **points = 1.000000 *points[0] = 1.000000 points[0][0] = 1.000000 Sizes ------------------------- sizeof(points) = 96 sizeof(*points) = 32 sizeof(points[0]) = 32 sizeof(**points) = 8 sizeof(points[0][0]) = 8 sizeof(&points) = 8
Notes:
Accessing Elements in a 2-D Array
short matrix[3][8]; /* 24 shorts, 3x8 array, 3 rows, 8 columns */
matrix
matrix[0] *(matrix + 0) *matrix
matrix[1] *(matrix + 1)
matrix[2] *(matrix + 2)
Remember the rule:
matrix[1][2] *(*(matrix + 1) + 2)
where:array[i] == *(array + i)
Pointer arithmetic is used to locate each element (base address + offset) and is done by the compiler. If you look at the assembly code that is generated, you will see all of the pointer arithmetic that is being done for each access.array[i][j] == *(*(array + i) + j) array[i][j][k] == *(*(*(array + i) + j) + k) etc...
Given this declaration:
The value of sizeof varies with the argument:short matrix[3][8];
Sizes ------------------------- sizeof(matrix) = 48 ; entire matrix sizeof(matrix[0]) = 16 ; first row sizeof(matrix[1]) = 16 ; second row sizeof(matrix[0][0]) = 2 ; first short element
Dynamically Allocated 2D Arrays
double points[3][4]; |
double *pd = (double *)malloc(3 * 4 * sizeof(double)); |
Given a row and column:
int row = 1, column = 2; double value;
value = points[row][column]; /* OK */ value = pd[row][column]; /* ILLEGAL */
value = pd[row * 4 + column];
value = *(address-of-pd + (row * 4 + column) * sizeof(double));
Why can't we just cast pd to a two-dimensional array and have the compiler do the pointer arithmetic for us?
Using these definitions from above:
Create a variable that is a pointer to a pointer to a double/* Assume these values are chosen at runtime. */ int ROWS = 3; int COLS = 4; /* Dynamically allocate the memory */ double *pd = malloc(ROWS * COLS * sizeof(double));
Allocate an array of 3 (ROWS) pointers to doubles and point ppd at it:double **ppd;
ppd = malloc(ROWS * sizeof(double *));
Point each element of ppd at an array of 4 doubles:
Of course, for a large array, or an array whose size is not known at compile time, you would want to set these in a loop:ppd[0] = pd; ppd[1] = pd + 4; ppd[2] = pd + 8;
This yields the diagram (32-bit computer):int row; for (row = 0; row < ROWS; row++) ppd[row] = pd + (COLS * row);
Given a row and column, we can access elements through the single pointer or double pointer variable:
We could make the code easier by creating a helper function that will allocate a 2D array of doubles of any size:int row = 1, column = 3; double value; /* Access via double pointer (array of arrays) using subscripting */ value = ppd[row][column]; /* Access via single pointer using pointer arithmetic */ /* and/or subscripting. These statements are all equivalent. */ value = pd[row * COLS + column]; value = *(pd + row * COLS + column); value = (pd + row * COLS)[column];
double **allocate_2D(int rows, int cols)
{
int i; /* Loop variable */
double **pointers; /* The pointers for each row */
double *array; /* The actual array of doubles */
/* Allocate memory for the rows X cols array */
array = (double *) malloc(rows * cols * sizeof(double));
/* Allocate the array of pointers, one per row */
pointers = malloc(rows * sizeof(double *));
/* Point each pointer at its corresponding row */
for (i = 0; i < rows; i++)
pointers[i] = array + (cols * i);
return pointers;
}
void print2D(double **ppd, int rows, int cols)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
printf("%8.2f", ppd[i][j]);
printf("\n");
}
}
Using it:
Output:int i, j; int rows = 3, cols = 4; double **ppd = allocate_2D(3, 4); /* Do something with the array ... */ for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) ppd[i][j] = i * cols + j + 1; print2D(ppd, rows, cols); free(*ppd); /* Free the array of 12 doubles (Must do this first!) */ free(ppd); /* Free the pointers to each row */
1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00 10.00 11.00 12.00
You can't just create a 1D array and then cast it to a 2D array. The first reason is that you can't cast to an array. The second (if you could cast to an array) is that casting is done at compile-time, so the compiler needs to know how many columns there are. If you don't know the size until run-time, you need to do it this way.
Doing this:gives this error:double *p = (double [3][4]) points; /* Not legal */
error: cast specifies array type pd = (double [3][4])points;
Passing 2D Arrays to Functions
Recall how we pass an array to a function.Putting values in the matrix and printing it (from above):
short matrix[3][8]; /* 24 shorts, 3x8 array, 3 rows, 8 columns */
Implementations:Fill3x8Matrix(matrix); /* Put values in the matrix */ Print3x8Matrix(matrix); /* Print the matrix */
These functions could have specified the parameters this way: (precedence chart)void Fill3x8Matrix(short matrix[][8]) { int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 8; j++) matrix[i][j] = i * 8 + j + 1; } void Print3x8Matrix(short matrix[][8]) { int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 8; j++) printf("%i ", matrix[i][j]); printf("\n"); }
If you forget the parentheses it is incorrect and means something completely different:void Print3x8Matrix(short (*matrix)[8])
The above functions expect an array of pointers, which can also be written like this: (you don't provide the number)void Print3x8Matrix(short *matrix[8])
or this:void Print3x8Matrix(short *matrix[])
If you include the first number, it is ignored by the compiler just like all arrays that are passed as parameters:void Print3x8Matrix(short **matrix)
Why are they not declared like this?:void Print3x8Matrix(short matrix[3][8]); /* First number (3) is ignored */
This is the error from gcc:void Fill3x8Matrix(short matrix[][]); void Print3x8Matrix(short matrix[][]);
Here is a generic print function that can print any size 2D array (using pointer arithmetic). You must provide the size of both dimensions:error: array type has incomplete element type 'short int[]' void Fill3x8Matrix(short matrix[][]) ^~~~~~ note: declaration of 'matrix' as multidimensional array must have bounds for all dimensions except the first In function 'Fill3x8Matrix':
void PrintMatrix(short *matrix, int rows, int columns) { int i; for (i = 0; i < rows * columns; i++) printf("%i ", *(matrix + i)); printf("\n"); }
When you treat a 2D array as a 1D array like this it is called flattening the array. Technically speaking, static 2D arrays are not guaranteed to be contiguous in memory, although I've never encountered one that wasn't contiguous.void foo(void) { short mat[3][8]; Fill3x8Matrix(mat); /* only works with 3x8 */ Print3x8Matrix(mat); /* only works with 3x8 */ /* works with any size 2D array */ PrintMatrix(&mat[0][0], 3, 8); }
From the C-FAQ:
It must be noted, however, that a program which performs multidimensional array subscripting ``by hand'' in this way is not in strict conformance with the ANSI C Standard; according to an official interpretation, the behavior of accessing (&array[0][0])[x] is not defined for x >= [number of columns].
Finally:
The compiler needs to know the size of each element. It doesn't need to (and can't) know the number of elements. The size of each element is determined by the type of the elements, and for 2D arrays, the type is determined by the size of all but the first dimension.
Here's an example of using pointer arithmetic on static arrays:In decimal:void Test(int a[], int b[][6], int c[][3][5]) { printf("a = %p, b = %p, c = %p\n", (void *)a, (void *)b, (void *)c); a++; b++; c++; printf("a = %p, b = %p, c = %p\n", (void *)a, (void *)b, (void *)c); } Output: a = 0012FEE8, b = 0012FF38, c = 0012FEFC a = 0012FEEC, b = 0012FF50, c = 0012FF38
Shown graphically. The thick black lines indicate what the pointers are actually pointing at. The dotted gray elements represent zero or more elements. Remember, the size of the arrays (i.e. the number of elements in the arrays) is unknown to the function since it's only receiving pointers.Output: a = 1244904, b = 1244984, c = 1244924 a = 1244908, b = 1245008, c = 1244984
The function Test is equivalent to this:
Other methods for filling the matrix use explicit pointer arithmetic:void Test(int *a, int (*b)[6], int (*c)[3][5])
How does the compiler calculate the address (offset) for the element below?void Fill3x8Matrix(short matrix[][8]) { int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 8; j++) *(*(matrix + i) + j) = i * 8 + j + 1; } void Fill3x8Matrix(short matrix[][8]) { int i, j; for (i = 0; i < 3; i++) { short *pmat = *(matrix + i); for (j = 0; j < 8; j++) *pmat++ = i * 8 + j + 1; } }
matrix[1][2];
Using address offsets we get:
&matrix[1][2] ==> &*(*(matrix + 1) + 2) ==> *(matrix + 1) + 2
We can calculate the size of any portion:short matrix[3][8] short array[10]
Recap:Expression Meaning Size (bytes) ----------------------------------------------------------- array Entire array 20 array[N] Element in 1st dimension 2 matrix Entire array 48 matrix[N] Element in 1st dimension 16 matrix[N][M] Element in 2nd dimension 2
Additional examples with pointers and arrays
If you want to see even more examples, you can follow the link.