They have different memory layouts. A 2D array is one contiguous piece of memory while int**
is an array of pointers. With a 2D array, the offset into a location is computed as rownum * collength + colnum
(or vice versa depending on how you label rows/columns). That would not work with int**
, which actually produces two memory reads; the first to get the column pointer, then the read of the data at an offset from that memory.
This different layout is, incidentally, why you must declare the array dimensions (all but the left-most) in functions that accept 2D arrays; otherwise it would not be possible for the compiler to generate the code to compute the offsets.
The following is an attempt at a picture of the memory layout of int**
. The left column is a contiguous array of pointers where each contains the address of a contiguous piece of memory with the data. Note that the data in each column does not have to be the same length (although that might not necessarily be a good thing for code readability):
int **array;
int i, j;
int cntr = 1;
array = malloc( 3 * sizeof( int* ));
for ( i = 0; i < 3; i++ ) {
array[i] = malloc( 4 * sizeof( int ));
for ( j = 0; j < 4; j++ )
array[i][j] = cntr++;
}
Gives something like this:
array ==> |addr1| ==> [1,2,3,4]
|addr2| ==> [5,6,7,8]
|addr3| ==> [9,10,11,12]
In contrast, This is what the layout might look like for int[3][4]
. The brackets just show the logical break of each column of data. The integer values are contiguous in memory:
int array[3][4];
int i, j;
int cntr = 1;
for ( i = 0; i < 3; i++ )
for ( j = 0; j < 4; j++ )
array[i][j] = cntr++;
Gives something like this:
array ==> [1,2,3,4][5,6,7,8][9,10,11,12]