Compaq C
Compaq C
Language Reference Manual
Chapter 4
Declarations
Declarations are used to introduce the identifiers used in a program
and to specify their important attributes, such as type, storage class,
and identifier name. A declaration that also causes storage to be
reserved for an object or that includes the body of a function, is
called a definition.
Section 4.1 covers general declaration syntax rules, Section 4.2
discusses initialization, and Section 4.3 describes external
declarations.
The following kinds of identifiers can be declared. See the associated
section for information on specific declaration and initialization
syntax. Functions are discussed in Chapter 5.
Note
Preprocessor macros created with the
#define
directive are not declarations. Chapter 8 has information on
creating macros with preprocessor directives.
|
4.1 Declaration Syntax Rules
The general syntax of a declaration is as follows:
declaration:
declaration-specifiers init-declarator-listopt;
|
declaration-specifiers:
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
type-qualifier declaration-specifiersopt
|
init-declarator-list:
init-declarator
init_declarator-list , init-declarator
|
init-declarator:
declarator
declarator = initializer
|
Note the following items about the general syntax of a declaration:
- The storage-class-specifier, type-qualifier, and
type-specifier can be listed in any order. All are optional,
but, except for function declarations, at least one such specifier or
qualifier must be present. Placing the storage-class-specifier anywhere
but at the beginning of the declaration is an obsolete style.
- Storage-class keywords are
auto
,
static
,
extern
, and
register
.
- Type qualifiers are
const
and
volatile
.
- The declarator is the name of the object or function being
declared. A declarator can be as simple as a single identifier, or can
be a complex construction declaring an array, structure, pointer,
union, or function (such as
*x
,
tree()
, and
treebar[10]
).
A full declarator is a declarator that is not part of
another declarator. The end of a full declarator is a sequence point.
If the nested sequence of declarators in a full declarator contains a
variable-length array type, the type specified by the full declarator
is said
to be variably modified.
- Initializers are optional and provide the initial value of
an object. Initializers can be a single value or a brace-enclosed list
of values, depending on the type of object being declared.
- A declaration determines the beginning of an identifier's scope.
- An identifier's linkage is determined by the declaration's
placement and its specified storage class.
Consider the following example:
volatile static int data = 10;
|
This declaration shows a qualified type (a data type with a type
qualifier -- in this case,
int
qualified by
volatile
), a storage class (
static
), a declarator (
data
), and an initializer (
10
). This declaration is also a definition, because storage is reserved
for the data object
data
.
The previous example is simple to interpret, but complex declarations
are more difficult. See your platform-specific Compaq C
documentation for more information about interpreting C declarations.
The following semantic rules apply to declarations:
- Empty declarations are illegal; declarations must contain at least
one declarator, or specify a structure tag, union tag, or the members
of an enumeration.
- Each declarator declares one identifier. There is no limit to the
number of declarators in a declaration.
- At most, one storage-class specifier can be used in each object
declaration. If none is provided, the
auto
storage class is assigned to objects declared inside a function
definition, and the
extern
class is assigned to objects declared outside of a function definition.
- The only allowable (and optional) storage class for declaration of
a function with block scope is
extern
.
- If no type-specifier is present, the default is
signed int
.
- A declarator is usable only over a certain range of the program,
determined by the declarator's scope. The duration of its storage
allocation is dependent on its storage class. See Section 2.3 for
more information on scope and Section 2.10 for more information on
storage classes.
- The usefulness of an identifier can be limited by its visibility,
which can be hidden in some parts of the program. See Section 2.4 for
more information on visibility.
- All declarations in the same scope that refer to the same object or
function must have compatible types.
- If an object has no linkage, there can be no more than one
declaration of the object with the same scope and in the same name
space. Objects without linkage must have their type completed by the
end of the declaration, or by the final initializer (if it has one).
Section 2.8 describes linkage.
Storage Allocation
Storage is allocated to a data object in the following circumstances:
- If the object has no linkage, storage is allocated upon declaration
of the object. If a block scope object with
auto
or
register
storage class is declared, storage is deallocated at the end of the
block.
- If the object has internal linkage, storage is allocated upon the
first definition of the object.
- If the object has external linkage, storage is allocated upon
initialization of the object, which must occur only once for each
object. If an object has only a tentative definition (see
Section 2.9), the compiler acts as though there were a file scope
definition of the object with an initializer of zero. Section 2.8
describes linkage in detail.
Note
The compiler does not necessarily allocate distinct variables to memory
locations according to the order of declaration in the source code.
Furthermore, the order of allocation can change as a result of
seemingly unrelated changes to the source code, command-line options,
or from one version of the compiler to the next - it is essentially
unpredictable. The only way to control the placement of variables
relative to each other is to make them members of the same
struct
type.
|
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
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.
privacy and legal statement