Would someone kindly tell me why the following bit of code generates a
runtime error:
It should get a compile-time error.
int array[3][3]; /* data can be of any size */
foo( array ); /* compile-time error */
foo( (int**)array ); /* run-time error */
It generates an error because it's wrong.
Conceptually, a 2D array looks like this:
array = [ [ a, b, c ], [ d, e, f], [ g, h, i] ]
array[0] is really the array [a, b, c]. But! When you use the
expression "array[0]", the array is implicitly converted into a pointer
to its first element (in most contexts; not, say, in a sizeof()).
So array[0][0] is interpreted as array (understood to be a pointer to the
block [a, b, c]), indexed by 0 (getting us the actual object [a, b, c]),
which in turn is then interpreted as a pointer to its first element (a),
which is then dereferenced, yielding the actual object a.
If you had an int **, though, it would look like this:
ptrptr = &level1
level1 = &[ level2sub1, level2sub2, level2sub3 ]
level2sub1 = &[ a, b, c ]
level2sub2 = &[ d, e, f ]
level2sub3 = &[ g, h, i ]
When you write ptrptr[0][0], the interpretation is different. ptrptr is
already a pointer to level1. The variable ptrptr doesn't actually refer to
the block of memory containing those three other pointers; it is actually
an address to begin with, rather than being an object that is converted to
an address when you use it in an expression. Similarly, ptrptr[0] isn't
an object which contains a, b, and c -- it's an object which contains a
single address, which describes where a, b, and c are stored.
To experiment with this a bit, try:
int array[10];
int *ptr;
printf("array is %d bytes\n", (int) sizeof(array));
printf("pointer is %d bytes\n", (int) sizeof(ptr));
Now, think a bit about this:
int array[10];
void foo(int *a) {
}
...
foo(array);
Why does this work? It works because "array" is converted into a pointer
to array[0]. That's good enough to be mostly interchangeable with any
other pointer. But if you do:
int array[10][10];
void foo(int **a) {
}
...
foo(array);
it's not quite right. foo() is expecting a pointer to a series of pointers
to ints. array is converted, not into a pointer to a bunch of pointers, but
into a pointer to a series of arrays. If foo() tries to look at the second
member of its argument a, it calculates where in an array of pointers the
second pointer would be, then uses the bits it finds there as a pointer,
which it thinks will point to some ints. Instead, you've given it something
where (most likely) the bits that it takes are actually one of the members
of array[0], so it's not a pointer at all.
Basically, you lied to the compiler, and it got its revenge.
-s