Previous Table of Contents Next


The line

return a;

is a simple return statement; it causes the current function to relinquish control back to its caller, and it also supplies the return value. The value in a return statement can be any expression; it is converted to the return type of the function if necessary. Many programmers parenthesize the expression, although this is not required.

In a void-valued function, of course, there is no return value, so the expression would be omitted from the return statement. A void-valued function may also return by “falling off the end”—that is, by having its control flow reach the final } of the function.

(The expression in a return statement is disallowed in void-valued functions. However, because type void did not always exist, the expression is optional, not mandatory, in other functions. Needless to say, it is usually an error for a function that should return a value to execute a return statement without one, although not all compilers complain about this error.)

It is possible for a function to accept a varying number of arguments. (An obvious example is printf.) Variable arguments are indicated by three dots (an ellipsis) at the end of the function’s parameter list:

return-type function-name ( fixed-arguments, …)
{
            body-of-function
}

The mechanism by which a function accesses its variable arguments is described in section 3.10.10. A variable-arguments function must accept at least one known argument, which means that the ellipsis cannot appear alone as the parameter list. There is no way to specify the type or types of the variable arguments; the caller must supply them correctly, and the function must somehow know their type(s) in order to fetch them correctly. (For example, printf intuits the expected number and type of its variable arguments by parsing its format string.)

An older, nonprototyped, “pre-ANSI” form of function definition is also permitted and is occasionally seen. The syntax looks like this:

return-type function-name ( parameter-name-list )
parameter-declarations
{
          local-variable-declarations
statements
}

Only the names of the parameters appear between the parentheses; their types are given by conventional-looking declarations between the close parenthesis and the opening brace of the function body. Named but undeclared parameters default to type int. In this older style, the beginning of the gcd function might look like this:

int gcd(a, b)
int a, b;
{
…

Also, in this old function-definition style, the compiler assumes that any parameters of types char, short int, or float are actually passed as int or double, according to the default argument promotions described in section 3.5.3.

3.5.2. Declaring Functions

It is usually appropriate to declare functions before calling them, to ensure that the compiler generates the correct calling sequences, and so that the compiler can check that the programmer has supplied the correct number (and type) of arguments. A complete declaration for a function takes the form of an external function prototype declaration:

extern return-type function-name ( parameter-type-list );

For example, a declaration of the gcd function of the preceding section might look like this:

extern int gcd(int, int);

The function declaration differs from the definition in three respects:

  It is preceded by the storage-class specifier extern.
  The parameter names are omitted.
  It is terminated by a semicolon where the brace-enclosed body in the definition would be.

In fact, only the third point is vital; the presence of the semicolon is sufficient for the compiler to distinguish function declarations from definitions. Accordingly, the keyword extern is optional in function declarations, and names for the parameters may be supplied, if desired. (Named parameters in function prototype declarations can be useful for documentary purposes.)

As for function definitions, an ellipsis () at the end of the parameter list in a function declaration indicates that the function accepts a varying number of additional arguments.

Corresponding to the pre-ANSI form of function definition, there is an old, nonprototyped form of function declaration, as well:

extern return-type function-name ( );

Only the return type of the function is specified; no information about the function’s expected parameter list is supplied. Unlike function definitions, the empty parentheses in an old-style external function declaration indicate an unspecified (quite possibly nonzero) number of arguments. To explicitly declare a function as accepting no arguments, the prototype form, with the keyword void in the place of the parameter list, must be used.

Due to the default argument promotions (mentioned in section 3.5.1 and described in section 3.5.3), the correct external function prototype declaration for a function defined in the old style but with “narrow” parameters must be written carefully. The correct prototype specifies the widened versions of the parameters. For example, the function

int oldfunc(x, y, z)
char x;
short y;
float z;
{ … }

has the corresponding external function prototype declaration

extern int oldfunc(int, int, double);

Function prototype declarations are optional in some circumstances and required in others (as discussed in section 3.5.3), but modern practice is to use them at all times. Function prototype declarations are usually arranged to appear at the beginning of each source file (supplying declarations for all functions called anywhere in that source file). Rather than typing multiple function declarations into many source files separately, it is usually convenient to type them into a separate header file once and insert them into each source file by means of header file inclusion, discussed in section 3.8.1. This practice is strongly recommended.


Previous Table of Contents Next