United States

Compaq C

# Compaq C
Language Reference Manual

Previous | Contents | Index

# 4.11 Declaring Type Definitions

In a declaration whose storage-class specifier is `typedef` , each declarator defines a `typedef` name that specifies an alias for the stated type. A `typedef` declaration does not introduce a new type, but only introduces a synonym for the stated type. For example:

    typedef int integral_type;
    integral_type x;

In the previous example, `integral_type` is defined as a synonym for `int` , and so the following declaration of `x` declares `x` to be of type `int` . Type definitions are useful in cases where a long type name (such as some forms of structures or unions) benefits from abbreviation, and in cases where the interpretation of the type can be made easier through a type definition.

A `typedef` name shares the same name space as other identifiers in ordinary declarators. If an object is redeclared in an inner scope, or is declared as a member of a structure or union in the same or inner scope, the type specifiers cannot be omitted from the inner declaration. For example:

    typedef signed int t;
    typedef int plain;
    struct tag {
       unsigned t:4;
       const t:5;
       plain r:5;
    };

It is evident that such constructions are obscure. The previous example declares a `typedef` name `t` with type `signed int` , a `typedef` name `plain` with type `int` , and a structure with three bit-field members, one named `t` , another unnamed member, and a third member named `r` . The first two bit-field declarations differ in that `unsigned` is a type specifier, which forces `t` to be the name of a structure member by the rule previously given. The second bit-field declaration includes `const` , a type qualifier, which only qualifies the still-visible `typedef` name `t` .

The following example shows additional uses of the `typedef` keyword:

    typedef int miles, klicksp(void);
    typedef struct { double re, im; } complex;
       .
       .
       .
    miles distance;
    extern klicksp *metricp;
    complex x;
    complex z, *zp;

All of the code shown in the previous example is valid. The type of `distance` is `int` , the type of `metricp` is a pointer to a function with no parameters returning `int` , and the type of `x` and `z` is the specified structure. `zp` is a pointer to the structure.

It is important to note that any type qualifiers used with a `typedef` name become part of the type definition. If the `typedef` name is later qualified with the same type qualifier, an illegal construction results. For example:

    typedef const int x;
    const x y;            /*  Illegal -- duplicate qualifier used  */

# Chapter 5
Functions

A C program is a collection of user-defined and system-defined functions. Functions provide a convenient way to break large computing tasks into smaller ones, which helps in designing modular programs that are easier to understand and maintain. A function contains zero or more statements to be executed when it is called, can be passed zero or more arguments, and can return a value.

This chapter discusses the following information about C functions:

- Function calls ( Section 5.1)
- Function types ( Section 5.2)
- Function definitions ( Section 5.3)
- Function declarations ( Section 5.4)
- Function prototypes ( Section 5.5)
- Parameters and arguments ( Section 5.6)

# 5.1 Function Calls

A function call is a primary expression, usually a function identifier followed by parentheses, that is used to invoke a function. The parentheses contain a (possibly empty) comma-separated list of expressions that are the arguments to the function. The following is an example of a call to the function `power` , assuming this function is appropriately defined:

    main()
    {
       .
       .
       .
    y = power(x,n);                     /* function call */
    }

See Section 6.3.2 for more information on function calls.

# 5.2 Function Types

A function has the derived type "function returning type". The type can be any data type except array types or function types, although pointers to arrays and functions can be returned. If the function returns no value, its type is "function returning `void` ", sometimes called a void function. A void function in C is equivalent to a procedure in Pascal or a subroutine in FORTRAN. A non-void function in C is equivalent to a function in these other languages.

Functions can be introduced into a program in one of two ways:

- A function definition can create a function designator, define its parameters and their type, define the type of its return value, and supply the body of the function. In the following example, `power` is a function returning `int` :

      int power(int base, int exp)
      {
       int n=1;

       if (exp < 0)
       {
        printf ("Error: Cannot handle negative exponent\n");
        return -1;
       }

       for ( ; exp; exp--)
           n = base * n;

       return n;
      }

  See Section 5.3 for more information on function definitions.
- A function declaration announces the properties of a function defined elsewhere. In the following example, the function `main` declares and calls the function `power` ; the definition of the function, where the code is defined, exists elsewhere:

      main()
      {
      int power(int base, int exp);       /* function declaration  */
      int x, n, y;
         .
         .
         .
      y = power(x,n);                     /* function call         */
      }

  This style of function declaration, in which the parameters are declared in a parameter type list, is called a function prototype. Function prototypes require the compiler to check function arguments for consistency with their parameters, and to convert arguments to the declared types of the parameters.
  See Sections 5.4 and 5.5 for more information on function declarations and prototypes.

# 5.3 Function Definitions

A function definition includes the code for the function. Function definitions can appear in any order, and in one source file or several, although a function cannot be split between files. Function definitions cannot be nested.

A function definition has the following syntax:

function-definition:

`declaration-specifiersopt declarator declaration-listopt compound-statement`

> ### declaration-specifiers

> The declaration-specifiers (storage-class-specifier, type-qualifier, and type-specifier) can be listed in any order. All are optional.

> By default, the storage-class-specifier is `extern` . The `static` specifier is also allowed. See Section 2.10 for more information on storage-class specifiers.

> ANSI allows the type-qualifier to be `const` or `volatile` , but either qualifier applied to a function return type is meaningless, because functions can only return rvalues and the type qualifiers apply only to lvalues.

> The type-specifier is the data type of the value returned by the function. If no return type is specified, the function is declared to return a value of type `int` . A function can return a value of any type except "array of type" or "function returning type". Pointers to arrays and functions can be returned. The value returned, if any, is specified by an expression in a `return` statement. Executing a `return` statement terminates function execution and returns control to the calling function. For functions that return a value, any expression with a type compatible with the function's return type can follow `return` using the following format:

> `return expression;`

> If necessary, the expression is converted to the return type of the function. Note that the value returned by a function is not an lvalue. A function call, therefore, cannot constitute the left side of an assignment operator.

> The following example defines a function returning a character:

>     char letter(char param1)
>     {
>        .
>        .
>        .
>        return param1;
>     }

> The calling function can ignore the returned value. If no expression is specified after `return` , or if a function terminates by encountering the right brace, then the return value of the function is undefined. No value is returned in the case of a `void` function.

> If a function does not return a value, or if the function is always called from within a context that does not require a value, a return type of `void` should be specified:

>     void message()
>     {
>        printf("This function has no return value.");
>        return;
>     }

> Specifying a return type of `void` in a function definition or declaration generates an error under the following conditions:

> - If the function attempts to return a value, an error occurs at the offending `return` statement.
> - If the `void` function is called in a context that requires a value, an error occurs at the function call site.

> ### declarator

> The declarator specifies the name of the function being declared. A declarator can be as simple as a single identifier, such as `f1` in the following example:

>     int f1(char p2)

> In the following example, `f1` is a "function returning `int` ". A declarator can also be a more complex construct, as in the following example:

>     int (*(*fpapfi(int x))[5])(float)

> In this example, `fpapfi` is a "function (taking an `int` argument) returning a pointer to an array of five pointers to functions (taking a `float` argument) returning `int` ". See Chapter 4 for information on specific declarator syntax.

> The declarator (function) need not have been previously declared. If the function was previously declared, the parameter types and return type in the function definition must be identical to the previous function declaration.

> The declarator can include a list of the function's parameters. In Compaq C, up to 253 parameters can be specified in a comma-separated list enclosed in parentheses. Each parameter has the `auto` storage class by default, although `register` is also allowed. There is no semicolon after the right parenthesis of the parameter list.

> There are two methods of specifying function parameters:

> - The new or prototype style, which includes a parameter type list. For example:

>       int f1(char a, int b)
>       {
>       function body
>       }

> - The old style, which includes an identifier list; the parameter types are defined in a separate declaration-list within the function definition, before the left brace that begins the function body. For example:

>       int f1(a, b)
>       char a;
>       int b;
>       { function body
>       }

>   Any undeclared parameters are assumed to be of type `int` .

> A function definition with no parameters is defined with an empty parameter list. An empty parameter list is specified in either of two ways:

> - Using the keyword `void` if the prototype style is used. For example:

>       char msg(void)
>       {
>       return 'a';
>       }

> - Using empty parentheses if the old style is used. For example:

>       char msg()
>       {
>       return 'a';
>       }

> A function defined using the prototype style establishes a prototype for that function. The prototype must agree with any preceding or following declarations of the same function.

> A function defined using the old style does not establish a prototype, but if a prototype exists because of a previous declaration for that function, the parameter declarations in the definition must exactly match those in the prototype after the default argument promotions are applied to the parameters in the definition.

> Avoid mixing old style and prototype style declarations and definition for a given function. It is allowed but not recommended.

> See Section 5.6 for more information on function parameters and arguments. See Section 5.5 for more information on function prototypes.

> ### compound-statement

> The compound-statement is the group of declarations and statements surrounded by braces in a function or loop body. This compound statement is also called the function body. It begins with a left brace ({) and ends with a right brace (}), with any valid C declarations and statements in between. One or more `return` statements can be included, but they are not required.

# 5.4 Function Declarations

A function can be called without declaring it if the function's return value is `int` (although this practice is not recommended due to the loss of type-checking capability; all functions should be declared). If the return value is anything else, and if the function definition is located after the calling function in the source code, the function must be declared before calling it. For example:

    char lower(int c);                    /* Function declaration */

    caller()                              /* Calling function     */
    {
    int c;
    char c_out;
          .
          .
          .
    c_out = lower(c);                     /* Function call        */

    }

    char lower(int c_up)                  /* Function definition  */
    {
       .
       .
       .
    }

If the function definition for `lower` was located before the function `caller` in the source code, `lower` would not have to be declared again before calling it. In that case, the function definition would serve as its own declaration and would be in scope for any function calls from within all subsequently defined functions in the same source file.

Note that both the function definition and function declaration for `lower` are in the prototype style. Although C supports the old style of function declaration in which the parameter types are not specified in the function declarator, it is good programming practice to use prototype declarations for all user-defined functions in your program, and to place the prototypes before the first use of the function. Also note that it is valid for the parameter identifier in the function declaration to be different from the parameter identifier in the function definition.

In a function declaration, the `void` keyword should be used to specify an empty argument list. For example:

       char function_name(void);

As with function definitions, the `void` keyword can also be used in function declarations to specify the return value type for functions that do not return a value. For example:

    main()
    {
       void function_name( );
          .
          .
          .
    }
    void function_name( )
    { }

# 5.5 Function Prototypes

A function prototype is a function declaration that specifies the data types of its arguments in the parameter list. The compiler uses the information in a function prototype to ensure that the corresponding function definition and all corresponding function declarations and calls within the scope of the prototype contain the correct number of arguments or parameters, and that each argument or parameter is of the correct data type.

Prototypes are syntactically distinguished from the old style of function declaration. The two styles can be mixed for any single function, but this is not recommended. The following is a comparison of the old and the prototype styles of declaration:

Old style:

- Functions can be declared implicitly by their appearance in a call.
- Arguments to functions undergo the default conversions before the call.
- The number and type of arguments are not checked.

Note The Compaq C compiler will warn about old-style function declarations only in strict ANSI standard mode, or when the check compiler option is specified.

Prototype style:

- Functions are declared explicitly with a prototype before they are called. Multiple declarations must be compatible; parameter types must agree exactly.
- Arguments to functions are converted to the declared types of the parameters.
- The number and type of arguments are checked against the prototype and must agree with or be convertible to the declared types. Empty parameter lists are designated using the `void` keyword.
- Ellipses are used in the parameter list of a prototype to indicate that a variable number of parameters are expected.

## 5.5.1 Prototype Syntax

A function prototype has the following syntax:

function-prototype-declaration:

`declaration-specifiersopt declarator;`

The declarator includes a parameter type list, which specifies the types of, and can declare identifiers for, the parameters of the function.

A parameter type list can consist of a single parameter of type `void` to specify that the function has no parameters.

A parameter type list can contain a member that is a variable-length array, specified by the `[*]` notation.

In its simplest form, a function prototype declaration might have the following format:

`storage_classopt return_typeopt function_name ( type1 parameter1, ..., typen parametern );`

Consider the following function definition:

    char  function_name( int lower, int *upper, char (*func)(), double y )
    { }

The corresponding prototype declaration for this function is:

    char  function_name( int lower, int *upper, char (*func)(), double y );

A prototype is identical to the header of its corresponding function definition specified in the prototype style, with the addition of a terminating semicolon (;) or comma (,), as appropriate (depending on whether the prototype is declared alone or in a multiple declaration).

Function prototypes need not use the same parameter identifiers as in the corresponding function definition because identifiers in a prototype have scope only within the identifier list. Moreover, the identifiers themselves need not be specified in the prototype declaration; only the types are required.

For example, the following prototype declarations are equivalent:

    char  function_name( int lower, int *upper, char (*func)(), double y );
    char  function_name( int a, int *b, char (*c)(), double d );
    char  function_name( int, int *, char (*)(), double );

Though not required, identifiers should be included in prototypes to improve program clarity and increase the type-checking capability of the compiler.

Variable-length argument lists are specified in function prototypes with ellipses. At least one parameter must precede the ellipses. For example:

    char  function_name( int lower, ... );

Data-type specifications cannot be omitted from a function prototype.

The C99 standard permits the keyword `static` to be used within the outermost array bound of a formal parameter in a prototype function declaration. The effect is to assert to the compiler that at each call to the function, the corresponding actual argument will provide access to at least as many array elements as specified in the declared array bound. Consider the following two function definitions:

    void foo(int a[1000]){ ... }
    void bar(int b[static 1000]) { ...}

The declaration of `foo` is absolutely equivalent to one that declares `a` to be `int *` . When compiling the body of `foo` , the compiler has no information about how many array elements might exist. The declaration of `bar` differs in that the compiler can assume that at least 1000 array elements exist and may be safely accessed. The intent is to provide a hint to the optimizer about what can be safely pre-fetched.

Previous | Next | Contents | Index

privacy and legal statement