A function definition may have its old form (and say nothing about arguments on calls), or it may be introduced by a prototype (which affects argument checking and coercion on subsequent calls). (See also §3.1.2.2.)
To avoid a nasty ambiguity,
the Standard bans the use of typedef
names as formal parameters.
For instance, in translating the text
int f(size_t, a_t, b_t, c_t, d_t, e_t, f_t, g_t, h_t, i_t, j_t, k_t, l_t, m_t, n_t, o_t, p_t, q_t, r_t, s_t)the translator determines that the construct can only be a prototype declaration as soon as it scans the first
size_t
and following comma.
In the absence of this rule, it might be necessary to see
the token following the
right parenthesis that closes the parameter list,
which would require a sizeable look-ahead,
before deciding whether the text under scrutiny is a prototype declaration
or an old-style function header definition.
An argument list must be explicitly present in the declarator;
it cannot be inherited from a typedef
(see §3.5.4.3).
That is to say, given the definition
typedef int p(int q, int r);the following fragment is invalid:
p funk /* weird */ { return q + r ; }
Some current implementations rewrite the type of a
(for instance)
char
parameter as if it were declared int
,
since the argument is known to be passed as an int
(in the absence of prototypes).
The Standard requires, however, that the received argument be converted
as if by assignment upon function entry.
Type rewriting is thus no longer permissible.
char
or short
parameter types being widened to int
,
or float
to double
, may behave differently.
short
or char
is a contiguous subset of the bytes comprising the int
actually passed (for even the most unusual byte orderings),
so that assignment conversion can be
effected by adjusting the address of the
argument (if necessary) .
For an argument declared float
, however,
an explicit conversion must usually be performed from the
double
actually passed to the float
desired.
Not many implementations can subset the bytes of a double
to get a float
.
(Even those that apparently permit simple truncation often get the wrong
answer on certain negative numbers.)
Some current implementations permit an argument to be masked by a declaration of the same identifier in the outermost block of a function. This usage is almost always an erroneous attempt by a novice C programmer to declare the argument; it is rarely the result of a deliberate attempt to render the argument unreachable. The Committee decided, therefore, that arguments are effectively declared in the outermost block, and hence cannot be quietly redeclared in that block.
The Committee considered it important
that a function taking a variable number of arguments,
such as printf
,
be expressible portably in C.
Hence, the Committee devoted much time to exploring methods of traversing
variable argument lists.
One proposal was to require arguments to be passed as a
``brick''
(i.e., a contiguous area of memory),
the layout of which would be sufficiently well specified
that a portable method of traversing the brick could be determined.
Several diverse implementations, however, can implement argument passing more efficiently if the arguments are not required to be contiguous. Thus, the Committee decided to hide the implementation details of determining the location of successive elements of an argument list behind a standard set of macros (see §4.8).
See §3.1.2.2.