The literal answer to the question is simple: a[i]
is defined to be always identical to *(a+i)
by the standard, and therefore, a[j][i]
is guaranteed to be always identical to *(*(a+j)+i)
. However, that by itself does not help us to understand what is going on; it just transforms one compound expression to another.
a[j][i]
(and by extension, *(*(a+j)+i)
) does very different things depending on the type of a
. This is because, depending on the types, there may be implicit array-to-pointer conversions that are not apparent.
In C, a value of array type T[x]
is implicitly converted to an rvalue of pointer type T*
in many contexts, some of which include the left side of the subscript operator, as well as an operand in addition. So if you do either a[i]
or *(a+i)
, and a
is an expression of array type, in both cases it is converted to a pointer to its first element (like &a[0]
) and it's the pointer that participates in the operation. Thus you can see how *(a+i)
makes sense.
If a
had type T[x][y]
, it would be a "true" multidimensional array, which is a C array whose elements are themselves C arrays (of a certain compile-time-constant size). In this case, if you consider *(*(a+j)+i)
, what is happening is 1) a
is converted to a pointer to its first element (which is a pointer to an array, of type T(*)[y]
), 2) that pointer is incremented and dereferenced, producing a value of array type (the j
th subarray of a
), 3) that array is then converted to a pointer to its first element (a pointer of type T*
), which is then 4) incremented and dereferenced. This finally produces the i
th element of the j
th element of a
, what you usually think of as a[j][i]
.
However, a
could also have type, say, T**
. This is usually used to implement "fake" multidimensional arrays, which is an array of pointers, which then in turn point to the first element of some array. This allows you to have "rows" that can have different sizes (thus the multidimensional array need not be "rectangular"), and sizes not fixed at compile time. The "rows", as well as the main pointer array, do not have to be stored contiguously. In this case, if you consider *(*(a+j)+i)
, what is happening is 1) a
is incremented and dereferenced, producing a value of pointer type (the j
th element of a
), 2) that pointer is then incremented and dereferenced. This finally produces the i
th element of the array referred to by the j
th element of the main pointer array. Note that in this case there are no implicit array-to-pointer conversions.