From the C language standard:
6.3.2.1.3 Except when it is the operand of the sizeof operator or the
unary & operator, or is a string literal used to initialize
an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that
points to the initial element of the array object and is not
an lvalue. If the array object has register storage class, the
behavior is undefined.
Assume the following code:
#include <stdio.h>
#include <string.h>
int main(void)
{
char foo[10] = {0};
char *p = foo;
foo[0] = 'b';
*(foo + 1) = 'a';
strcat(foo, "t");
printf("foo = %s, &foo = %p, &p = %p, sizeof foo = %lu, sizeof p = %lu\n",
foo, &foo, &p, (unsigned long) sizeof foo, (unsigned long) sizeof p);
return 0;
}
foo is declared as a 10-element array of char with all elements initialized to 0. p is declared as a pointer to char and is initialized to point to foo.
In the line
char *p = foo;
the expression foo has type "10-element array of char"; since foo is not an operand of either sizeof or &, and is not a string literal being used to initialize an array, its type is implicitly converted to "pointer to char" and is set to point to the first element of the array. This pointer value is copied to p.
In the lines
foo[0] = 'b';
*(foo + 1) = 'a';
the expression foo has type "10-element array of char"; since foo is not an operand of either sizeof or &, and is not a string literal being used to initialize an array, its type is implicitly converted to "pointer to char" and is set to point to the first element of the array. The subscript expression is interpreted as "`*(foo + 0)".
In the line
strcat(foo, "t");
foo has type "10-element array of char" and the string literal "t" has type "2-element array of char"; since neither is an operand of either sizeof or &, and while "t" is a string literal, it is not being used to initialize an array, both are implicitly converted to type "pointer to char", and the pointer values are passed to strcat().
In the line
printf("foo = %s, &foo = %p, &p = %p, sizeof foo = %lu, sizeof p = %lu\n",
foo, &foo, &p, (unsigned long) sizeof foo, (unsigned long) sizeof p);
the first instance of foo is converted to a pointer to char as described above. The second instance of foo is an operand of the & operator, so its type is not converted to "pointer to char", and the type of the expression "&foo" is "pointer to 10-element array of char", or "char (*)[10]". Compare this with type type of the expression "&p", which is "pointer to pointer to char", or "char **". The third instance of foo is an operand of the sizeof operator, so again its type is not converted, and sizeof returns the number of bytes allocated to the array. Compare this with the result of sizeof p, which returns the number of bytes allocated to the pointer.
Whenever anyone tells you "an array is just a pointer", they are garbling the section from the standard quoted above. An arrays are not pointers and pointers are not arrays; however, in many circumstances, you can treat an array as though it were a pointer and you can treat a pointer as though it were an array. "p" could be substituted for "foo" in lines 6, 7, and 8. However, they are not interchangeable as operands to sizeof or &.
Edit: btw, as function parameters,
void foo(int *a);
and
void foo(int a[]);
are equivalent. "a[]" is interpreted as "*a". Note that this is only true for function parameters.