They are not entirely the same. Consider this code:
typedef struct foo T;
void fx1(T *p) { }
void fx2(T p[]) { }
When compiling this, Clang accepts the first function without complaint but reports “array has incomplete element type 'T' (aka 'struct foo')” for the second.
That is because the element type of an array must have complete type, per C 2018 6.7.6.2 1, and Clang applies that rule before the rule in C 2018 6.9.1 7 that an array parameter is adjusted to be a pointer.
Of course, the first function could not operate on what p
points to, *p
, without completing the type T
. However, this situation could arise in functions that merely pass the pointer to other functions that know the complete type or in functions that convert the pointer to a different type. For example, the comparison routine passed to qsort
may be declared as int compare(const void *a, const void *b);
and could not be declared as int compare(const void a[], const void b[]);
.
A more arcane difference is that the array grammar allows this:
void fx2(int p[static 3]);
which says the argument passed for p
must point to at least three elements. There is no corresponding grammar for pointer parameters.
And a yet more esoteric difference occurs with this:
void fx2(int p[foo()]) {}
This nominally declares p
as an array with a variable number of elements, and there is no corresponding grammar for a pointer parameter. And it is troublesome because the C standard is unclear about whether the adjustment of p
from an array to a parameter occurs before foo()
expression is evaluated. Clang evaluates it and GCC does not.
Aside from the issues above, the parameter p
of fx2
is, after adjustment, identical to the parameter p
of fx1
.