The real difference comes in dynamic allocation vs. not. Consider:
1 2 3 4 5 6 7 8
|
int stack_array[ 10 ][ 10 ];
// vs.
int** dynamic_array;
dynamic_array = new int*[ 10 ]; // Allocating 40 bytes here
for( size_t i = 0; i < 10; ++i )
dynamic_array[ i ] = new int[ 10 ]; // Allocating 40 bytes here, 10 times = 400 bytes
|
In case 1, exactly 400 bytes of memory is needed (assume 32 bit here, please) for the array.
In case 2, the total size of allocation is 400 + 40 = 440.
The difference? Pointers.
To access element [3][4] of stack_array, the compiler essentially has to generate the following
pseudo-assembler:
1 2 3 4 5 6
|
LD r1, stack_array // r1 <- address-of stack_array
LD r2, 10 // r2 <- 10
MUL r2, 3 // r2 *= 3
ADD r2, 4 // r2 += 4
ADD r2, r1 // r2 += r1
LD r3, [r2] // r3 <- value stored at that memory location
|
Note: 2 memory accesses.
To access element [3][4] of dynamic_array, here's what it needs to do:
1 2 3 4 5
|
LD r1, dynamic_array // r1 <- address-of dynamic_array
ADD r1, 3 // r1 += 3
LD r2, [r1] // r2 <- value stored at that memory location
ADD r2, 4 // r2 += 4
LD r3, [r2] // r3 <- value stored at that memory location
|
Note: 3 memory accesses. The extra memory access makes accessing dynamically
allocated multi-dimensional arrays slower than if they were fixed length and on the
stack. (Even though it might look faster because it is fewer instructions, the memory
accesses dominate the execution time).