int foo(x, y, z)
double *x;
int y;
double z;
{
/* do something */
}
or
int foo(double *x, int y, double z)
{
/* do something */
}
I notice the first one appeared in 1978's The C Programming Language
book.
The second edition of the book adopted the second one. The first one
seem more
readable when there are many arguments.
There are more issues than readability.
The first form, by not specifying the types in the parameter list,
applies standard rules of promotion of types but does not lead to
checking that the types match. The second allows the compiler to check
that the values of the types of the arguments in an invocation of the
function are convertible to the the types of the parameters the function
expects, and if they
a) are not convertible to issue a diagnostic, and
b) are convertible but not identical to apply the appropriate conversion.
Suppose you had the simple function
double square(x)
double x;
{
return x*x;
}
and called it with
square(1);
The compiler will generate a call with an _int_ of value 1, which
may not bear any semblance to a _double_ with value 1.
But suppose you had the simple function
double square(double x)
{
return x*x;
}
and called it with
square(1);
The compiler can now convert value 1 to a double and pass that to the
function.
You might ask why you can't do that with the first kind of function
definition. You could, it you were building a language from the ground
up, but the rules for the first way of handling argument lists were
established before the second way existed.
As for readability, suppose you use whitespace to rewrite the second as
int foo(double *x,
int y,
double z)
{ /* .... */ }
The whitespace does nothing to the meaning, but it now has all the
advantages you might see in
int foo(x, y, z)
double *x;
int y;
double z;
{ /* .... */ }