For starters according to the C Standard (6.7.6.3 Function declarators (including prototypes)):
7 A declaration of a parameter as "array of type" shall be adjusted
to "qualified pointer to type", where the type qualifiers (if any)
are those specified within the [ and ] of the array type derivation.
If the keyword static also appears within the [ and ] of the array
type derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first
element of an array with at least as many elements as specified by the
size expression.
and secondly (6.7.6.2 Array declarators )
4 If the size is not present, the array type is an incomplete type. If
the size is * instead of being an expression, the array type is a
variable length array type of unspecified size, which can only be used
in declarations or type names with function prototype scope;146) such
arrays are nonetheless complete types. If the size is an integer
constant expression and the element type has a known constant size,
the array type is not a variable length array type; otherwise, the
array type is a variable length array type. (Variable length arrays
are a conditional feature that implementations need not support; see
6.10.8.3.)
The highlighted phrase in the first quote from the C Standard is applied
to parameter declarations having variable length array types, non-variable array types (when the size of an array is specified with an integer constant expression) and incomplete array types.
In this example of function declarations
int f(int a[10]) { ... } // the 10 makes no difference
int f(int a[]) { ... }
int f(int *a) { ... }
parameters having an array type are adjusted by the compiler to pointers to element types of the arrays. So all three function declarations (pay attention to that only one definition must be provided) declare the same one function.
The same is valid for this example of function declarations
// 1.
int f(int n, int a[n]) { ... }
int f(int n, int *a) { ... }
In the first declaration the second parameter has a variable length array type. You may also write the declaration (that is not the function definition) the following way
int f(int , int [*]);
As for the third example
// 3.
int f(int a[n], int n) { ... }
int f(int *a, int n) { ... }
then if n is not declared in file scope before the first function declaration then the compiler evidently will issue an error that the identifier (or name) n is not declared. However if the variable n will be declared before the function declaration then the above two declarations will be equivalent.
Here is a demonstration program
//gcc 7.4.0
#include <stdio.h>
int f( int [*], int );
int f( int *, int );
int n = 10;
int f(int a[n], int n)
{
for ( int i = 0; i < n; i++ )
{
printf( "%d ", a[i] );
}
putchar( '\n' );
return 0;
}
int main(void)
{
int a[20];
const size_t N = sizeof( a ) / sizeof( *a );
for ( size_t i = 0; i < N; i++ )
{
a[i] = i;
}
f( a, N );
}
The program output is
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
int f(int arr[n])orint f(int *arr), provided both compilethe 10 makes no difference--> Analyzing tools will use that 10 and so it makes a difference there.