Isn't he better of using non-prototype declarations instead (same
thing, but less hastle)?
int f();
"Better off"? Possibly -- but these are different things. Given
the first example definition ("int f(a, b) float a, b; { ... }"),
the correct prototype declaration is:
int f(double, double);
Suppose we write that, and then include a call such as:
f(3, 4)
in the code. Any conforming C89 or C99 compiler must accept this,
and convert 3 and 4 to 3.0 and 4.0 in the call, because arguments
in a prototyped call "work like" ordinary assignments to the formal
parameters of the function. Given:
int f(a, b) float a, b; { ... }
the compiler has to implement the actual call-and-entry as if we
had written a prototyped version like this:
int f(double a0, double b0) { float a = a0, b = b0; ... }
so "a" will be set to 3.0f and b to 4.0f (ignoring any possible
error introduced via double-to-float conversion, anyway -- fortunately
both 3.0 and 4.0 are exact in all extant floating-point systems in
the world
).
If we remove the prototype declaration, and substitute in the
non-prototype declaration:
int f();
above the call to f(3, 4), the call suddenly becomes *incorrect*:
it passes two "int"s where two "double"s are required. The result
is likely to be disaster. (On typical x86 compilers, the two ints
will occupy the stack-space for one of the "double"s and the other
will be constructed from miscellaneous stack goop. On typical
register-parameter-machines, the "int"s will be delivered in integer
registers instead of floating-point registers. Some calling
conventions use the integer registers for floating-point anyway,
and if so, the behavior will be similar to that on the x86; otherwise
it will be even less predictable.)
Similarly, the lack of a prototype will prevent detection of
erroneous calls such as:
f("oops");
Clearly, prototypes are generally a good idea. This leads to the
desire to provide prototypes even for those old-style definitions,
which is generally also a good idea, but has the pitfalls that Eric
Sosman noted. The best solution, in my opinion, is to change the
code so that prototype declarations *and definitions* are used
everywhere, abandoning pre-C89 implementations entirely. (But
there are occasions where this is not possible, for whatever reason,
in which case, providing "optional prototypes", as 4.xBSD did with
its implementation-namespace __P macro, is a second-best solution.)