Path: utzoo!attcan!uunet!elroy.jpl.nasa.gov!usc!snorkelwacker.mit.edu!bloom-picayune.mit.edu!news From: scs@adam.mit.edu (Steve Summit) Newsgroups: comp.lang.c Subject: Re: Func Protos with K&R Func Defs Message-ID: <1991Feb28.072947.28885@athena.mit.edu> Date: 28 Feb 91 07:29:47 GMT References: <11614@jpl-devvax.JPL.NASA.GOV> <1991Feb28.021715.18153@athena.mit.edu> Sender: news@athena.mit.edu (News system) Reply-To: scs@adam.mit.edu Organization: Thermal Technologies, Inc., Cambridge, MA Lines: 141 In article <11614@jpl-devvax.JPL.NASA.GOV> david@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@athena.mit.edu> jik@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@adam.mit.edu