United States    
COMPAQ STORE | PRODUCTS |
SERVICES | SUPPORT | CONTACT US | SEARCH
Compaq C

Compaq C
Language Reference Manual


Previous Contents Index

  1. Delimit an array row initialization with braces.
  2. Delimit a structure initialization with braces.
  3. Delimit an array initialization with braces.

Example 4-1 writes the following output to the standard output:


row/col  ch      i            c 
------------------------------------- 
[0][0]:  a       1       3.000000e+10 
[0][1]:  b       2       4.000000e+10 
[0][2]:  c       3       5.000000e+10 
[1][0]:          0       0.000000e+00 
[1][1]:          0       0.000000e+00 
[1][2]:          0       0.000000e+00 

Note

See Section 4.9 for a description of initializers with designations for arrays and structures.

4.8.5 Initializing Unions

Unions are initialized with a brace-enclosed initializer that initializes only the first member of the union. For example:


static union 
  { 
     char ch; 
     int i; 
     float c; 
  } letter = {'A'}; 

Unions with the auto storage class may also be initialized with an expression of the same type as the union. For example:


main () 
{ 
union1 { 
    int i; 
    char ch; 
    float c; 
  } number1 = { 2 }; 
 
auto union2 
  { 
    int i; 
    char ch; 
    float c; 
  } number2 = number1; 
} 

4.9 Initializers with Designations

In conformance with ISO/IEC CD 9899 (SC22 N2620), otherwise known as CD1 of C9x, the in-progress revision to the ANSI/ISO C standard, Compaq C supports the use of designations in the initialization of arrays and structures. (Note that designations are not supported in the common C, VAX C, and Strict ANSI89 modes of the compiler.)

4.9.1 Current Object

C9x initializers introduce the concept of a current object and a designation.

The current object is the next thing to be initialized during the initialization of an array or structure.

A designation provides a way to set the current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the object: array elements in increasing subscript order, and structure members in declaration order.

So for an array, the first current object is a[0] when initialization begins; as each initializer is used, the current object is bumped to the next initializer, in increasing subscript order.

Similarly, for a structure, the current object is the first declaration within the structure when initialization begins; as each initializer is used, the current object is bumped to the next initializer, in declaration order.

4.9.2 Designations

The C9x Standard allows brace-enclosed initializer lists to contain designations, which specify a new current object. The syntax for a designation is:


        designation: 
                designator-list = 
 
        designator-list: 
                designator 
                designator-list designator 
 
        designator: 
                [ constant-expression ] 
                . identifier 

A designator within a designation causes the following initializer to begin initialization of the object described by the designator. Initialization then continues forward, in order, beginning with the next object after that described by the designator.

For an array, a designator looks like this:

[ integral-constant-expression ]

If the array is of unknown size, any nonnegative value is valid.

For a structure, a designator looks like this:

.identifier

Where identifier is a member of the structure.

4.9.3 Examples

The old way of initializing arrays and structures is still supported. However, the use of designators can simplify coding of initializer lists and better accommodate future changes you might want to make to arrays and structures in your application.

  1. Using designators, array elements can be initialized to nonzero values without depending on their order:


    int a[5] = { 0, 0, 0, 5 };  // Old way 
     
    int a[5] = { [3]=5 };       // New way 
    

    The designator [3] initializes a[3] to 5.

  2. Structure members can be initialized to nonzero values without depending on their order. For example:


     typedef struct { 
          char flag1; 
          char flag2; 
          char flag3; 
           int data1; 
           int data2; 
           int data3; 
           } Sx; 
     
    Sx = { 0, 0, 0, 0, 6 };   // Old way 
     
    Sx = { .data2 = 6 };      // New way 
    

    Designator .data2 initializes structure member .data2 to 6.

  3. Another example of using designators in an array:


    int a[10] = { 1, [5] = 20, 10 }; 
    

    In this example, the array elements are initialized as follows:


    a[0]=1 
    a[1] through a[4] = 0 
    a[5] = 20 
    a[6] = 10 
    a[7] through a[9] = 0 
    

  4. Future changes to structures can be accommodated without changing their initializer lists:


     typedef struct { 
          char flag1; 
          char flag2; 
          char flag3; 
           int data1; 
           int data2; 
           int data3; 
           } Sx; 
     
    Sx = { 1, 0, 1, 65, 32, 18 };   // Old way 
     
    Sx = { .flag1=1, 0, 1, .data1=65, 32, 18 }; // New way 
    

    Use of designators .flag1 and .data1 allows for future insertion of additional flags in front of .flag1 or between flag3 and data1.
    Designators do not have to be in order. For example, the following two initializer lists are equivalent:


    Sx = { .data1=65, 32, 18, .flag1=1, 0, 1 }; 
     
    Sx = { .flag1=1, 0, 1, .data1=65, 32, 18 }; 
    

  5. Space can be "allocated" from both ends of an array by using a single designator:


    int a[MAX] = 
    { 
        1, 3, 5, 7, 9, [MAX - 5] = 8, 6, 4, 2, 0 
    };      
    

    In this example, if MAX is greater than 10, there will be some zero-valued elements in the middle; if it is less than 10, some of the values provided by the first five initializers will be overridden by the second five.

  6. Designators can be nested:


    struct { int a[3], b } w[] = 
    { [0].a = {1}, [1].a[0] = 2 }; 
    

    This initialization is equivalent to the following:


    w[0].a[0]=1; 
    w[1].a[0]=2; 
    

  7. Another example of nesting designators:


    struct { 
         int a; 
         struct { 
              int b 
              int c[10] 
         }x; 
    }y = {.x = {1, .c = {[5] = 6, 7 }}}    
    

    This initialization is equivalent to the following:


    y.x.b = 1; 
    y.x.c[5] = 6; 
    y.x.c[6] = 7; 
    

4.10 Declaring Tags

The following syntax declares the identifier tag as a structure, union, or enumeration tag. If this tag declaration is visible, a subsequent reference to the tag substitutes for the declared structure, union, or enumerated type. Subsequent references of the tag in the same scope (visible declarations) must omit the bracketed list. The syntax of a tag is:

struct tag { declarator-list }


union tag { declarator-list }


enum tag { enumerator-list }

If the tag is declared without the complete structure or union declaration, it refers to an incomplete type. Incomplete enumerated types are illegal. An incomplete type is valid only to specify an object where the type is not required; for example, during type definitions and pointer declarations. To complete the type, another declaration of the tag in the same scope (but not within an enclosed block), defines the content.

The following construction uses the tag test to define a self-referencing structure.


struct test { 
 float height; 
 struct test *x, *y, *z; 
}; 

Once this declaration is given, the following declaration declares s to be an object of type struct test and sp to be a pointer to an object of type struct test :


struct test s, *sp; 

Note

The keyword typedef can also be used in an alternative construction to do the same thing:


typedef struct test tnode; 
struct test { 
     float height; 
     tnode *x, *y, *z; 
}; 
tnode s, *sp; 

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:

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:

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:

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:

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

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.


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement