Func Protos with K&R Func Defs

Steve Summit scs at adam.mit.edu
Thu Feb 28 18:29:47 AEST 1991


In article <11614 at jpl-devvax.JPL.NASA.GOV> david at jpl-devvax.JPL.NASA.GOV (David E. Smyth) writes:
>I do this all the time:
>
>    #ifdef _FUNCTION_PROTOTYPES
>    extern void WcWidgetCreation ( Widget root );
>    #else
>    extern void WcWidgetCreation();
>    #endif
>
>    void WcWidgetCreation ( root )
>        Widget       root;
>    {
>        ...
>
>This seems like the easiest way to use prototyped function declatations
>when your compiler supports it, and K&R function definitions in any
>case.  Then only the *.h files need to have #ifdef's.

Indeed, and this is essentially the technique I use.  (In
external function declarations, I omit the #else, and leave the
nonprototyped form visible to both kinds of compilers, which adds
a bit of consistency checking.)

This technique works well, although there are two important
caveats which require some care in applying, which is why mixing
prototyped declaration with "old style" definitions is not
generally recommended.

The two caveats are:

     1.	The prototype declaration must use the widened types (int
	or double) for any parameters in the old-style definition
	which are "narrow" (char, short, or float).

     2.	The prototype must not contain an ellipsis, and hence the
	function must not accept a variable number of arguments.

Caveat 2 means that this "reduced-#ifdef" technique cannot be
used everywhere; functions which take a variable number of
arguments must still be defined with #ifdefs or other tricks if
the code is to be acceptable to both kinds of compilers.

In article <1991Feb28.021715.18153 at athena.mit.edu> jik at athena.mit.edu (Jonathan I. Kamens) writes:
>  That may be the easiest way to do things, but it's not legal ANSI C.
>  In ANSI C, a function declaration must match its definition.  That means
>that if the declaration is prototyped, then the definition must also be
>prototyped, with the same types; if the declaration is not prototyped, then
>the definition cannot be prototyped either.
>  [mentions problem with parameters of type char; this is one of the caveats]
>  Summary: Function declarations and definitions must match.  If you have
>#ifdef's in hour headers to decide whether or not to use prototypes, then you
>must #ifdef your definitions similarly.

This advice is overly restrictive.  (In particular, the third
quoted sentence does not reflect a requirement of the Standard.)
The relevant sections from ANSI X3.159 are 3.3.2.2:

	If the function is defined with a type that is not compatible
	with the type (of the expression) pointed to by the expression
	that denotes the called function, the behavior is undefined.

and 3.5.4.3:

	For two function types to be compatible, both shall specify
	compatible return types.  Moreover, the parameter type lists, if
	both are present, shall agree in the number of parameters and in
	use of the ellipsis terminator; corresponding parameters shall
	have compatible types.  If one type has a parameter type list
	and the other type is specified by a function declarator that is
	not part of a function definition and that contains an empty
	identifier list, the parameter list shall not have an ellipsis
	terminator and the type of each parameter shall be compatible
	with the type that results from the application of the default
	argument promotions.  If one type has a parameter type list and
	the other type is specified by a function definition that
	contains a (possibly empty) identifier list, both shall agree in
	the number of parameters, and the type of each prototype
	parameter shall be compatible with the type that results from
	the application of the default argument promotions to the type
	of the corresponding identifier.

That's a lot of words, but if you read it carefully, you'll find
that the four sentences cover:

     1.	the return type

and the cases in which:

     2.	both declaration and definition have prototypes
     3.	definition (or one declaration) has a prototype, another
	declaration does not
     4.	declaration has a prototype, definition is old-style

There is a minor omission in the fourth sentence; it should
probably emphasize that the type with a parameter type list shall
not have an ellipsis terminator.

We are most interested in the fourth sentence, which says that
the correct (new-style) prototype declaration for the (old style)
function definition

	x(c, s, i, f, d)
	char c;
	short s;
	int i;
	float f;
	double d;

is

	extern x(int, int, int, double, double);

Obviously it is best to avoid such anomalous and apparently
contradictory declarations, by avoiding parameters of type char,
short, and float.  (Many people suspect gcc to be buggy when it
correctly diagnoses, with a long, verbose error message, a float
in a prototype erroneously corresponding to a float in an
old-style definition, which is why a question on this part of the
problem appears in the comp.lang.c frequently-asked questions
list.)

However, as long as either

     1.	the old-style definition contains no char, short, or int
	parameters 
or
     2. the prototype declaration is careful to use the promoted
	type of each "narrow" parameter

, and as long as the prototype does not contain an ellipsis, the
"mixture" is guaranteed to work.

If you want to be safe and follow the sheep, use prototypes
consistently.  If you want to keep using old tools (compilers,
lint, etc.), and if you're not using an automated ANSI->K&R tool,
and if you don't like #ifdefs or other preprocessor tricks in
function definitions, use these mixtures in good health.  (Just
be very careful of those narrow parameters and ellipses.)

                                            Steve Summit
                                            scs at adam.mit.edu



More information about the Comp.lang.c mailing list