Sun Studio 12: C User's Guide

6.2.3 Mixing Considerations

For function prototype declarations to work with old-style function definitions, both must specify functionally identical interfaces or have compatible types using ISO C’s terminology.

For functions with varying arguments, there can be no mixing of ISO C’s ellipsis notation and the old-style varargs() function definition. For functions with a fixed number of parameters, the situation is fairly straightforward: just specify the types of the parameters as they were passed in previous implementations.

In K&R C, each argument was converted just before it was passed to the called function according to the default argument promotions. These promotions specified that all integral types narrower than int were promoted to int size, and any float argument was promoted to double, hence simplifying both the compiler and libraries. Function prototypes are more expressive—the specified parameter type is what is passed to the function.

Thus, if a function prototype is written for an existing (old-style) function definition, there should be no parameters in the function prototype with any of the following types:


signed char

unsigned char



signed short

unsigned short


There still remain two complications with writing prototypes: typedef names and the promotion rules for narrow unsigned types.

If parameters in old-style functions were declared using typedef names, such as off_t and ino_t, it is important to know whether or not the typedef name designates a type that is affected by the default argument promotions. For these two, off_t is a long, so it is appropriate to use in a function prototype; ino_t used to be an unsigned short, so if it were used in a prototype, the compiler issues a diagnostic because the old-style definition and the prototype specify different and incompatible interfaces.

Just what should be used instead of an unsigned short leads us into the final complication. The one biggest incompatibility between K&R C and the 1990 ISO C compiler is the promotion rule for the widening of unsigned char and unsigned short to an int value. (See 6.4 Promotions: Unsigned Versus Value Preserving.) The parameter type that matches such an old-style parameter depends on the compilation mode used when you compile:

The best approach is to change the old-style definition to specify either int or unsigned int and use the matching type in the function prototype. You can always assign its value to a local variable with the narrower type, if necessary, after you enter the function.

Watch out for the use of id’s in prototypes that may be affected by preprocessing. Consider the following example:

#define status 23
void my_exit(int status);   /* Normally, scope begins */
                            /* and ends with prototype */

Do not mix function prototypes with old-style function declarations that contain narrow types.

void foo(unsigned char, unsigned short);
void foo(i, j) unsigned char i; unsigned short j; {...}

Appropriate use of __STDC__ produces a header file that can be used for both the old and new compilers:

    struct s { /* .  .  .  */ };
    #ifdef __STDC__
       void errmsg(int, ...);
       struct s *f(const char *);
       int g(void);
      void errmsg();
      struct s *f();
      int g();

The following function uses prototypes and can still be compiled on an older system:

struct s *
#ifdef __STDC__
    f(const char *p)
    f(p) char *p;
    /* .  .  .  */

Here is an updated source file (as with choice 3 above). The local function still uses an old-style definition, but a prototype is included for newer compilers:

   #include “header.h”
      typedef /* .  .  .  */ MyType;
   #ifdef __STDC__
      static void del(MyType *);
      /* .  .  .  */
      static void
      MyType *p;
      /* .  .  .  */
      /* .  .  .  */