6.2 A Mixture of Old- and New-Style Functions

The 1990 ISO C standard’s most sweeping change
to the language is the function prototype borrowed from the C++ language.
By specifying for each function the number and types of its parameters, not
only does every regular compile get the benefits of argument and parameter
checks (similar to those of lint) for each function call,
but arguments are automatically converted (just as with an assignment) to
the type expected by the function. The 1990 ISO C standard includes rules
that govern the mixing of old- and new-style function declarations since there
are many, many lines of existing C code that could and should be converted
to use prototypes.

6.2.1 Writing New Code

When you write an entirely new program, use new-style function declarations
(function prototypes) in headers and new-style function declarations and definitions
in other C source files. However, if there is a possibility that someone will
port the code to a machine with a pre-ISO C compiler, we suggest you use the
macro __STDC__ (which is defined only for ISO C compilation
systems) in both header and source files. Refer to 6.2.3 Mixing Considerations for an example.

An ISO C-conforming compiler must issue a diagnostic whenever two incompatible
declarations for the same object or function are in the same scope. If all
functions are declared and defined with prototypes, and the appropriate headers
are included by the correct source files, all calls should agree with the
definition of the functions. This protocol eliminates one of the most common
C programming mistakes.

6.2.2 Updating Existing Code

If you have an existing application and want the benefits of function
prototypes, there are a number of possibilities for updating, depending on
how much of the code you would like to change:

Recompile without making any changes.

Even with
no coding changes, the compiler warns you about mismatches in parameter type
and number when invoked with the– v option.

Add function prototypes just to the headers.

All
calls to global functions are covered.

Add function prototypes to the headers and start each source
file with function prototypes for its local (static) functions.

All
calls to functions are covered, but doing this requires typing the interface
for each local function twice in the source file.

Change all function declarations and definitions to use function
prototypes.

For most programmers, choices 2 and 3 are probably the best cost/benefit
compromise. Unfortunately, these options are precisely the ones that require
detailed knowledge of the rules for mixing old and new styles.

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:

char

signed char

unsigned char

float

short

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 unsignedshort, 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 unsignedshort 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 unsignedchar and unsignedshort 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:

-Xs and– Xt should
use unsignedint

–Xa and– Xc should
use int

The best approach is to change the old-style definition to specify either int or unsignedint 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: