As I wrote a couple of messages ago:
It is essential to understand the array-to-pointer standard conversion, which converts an array to a pointer to its first element in most contexts. This is the thing from which all the pointer-like behavior of arrays arises, and it is the explanation for chown33's first sentence. Then it's mainly a matter of understanding how pointers work. For example, array indexing is defined in terms of pointer arithmetic, and the first step from a[2] (where "a" is an array name) to *(a+2) is the array-to-pointer conversion. Then you need to understand pointer arithmetic. Then dereferencing the pointer result.
While the array-to-pointer conversion occurs in array indexing, passing arguments to functions, returning values from functions (that being a big gotcha for a local non-static array), etc, there are contexts in which it does not occur. A couple that come to mind include when an array is the operand of the address-of operator and sizeof. In C++, other examples include when binding to an array reference and when the array is the operand to typeid. In those instances, an array does not undergo the array-to-pointer conversion. There might be one or two more instances I'm not thinking of at the moment. But anytime an array occurs in other expressions, it undergoes the array-to-pointer conversion, e.g.
Code:
typedef struct S { int x; } S;
void f(S*);
void g()
{
S s[10];
S* p = s+5;
s[1];
*s;
s->x;
p-s;
f(s);
}
In each of the expressions above involving s, s undergoes the array-to-pointer conversion. And it's not just named arrays that behave like this; it's also subarrays of multidimensional arrays, e.g.
The expressions t[0] and t[1] are both arrays with type S[10] and they behave just like s in the first example, to the extent you can substitute (say) t[0] for every occurrence of s. Note, however, that t contains no pointers at all. It's just a region of storage containing 20 S's, one after the other, in row major order.