1.11 Functions

Every C program contains at least the function main(), which is the first function executed when the program starts. All other functions are subroutines.

The definition of a function lists the statements it executes. Before a function can be called in a given translation unit, it must be declared. A function definition also serves as a declaration of the function. The declaration of a function informs the compiler of its return type. For example:

extern double pow();

Here pow() is declared as a function that returns a value with type double. Because function names are external names by default, the storage class specifier extern can also be omitted.

In ANSI C99, implicit function declarations are no longer permitted. Formerly, calls to undeclared functions were allowed, and the compiler implicitly assumed in such cases that the function returned a value of type int.

The declaration of the function pow() in the example above contains no information about the number and type of the function's parameters. Hence the compiler has no way of testing whether the arguments supplied in a given function call are compatible with the function's parameters. This missing information is supplied by a function prototype.

1.11.1 Function Prototypes

A function prototype is a declaration that indicates the types of the function's parameters as well as its return value. For example:

double pow( double, double );    // prototype of pow()

This prototype informs the compiler that the function pow() expects two arguments of type double, and returns a result of type double. Each parameter type may be followed by a parameter name. This name has no more significance than a comment, however, since its scope is limited to the function prototype itself. For example:

double pow( double base, double exponent );

Functions that do not return any result are declared with the type specifier void. For example:

void func1( char *str ); // func1 expects one string
                         // argument and has no return 
                         // value.

Functions with no parameters are declared with the type specifier void in the parameter list:

int  func2( void );     // func2 takes no arguments and
                        // returns a value with type int.

Function declarations should always be in prototype form. All standard C functions are declared in one (or more) of the standard header files. For example, math.h contains the prototypes of the mathematical functions, such as sin(), cos(), pow(), etc., while stdio.h contains the prototypes of the standard input and output functions.

1.11.2 Function Definitions

The general form of a function definition is:

[storage_class] [type] name( 
[parameter_list] ) // function declarator
{
    /* declarations, statements */      // function body
}

storage_class

One of the storage class specifiers extern or static. Because extern is the default storage class for functions, most function definitions do not include a storage class specifier.

type

The type of the function's return value. This can be either void or any other type, except an array.

name

The name of the function.

parameter_list

The declarations of the function's parameters. If the function has no parameters, the list is empty.

Here is one example of a function definition:

long sum( int arr[], int len )// Find the sum of the first 
{                         // len elements of the array arr
  int i;
  long result = 0;       
 
  for( i = 0;  i < len;  ++i )
     result += (long)arr[i];
  return result;
}

Because by default function names are external names, the functions of a program can be distributed among different source files, and can appear in any sequence within a source file.

Functions that are declared as static, however, can only be called in the same translation unit in which they are defined. But it is not possible to define functions with block scope—in other words, a function definition cannot appear within another function.

The parameters of a function are ordinary variables whose scope is limited to the function. When the function is called, they are initialized with the values of the arguments received from the caller.

The statements in the function body define what the function does. When the flow of execution reaches a return statement or the end of the function body, control returns to the calling function.

A function that calls itself, directly or indirectly, is called recursive. C permits the definition of recursive functions, since variables with automatic storage class are created anew—generally in stack memory—with each function call.

The function declarator shown above is in prototype style. Today's compilers still support the older Kernighan-Ritchie style, however, in which the parameter identifiers and the parameter type declarations are separate. For example:

long sum( arr, len )  // Parameter identifier list
int arr[], len;       // Parameter declarations 
{ ... }               // Function body

In ANSI C99, functions can also be defined as inline. The inline function specifier instructs the compiler to optimize the speed of the function call, generally by inserting the function's machine code directly into the calling routine. The inline keyword is prefixed to the definition of the function:

inline int max( int x, int y )
{  return  ( x >= y ? x : y ); }

If an inline function contains too many statements, the compiler may ignore the inline specifier and generate a normal function call.

An inline function must be defined in the same translation unit in which it is called. In other words, the function body must be visible when the inline "call" is compiled. It is therefore a good idea to define inline functions—unlike ordinary functions—in a header file.

Inline functions are an alternative to macros with parameters. In translating a macro, the preprocessor simply substitutes text. An inline function, however, behaves like a normal function—so that the compiler tests for compatible arguments, for example—but without the jump to and from another code location.

1.11.3 Function Calls

A function call is an expression whose value and type are those of the function's return value.

The number and the type of the arguments in a function call must agree with the number and type of the parameters in the function definition. Any expression, including constants and arithmetic expressions, may be specified as an argument in a function call. When the function is called, the value of the argument is copied to the corresponding parameter of the function! For example:

double x=0.5, y, pow();    // Declaration
y = pow( 1.0 + x, 2.5 );   // Call to pow() yields 
                           // the double value (1.0+x)2.5

In other words, the arguments are passed to the function by value. The function itself cannot modify the values of the arguments in the calling function: it can only access its local copy of the values.

In order for a function to modify the value of a variable directly, the caller must give the function the address of the variable as an argument. In other words, the variable must be passed to the function by reference. Examples of functions that accept arguments by reference include scanf(), time(), and all functions that have an array as one of their parameters. For example:

double swap( double *px, double *py ) // Exchange values
                                      // of two variables
{ double z = *px; *px = *py; *py = z; }

The arguments of a function are subject to implicit type conversion:

·         If the function was declared in prototype form (as is usually the case), each argument is converted to the type of the corresponding parameter, as for an assignment.

·         If no prototype is present, integer promotion is performed on each integer argument. Arguments of type float are converted to double.

1.11.4 Functions with Variable Numbers of Arguments

Functions that can be called with a variable number of arguments always expect a fixed number of mandatory arguments—at least one is required—and a variable number of optional arguments. A well-known example is the function printf(): the format string argument is mandatory, while all other arguments are optional. Internally, printf() determines the number and type of the other arguments from the information in the format string.

In the function declarator, optional arguments are indicated by three dots (...). For example:

int printf( char *str, ... );     // Prototype

In the function definition, the optional arguments are accessed through an object with the type va_list, which contains the argument information. This type is defined in the header file stdarg.h, along with the macros va_start, va_arg, and va_end, which are used to manage the arguments.

In order to read the optional arguments, the function must carry out the following steps:

1.       Declare an object of type va_list. In the following example, this object is named arglist.

2.       Invoke the macro va_start to prepare the arglist object to return the first optional argument. The parameters of va_start are the arglist object and the name of the last mandatory parameter.

3.       Invoke the macro va_arg with the initialized arglist object to obtain each of the optional arguments in sequence. The second parameter of va_arg is the type of the optional argument that is being obtained.

After each invocation of the va_arg macro, the arglist object is prepared to deliver the first optional argument that has not yet been read. The result of va_arg has the type specified by the second argument.

4.       After reading out the argument list, the function should invoke the va_end macro before returning control to the caller. The only parameter of va_end is the arglist object.

Following is an example of a function, named max, that accepts a variable number of arguments:

// Determine the maximum of a number of positive integers. 
// Parameters: a variable number of positive values of
// type unsigned int. The last argument must be 0.
// Return value: the maximum of the arguments 
 
#include <stdarg.h>
unsigned int max( unsigned int first, ... )
{
   unsigned int maxarg, arg;
   va_list arglist;  // The optional-argument 
                     // list object
   va_start( arglist, first ); // Set arglist to deliver
                               // the first optional
                               // argument  
    arg = maxarg = first;
    while ( arg != 0 )
    { arg = va_arg( arglist, unsigned );// Get an argument
       if ( arg > maxarg )  maxarg = arg;
    }
    va_end( arglist );         // Finished reading the 
                               // optional arguments 
    return maxarg; 
}