3.6 Statements  -< ANSI C Rationale  -> 3.8 Preprocessing directives             Index 

3.7  External definitions

3.7.1  Function definitions

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. 

Notes for implementors: the assignment conversion for argument passing often requires no executable code.  In most twos-complement machines, a 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). 

3.7.2  External object definitions

See §3.1.2.2


3.6 Statements  -< ANSI C Rationale  -> 3.8 Preprocessing directives             Index