Why do you think that foo[1][2][3] requires more than one memory reference?
T value = foo[1][2][3]; // becomes:
foo -> (T **)
(T **) -> (T *)
(T **) (T *)
... (T *) -> (T)
(T *) (T)
... (T)
(T) <-- This one!
This allows for jagged arrays, yay! So useful.This is in stark contrast to a Fortran-style array, which is allocated as one contiguous piece, all dimensions folded up for linear access with one dereference.
I recommend you read up on what pointer decay actually does; it's more complicated than replacing all arrays with pointers!
In particular, the type of an `arr` expression (after decay) is `int(*)[20][30]`. Decay only ever changes the top-level (outermost) type! And the type of `&arr` is even `int(*)[10][20][30]` -- using `&` or `sizeof` prevents decay from happening. Pointers to arrays are rarely used because using decay is more idiomatic (and because their syntax is unwieldy), but they still exist and would be safer than using decay.