United States

Compaq C

# Compaq C
Language Reference Manual

Previous | Contents | Index

# 4.2 Initialization

Initializers provide an initial value for objects, and follow this syntax:

initializer:

`assignment-expr { initializer-list } { initializer-list, }`

initializer-list:

`designation-opt initializer initializer-list, designation-opt initializer`

designation:

`designator-list =`

designator-list:

`designator designator-list designator`

designator:

`[ constant-expr ] . identifier`

Initialization of objects of each type is discussed in the following sections, but a few universal constraints apply to all initializations in C:

- The number of initializers cannot exceed the number of objects to be initialized. Initializers can number less than the number of objects to be initialized, in which case the remaining objects are initialized to zero.
- Constant expressions must be used in an initializer for an object that has static storage duration, or in an initializer list for an object that has an aggregate or union type.
- If an identifier's declaration has block scope, and the identifier has external or internal linkage, the declaration of the identifier cannot include an initializer.
- If an object that has static storage duration is not explicitly initialized, it is initialized implicitly as if every member with an arithmetic type were assigned 0, and every member with a pointer type were assigned a null pointer constant. If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.
- The initializer for a scalar object must be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression. The same type constraints and conversions apply as for simple assignment.
- If an aggregate object contains members that are aggregates or unions, or if the first member of a union is an aggregate or union, the initialization rules apply recursively to the aggregate members or contained unions. If an initializer list is used for an aggregate member or contained union, the initializers in that list initialize the members of the aggregate member or contained union. Otherwise, only enough initializers from the list are used to account for the object; any remaining members in the list are left to initialize the next member of the aggregate object. For example:

      struct t1 {
       int i;
       double d;
      };

      union t2 {
       int i;
       double d;
      };

      struct t3 {
       struct t1 s;
       union t2 u;
      };

      struct t3 st[] = { /* complete initializer */
       1, 2, 0, 4, 0, 0, 7, 0, 0
      };

  Given the previous declarations, the variable `st` is an array of 3 structures. Its initial contents are:

                  s      u
               ------    -
      st[0]:   1, 2.0,   0
      st[1]:   4, 0.0,   0
      st[2]:   7, 0.0,   0

  This variable can also be defined in the following ways---all four initializers are equivalent:

      struct t3 st[] = { /* partial initializer */
       1, 2, 0, 4, 0, 0, 7
      };

      struct t3 st[] = { /* nested and complete initializers */
       {1, 2, 0},
       {4, 0, 0},
       {7, 0, 0}
      };

      struct t3 st[] = { /* nested and partial initializers */
       {1, 2},
       {4},
       {7}
      };

  For initialization of arrays, structures, and unions, see Sections 4.7.1, 4.8.4, and 4.8.5.
- For a description of initializers with designations for arrays and structures, see Section 4.9.
- Variant structures and unions are initialized just like normal structures and unions. See Section 4.8.4 and Section 4.8.5 for more information.

C has historically allowed initializers to be optionally surrounded by extra braces (to improve formatting clarity, for instance). These initializers are parsed differently depending on the type of parser used. Compaq C uses the parsing technique specified by the ANSI standard, known as the top-down parse. Programs depending on a bottom-up parse of partially braced initializers can yield unexpected results. The compiler generates a warning message when it encounters unnecessary braces in common C compatibility mode or when the error-checking compiler option is specified on the command line.

# 4.3 External Declarations

An object declaration outside of a function is called an external declaration. Contrast this with an internal declaration, which is a declaration made inside a function or block; the declaration is internal to that function or block, and is visible only to that function or block. The compiler recognizes an internally declared identifier from the point of the declaration to the end of the block.

If an object's declaration has file scope and an initializer, the declaration is also an external definition for the object. A C program consists of a sequence of external definitions of objects and functions.

Any definition reserves storage for the entity being declared. For example:

    float fvalue = 15.0;                /* external definition  */
    main ()
    {
      int ivalue = 15;                  /* internal definition  */
    }

External data declarations and external function definitions take the same form as any data or function declaration (see Chapter 5 for standard function declaration syntax), and must follow these rules:

- The storage class of an object externally declared can be left unspecified, or it can be declared as `extern` or `static` (see Section 2.10). If it is unspecified, the default is the `extern` storage class, and linkage for the declared object is external. The type specifier may also be omitted, in which case the default type is `int` . Note that the storage-class-specifier, type-qualifier, and type-specifier cannot all be omitted from a declaration.
- If an object with external linkage is declared or used in an expression, there must be only one external definition for the identifier somewhere in the program. If the same object is declared more than once externally, the declarations must agree in type and linkage. (See Section 2.8.)
- If one or more of the declarations incompletely specify the object's type, and there exists one declaration of the object with completed type, all the declarations are taken to be in agreement with the completed type.
- The scope of external declarations persist to the end of the file in which they are declared, while internal declarations persist only to the end of the block in which they were declared. Data objects to be used within only one block should be declared in that block. The syntax for external definitions is the same as for all definitions. Function definitions can only occur at the external level.
- Externally declared `auto` and `register` objects are not permitted. Internally declared `auto` and `register` objects are not automatically initialized and, if not explicitly initialized, have the irrelevant value previously stored at their address. All `static` objects are automatically initialized to 0, if not explicitly initialized.

Note An external function can be called without previously declaring it in C, but this construction is not recommended because of the loss of type checking and subsequent susceptibility to bugs. If such a function call is made, the compiler will treat the function as if an external declaration of type `int` appeared in the block containing the call. For example:
    void function1()
    {
    int a,b;
    x (a,b);
    }
Here, the compiler will behave as if the declaration `extern int x();` appeared within the `function1` definition block.

The first declaration of an identifier in a compilation unit must specify, explicitly or by the omission of the `static` keyword, whether the identifier is internal or external. For each object, there can be only one definition. Multiple declarations of the same object may be made, as long as there are no conflicting or duplicate definitions for the same object.

An external object may be defined with either an explicit initialization or a tentative definition. A declaration of an object with file scope, without an initializer, and with a storage-class specifier other than `static` is a tentative definition. The compiler will treat a tentative definition as the object's only definition unless a complete definition for the object is found. As with all declarations, storage is not actually allocated until the object is defined.

If a compilation unit contains more than one tentative definition for an object, and no external definition for the object, the compiler treats the definition as if there were a file scope declaration of the object with an initializer of zero, with composite type as of the end of the compilation unit. See Section 2.7 for a definition of composite type.

If the declaration of an object is a tentative definition and has internal linkage, the declared type must not be an incomplete type. See Section 2.9 for examples of tentative definitions.

# 4.4 Declaring Simple Objects

Simple objects are objects with one of the basic data types. Therefore, a simple object can have an integral or floating-point type. Like all objects, simple objects are named storage locations whose values can change throughout the execution of the program. All simple objects used in a program must be declared.

A simple object declaration can be composed of the following items:

- Optional data-type specifier keywords
- Optional type-qualifier keywords ( `const` or `volatile` ). For example:

      const int *p;   /*  const qualifies the integer p points to   */
      int *const p;   /*  const qualifies the pointer p             */

- An optional storage-class keyword. If the storage-class keyword is omitted, there is a default storage class that depends on the location of the declaration in the program. The positions of the storage-class keywords and the data-type keywords are interchangeable, but placing the storage-class keyword anywhere but at the beginning of the declaration is an obsolete construction.
- Declarators, which list the names of the declared objects.
- Initializers giving the initial value of a simple object. An initializer for a simple object consists of an equal sign (=) followed by a single expression.

## 4.4.1 Initializing Simple Objects

An initializer for a simple object consists of an equal sign (=) followed by a single constant expression. For example:

    int x = 10;
    float y = ((12 - 2) + 25);

Here, the declaration both declares and defines the object `x` as an integer value initially equal to 10, and declares and defines the floating-point value `y` with an initial value of 35.

Without an initializer, the initial value of an `auto` object is undefined. A `static` object without explicit initialization is automatically initialized to 0. (If the object is a `static` array or structure, all members are initialized to 0.)

A block scope identifier with external or internal linkage (that is, declared using the `extern` or `static` keywords) cannot include an initializer in the declaration, because it is initialized elsewhere.

## 4.4.2 Declaring Integer Objects

Integer objects can be declared with the `int` , `long` , `short` , `signed` , and `unsigned` keywords. `char` can also be used, but only for small values. The following statements are examples of integer declarations:

    int x;        /*  Declares an integer variable x    */
    int y = 10;   /*  Declares an integer variable y    */
                  /*  and sets y's initial value to 10  */

Some of the keywords can be used together to explicitly state the allowed value range. For example:

    unsigned long int a;
    signed long;           /*  Synonymous with "signed long int"   */
    unsigned int;

Consider the range of values an integer object must be capable of representing when selecting the integral data type for the object. See Chapter 3 for more information on the size and range of integral data types.

## 4.4.3 Declaring Character Variables

Character objects are declared with the `char` keyword. The following example shows a character declaration with the initialization of a character object:

    char ch = 'a'; /* Declares an object ch with an initial value 'a' */

In C, character string literals are stored in arrays of type `char` . See Section 4.7 for more information on arrays.

## 4.4.4 Declaring Floating-Point Variables

When declaring floating-point objects, determine the amount of precision needed for the stored object. Single-precision or double-precision objects can be used. For single precision, use the `float` keyword. For double precision, use the `double` or `long double` keywords. For example:

    float x = 7.5;
    double y = 3.141596;

See your platform-specific Compaq C documentation for specific information on the range and precision of floating-point types.

# 4.5 Declaring Enumerations

An enumerated type is a user-defined integer type. An enumerated type defines enumeration constants, which are integral constant expressions with values that can be represented as integers. An enumerated type declaration follows this syntax:

enum-specifier:

`enum identifieropt { enumerator-list} enum identifieropt { enumerator-list , } enum identifier`

enumerator-list:

`enumerator enumerator-list, enumerator`

enumerator:

`enumeration-constant enumeration-constant = constant_expression`

In Compaq C, objects of type `enum` are compatible with objects of type `signed int` .

The following example shows the declaration of an enumeration type and an enumeration tag:

    enum shades
       {
          off, verydim, dim, prettybright, bright
       }  light;

This declaration defines the variable `light` to be of an enumerated type `shades` . `light` can assume any of the enumerated values.

The tag `shades` is the enumeration tag of the new type. `off` through `bright` are the enumeration constants with values 0 through 4. These enumeration constants are constant values and can be used wherever integer constants are valid.

Once a tag is declared, it can be used as a reference to that enumerated type, as in the following declaration, where the variable `light1` is an object of the enumerated data type `shades` :

    enum  shades  light1;

An incomplete type declaration of an enumerated type is illegal; for example:

    enum e;

An `enum` tag can have the same spelling as other identifiers in the same program in other name spaces. However, `enum` constant names share the same name space as variables and functions, so they must have unique names to avoid ambiguity.

Internally, each enumeration constant is associated with an integer constant; the compiler gives the first enumeration constant the value 0 by default, and the remaining enumeration constants are incremented by 1 for each succeeding value. Any enumeration constant can be set to a specific integer constant value. The enumeration constants following such a construct (unless they are also set to specific values) then receive values that are one greater than the previous value. Consider the following example:

    enum spectrum
       {
          red, yellow = 4, green, blue, indigo, violet
       }  color2 = yellow;

This declaration gives `red` , `yellow` , `green` , `blue` ,..., the values 0, 4, 5, 6,... Assigning duplicate values to enumeration constants is permitted.

The value of `color2` is an integer (4), not a string such as "red" or "yellow".

# 4.6 Declaring Pointers

Pointers are variables that contain the memory addresses of objects or functions. Pointer variables are declared as a pointer type by using the asterisk punctuator and the data type of the object pointed to, as shown in the following syntax:

pointer:

`* type-qualifier-listopt * type-qualifier-listopt pointer`

type-qualifier-list:

`type-qualifier type-qualifier-list type-qualifier`

By default, Compaq C pointers are 32 bits long on OpenVMS systems and 64 bits long on Tru64 UNIX systems. Although their defaults are different, both OpenVMS Alpha and Tru64 UNIX systems support 32-bit (short) and 64-bit (long) pointers. Compaq C provides qualifiers/switches and `#pragma` preprocessor directives to control pointer size.

The type-qualifier is either `const` , `volatile` , `__unaligned` (ALPHA), `__restrict` , or any combination thereof.

An object of pointer type is declared as in the following example:

    char *px;

In this example, identifier `px` is declared as a pointer to an object of type `char` . No type-qualifier is used in this example. The expression `*px` yields the `char` that `px` points to.

The following declarations show the difference between a variable pointer to a constant, a constant pointer to a variable, and a constant pointer to a constant object.

    const int *ptr_to_constant;     /*  pointer variable pointing
                                        to a const object         */
    int *const constant_ptr;        /*  constant pointer to a
                                        non-const object          */
    const int *const constant_ptr;  /*  Const pointer to a
                                        const object              */

The contents of an object pointed to by `ptr_to_constant` cannot be modified through that pointer, but `ptr_to_constant` itself can be changed to point to another `const` -qualified object. Similarly, the contents of the integer pointed to by `constant_ptr` can be modified, but `constant_ptr` itself will always point to the same location.

The declaration of the constant pointer `constant_ptr` can be clarified by including a definition for the type pointer to `int` . The following example declares `constant_ptr` as an object with type const-qualified pointer to `int` . The pointer's value (an address) is constant:

    typedef int *int_ptr;
    const int_ptr constant_ptr;

The `__unaligned` data-type qualifier can be used in pointer definitions on Alpha systems. to indicate to the compiler that the data pointed to is not properly aligned on a correct address. (To be properly aligned, the address of an object must be a multiple of the size of the type. For example, 2-byte objects must be aligned on even addresses.) (ALPHA)

When data is accessed through a pointer declared `__unaligned` , the compiler generates the additional code necessary to copy or store the data without causing alignment errors. It is best to avoid use of misaligned data altogether, but in some cases the usage may be justified by the need to access packed structures, or by other considerations. (ALPHA)

The `__restrict` data-type qualifier is used to designate a pointer as pointing to a distinct object, thus allowing compiler optimizations to be made (see Section 3.7.4).

Unless an `extern` or `static` pointer variable is explicitly initialized, it is initialized to a null pointer. A null pointer is a pointer value of 0. The contents of an uninitialized `auto` pointer are undefined.

Previous | Next | Contents | Index

privacy and legal statement