Previous Table of Contents Next


3.2.7. Type Qualifiers

The final piece of the declaration scheme is the type qualifier, which can be used to specify two attributes of a variable’s storage, in a way independent of and orthogonal to any storage-class. The two type qualifiers are

  const—Indicates that a location will not be written to during the running of the program, such that it may be placed in read-only storage. A const object may be assigned a value only by initialization. The compiler attempts to warn about attempts to modify const objects.
(A const object is, however, not a compile-time constant; it is a runtime object that will not be modified. A const object cannot be used where a constant expression is required, such as in an array dimension. This is another significant difference from C++.)
  volatile—Indicates that a location may change its value other than as written to by the program, or that it otherwise has peculiar access semantics. The canonical example of a volatile location is a memory-mapped I/O register, which may have a different value each time it is read from, or may cause output operations to be performed when it is written to. The effect of the volatile qualifier is typically to suppress optimizations that would rearrange or eliminate loads or stores of the affected variable.

Simple examples of these type qualifiers in use are

const float pi = 3.14;

and

volatile int diskcsr;

Type qualifiers are distinct from storage classes because, in complex pointer declarations, it is possible for either the pointer itself or the pointed-to location to be qualified. (See section 3.6.1.)

If a declaration includes a type qualifier, the type may be omitted, and defaults to int. Thus,

const x = 10;

declares a nonwritable int variable named x with the value 10.

3.2.8. Initialization

As mentioned in section 3.2.3, the declaration of a variable may contain an initial value, termed an initializer. The allowable forms of initializers (and the default initialization, in the absence of an explicit initializer) depend on the type and duration of the variable.

The initializer for a variable with static duration must be a constant expression, either a single constant or an expression composed ultimately of constants (see section 3.3.12). In general, the initializer is computed at compile time, and arrangements are made to copy the value into the variable at program startup. In the absence of an explicit initializer, a variable with static duration is initialized to 0, just as if the programmer had written “= 0”. (The initialization is to the value 0; the default initialization of a floating-point or pointer value is therefore not necessarily all-bits-0.)

The initializer for a variable with automatic duration may be any expression. The initializer is recomputed and reassigned each time the containing function is called (or control flow enters the top of the containing block). Any expression that could have been assigned to the variable using a conventional assignment may also appear as its initializer. In the absence of an explicit initializer, a variable with automatic duration is not initialized at all, and typically contains garbage. It is an error to attempt to use the value of an uninitialized local variable, but it is sometimes a subtle error, because in some cases the program may “happen” to work. (For example, most machines allocate local variables on a stack, and many operating systems provide programs with stack space that is initially all zero.)

It is also possible to initialize arrays; the initialization takes the form of a brace-enclosed, comma-separated list of values:

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

The individual initializers must all be constant expressions; they cannot contain function calls and the like. There must not be more initializers than there are elements in the array. If there are fewer, the remaining elements are initialized to 0 (as was the case for uninitialized static variables, and in this case, whether the array is static or automatic). When the declaration for an array includes an initialization list, the size of the array may be omitted, and the compiler computes it automatically it by counting the initializers. For example,

int b[] = {1, 2, 3};

declares an array of three elements, just as if the programmer had written int b[3].

The initializer for a character array may be a string literal:

char str[] = “Hello, world!”;

The interpretation is the obvious one: The array is initialized with characters from the string literal. As before, the array size is inferred from the size of the initializer (which includes the implicit trailing \0) if the declared size is missing, and the array is filled with trailing 0’s (i.e., \0 characters) if the explicit array size is larger than the initializer. (It is also possible, though rarely useful, for the explicitly given size to match the apparent length of the string literal initializer exactly, in which case the terminating \0 is omitted.)


Previous Table of Contents Next