Interdata_v6/usr/doc/cman/cman3
.bp
.SH
8. Declarations
.LP
Declarations are used within function definitions to specify the interpretation
which C gives to each identifier; they do not necessarily
reserve storage associated with the identifier.
Declarations have the form
.SY
declaration:
decl-specifiers declarator-list\*(op \fG;
.ES
The declarators in the declarator-list
contain the identifiers being declared.
The decl-specifiers
consist of a sequence of type and storage class specifiers.
.SY
decl-specifiers:
type-specifier decl-specifiers\*(op
sc-specifier decl-specifiers\*(op
.ES
The list must be self-consistent in a way described below.
.SH
8.1 Storage class specifiers
.LP
The sc-specifiers are:
.SY
sc-specifier:
.ft G
auto
static
extern
register
typedef
.ES
The
.Bd typedef
specifier does not reserve storage
and is called a `storage class specifier' only for syntactic convenience;
it is discussed in \(sc8.8.
.PP
The meanings of the various storage classes were discussed in \(sc4.
.PP
The
.Bd "auto, static,"
and
.Bd register
declarations also serve as definitions
in that they cause an appropriate amount of storage to be reserved.
In the \fGextern\fR case
there must be an external definition (\(sc10)
for the given identifiers
somewhere outside the function in which they are declared.
.PP
A
.Bd register
declaration is best thought of as an
.Bd auto
declaration, together with a hint to the compiler
that the variables declared will be heavily used.
.MC
Only the first few
(\*(pd: three; \*I: five)
such declarations are effective.
Moreover, only variables of certain types will be stored in registers;
on the \*(pd and \*I they are
.Bd int,
.Bd char,
or pointer.
.mc
One restriction applies to register variables:
the address-of operator
.Bd &
cannot be applied to them.
Smaller, faster programs can be expected if register declarations
are used appropriately,
but future developments
may render them unnecessary.
.PP
At most one sc-specifier may be given in a declaration.
If the sc-specifier is missing from a declaration, it
is taken to be \fGauto\fR
inside a function,
.Bd extern
outside.
Exception:
functions are always
.Bd extern.
.SH
8.2 Type specifiers
.LP
The type-specifiers are
.SY
type-specifier:
\fGchar\fR
\fGshort\fR
\fGint\fR
\fGlong\fR
\fGunsigned\fR
\fGfloat
\fGdouble
\fIstruct-or-union-specifier
typedef-name
.ES
The words
.Bd "long, short,"
and
.Bd unsigned
may be thought of as adjectives; the following combinations
are acceptable (in any order).
.SY
.ft G
short int
long int
unsigned int
long float
.ES
The meaning of the last is the same as
.Bd double.
Otherwise, at most one type-specifier may be given
in a declaration.
If the type-specifier is missing from a declaration,
it is taken to be
.Bd int.
.PP
Specifiers for structures and unions are discussed in \(sc8.5;
declarations with
.Bd typedef
names are discussed in \(sc8.8.
.SH
8.3 Declarators
.LP
The declarator-list appearing in a declaration
is a comma-separated sequence of declarators,
each of which may have an initializer.
.SY
declarator-list:
init-declarator
init-declarator \fG,\fI declarator-list
.ES
.SY
init-declarator:
declarator initializer\*(op
.ES
Initializers are discussed in \(sc8.6.
The specifiers in the declaration
indicate the type and storage class of the objects to which the
declarators refer.
Declarators have the syntax:
.SY
declarator:
identifier
\fG( \fIdeclarator \fG)
\fG\**\fI declarator
declarator \fG( )\fI
declarator \fG[ \fIconstant-expression\*(op \fG]
.ES
The grouping is
the same as in expressions.
.SH
8.4 Meaning of declarators
.LP
Each declarator is taken to be
an assertion that when a construction of
the same form as the declarator appears in an expression,
it yields an object of the indicated
type and storage class.
Each declarator contains exactly one identifier; it is this identifier that
is declared.
.PP
If an unadorned identifier appears
as a declarator, then it has the type
indicated by the specifier heading the declaration.
.PP
A declarator in parentheses is identical to the unadorned declarator,
but the binding of complex declarators may be altered by parentheses.
See the examples below.
.PP
If a declarator has the form
.PR
\** D
.EP
for D a declarator, then the
contained identifier has the type `pointer to .\|.\|.', where
`\|.\|.\|.\|' is the type which the identifier would have had
if the declarator had been simply D.
.PP
If a declarator has the form
.PR
D\|(\|\|)
.EP
then the contained identifier has the type
`function returning ...', where `\|.\|.\|.\|' is the
type which the identifier would have
had if the declarator had been simply D.
.PP
A declarator may have the form
.PR
D[constant-expression]
.EP
or
.PR
D[\|\|]
.EP
Such declarators make the contained identifier have
type `array.'
If the unadorned declarator D would
specify a non-array of type `.\|.\|.',
then the declarator
`D[\|i\|]'
yields a 1-dimensional array with rank \fIi\fR of objects
of type `.\|.\|.'.
If the unadorned declarator D would
specify an \fIn\|\fR-dimensional array
with rank
$i sub 1 times i sub 2 times ... times i sub n$,
then the declarator
$roman D [ i sub { n+1 } ]$
yields an
$(n+1)$-dimensional
array with rank
$i sub 1 times i sub 2 times ... times i sub n times i sub { n+1}$.
.PP
In the first case the constant
expression
is an expression
whose value is determinable at compile time,
and whose type is
.Bd int.
(Constant expressions are defined precisely in \(sc15.)\|\|
The constant expression of an array declarator may be missing
only for the first dimension.
This notation is useful when the array is external
and the actual declaration, which allocates storage,
is given elsewhere.
The constant-expression may also be omitted
when the declarator is followed by initialization.
In this case the size is calculated from the number
of initial elements supplied.
.PP
An array may be constructed from one of the basic types, from a pointer,
from a structure or union,
or from another array (to generate a multi-dimensional array).
.PP
Not all the possibilities
allowed by the syntax above are actually
permitted.
The restrictions are as follows:
functions may not return
arrays, structures or functions,
although they may return pointers to such things;
there are no arrays of functions, although
there may be arrays of pointers to functions.
Likewise a structure may not contain a function,
but it may contain a pointer to a function.
.PP
As an example, the declaration
.PR
int i, \**ip, f\|(\|\|), \**fip(\|\|), (\**pfi)\|(\|\|);
.EP
declares an integer \fIi\fR,
a pointer \fIip\fR to an integer,
a function \fIf\fR returning an integer,
a function \fIfip\fR returning a pointer to an integer,
and a pointer \fIpfi\fR to a function which
returns an integer.
It is especially useful to compare the last two.
The binding of `\**fip(\|)' is `\**(fip(\|))',
so that the declaration suggests,
and the same construction in an expression
requires, the calling of a function
.It fip,
and then using indirection through the (pointer) result
to yield an integer.
In the declarator `(\**pfi)\|(\|)',
the extra parentheses are necessary, as they are also
in an expression, to indicate that indirection through
a pointer to a function yields a function, which is then called.
.PP
As another example,
.PR
float fa[17], \**afp[17];
.EP
declares an array of \fGfloat\fR numbers and an array of
pointers to \fGfloat\fR numbers.
Finally,
.PR
static int x3d[3][5][7];
.EP
declares a static three-dimensional array of integers,
with rank 3\(mu5\(mu7.
In complete detail, \fIx3d\fR is an array of three items:
each item is an array of five arrays;
each of the latter arrays is an array of seven
integers.
Any of the expressions `x3d', `x3d[\|i\|]', `x3d[\|i\|][\|j\|]', `x3d[\|i\|][\|j\|][\|k\|]'
may reasonably appear in an expression.
The first three have type `array',
the last has type \fGint\fR.
.SH
8.5 Structure and union declarations
.LP
A structure
is an object consisting of a sequence of named members.
Each member may have any type.
A union is an object which may, at a given time, contain any one
of several members.
Structure and union specifiers have the same form.
.SY
structure-or-union-specifier:
struct-or-union { struct-decl-list }
struct-or-union identifier { struct-decl-list }
struct-or-union identifier
.ES
.SY
struct-or-union:
\fGstruct
\fGunion
.ES
The
struct-decl-list
is a sequence of declarations for the members of the structure or union:
.SY
struct-decl-list:
struct-declaration
struct-declaration struct-decl-list
.ES
.SY
struct-declaration:
type-specifier struct-declarator-list \fG;
.ES
.SY
struct-declarator-list:
struct-declarator
struct-declarator \fG,\fI struct-declarator-list
.ES
In the usual case, a struct-declarator is just a declarator
for a member of a structure or union.
A structure member may also consist of a specified number of bits.
Such a member is also called a
.It field;
its length is set off from the field name by a colon.
.SY
struct-declarator:
declarator
declarator \fG: \fI constant-expression
\fG: \fIconstant-expression
.ES
Within a structure, the objects declared
have addresses which increase as their declarations
are read left-to-right.
Each non-field member of a structure
begins on an addressing boundary appropriate
.MC
to its type;
therefore, there may
be unnamed holes in a structure.
.mc
Field members are packed into machine integers;
they do not straddle words.
A field which does not fit into the space remaining in a word
is put into the next word.
No field may be wider than a word.
.MC
Fields are assigned right-to-left
on the \*(pd,
left-to-right on other machines.
.mc
.PP
A struct-declarator with no declarator, only a colon and a width,
indicates an unnamed field useful for padding to conform
to externally-imposed layouts.
As a special case, an unnamed field with a width of 0
specifies alignment of the next field at a word boundary.
The `next field' presumably is a field, not an ordinary
structure member, because in the latter case the alignment would
have been automatic.
.PP
The language does not restrict the types of things that
are declared as fields,
but implementations are not required to support any but
integer fields.
Moreover,
even
.Bd int
fields may be considered to be unsigned.
.MC
On the \*(pd and \*I,
fields are not signed and have only integer values.
.mc
.PP
A union may be thought of as a structure all of whose members
begin at offset 0 and whose size is sufficient to contain
any of its members.
At most one of the members can be stored in a union
at any time.
.PP
A structure or union specifier of the second form, that is, one of
.SY
\fGstruct \fIidentifier { struct-decl-list }
\fGunion \fIidentifier { struct-decl-list }
.ES
declares the identifier to be the
.It "structure tag"
(or union tag)
of the structure specified by the list.
A subsequent declaration may then use
the third form of specifier, one of
.SY
\fGstruct \fIidentifier
\fGunion \fIidentifier
.ES
Structure tags allow definition of self-referential
structures; they also
permit the long part of the declaration to be
given once and used several times.
It is however absurd to declare a structure or union
which contains an instance of
itself, as distinct from a pointer to an instance of itself.
.PP
The names of members and tags may
be the same as ordinary variables.
However, names of tags and members
must be mutually distinct.
.PP
Two structures may share a common initial sequence of members;
that is, the same member may appear in two different structures
if it has the same type in both and if all previous members are the same
in both.
(Actually, the compiler checks only that
a name in two different structures has the same type and
offset in both,
but if preceding members differ the construction is nonportable.)
.PP
A simple example of a structure declaration is
.PR
struct tnode {
char tword[20];
int count;
struct tnode \**left;
struct tnode \**right;
};
.EP
which contains an array of 20 characters, an integer, and two pointers
to similar structures.
Once this declaration has been given, the following
declaration makes sense:
.PR
struct tnode s, \**sp;
.EP
which declares
\fIs\fR to be a structure of the given sort
and \fIsp\fR to be a pointer to a structure
of the given sort.
With these declarations, the expression
.PR
sp\(mi>count
.EP
refers to the
.It count
field of the structure to which
.It sp
points;
.PR
s\fB.\fGleft
.EP
refers to the left subtree pointer
of the structure
.It s.
Finally,
.PR
s.right\(mi>tword[0]
.EP
refers to the first character of the
.It tword
member of the right subtree of
.It s.
.SH
8.6 Initialization
.LP
A declarator may specify an initial value for the
identifier being declared.
The initializer is preceded by `=', and
consists of an expression or a list of values nested in braces.
.SY
initializer:
\fB=\fI expression
\fB= { \fIinitializer-list }
\fB= { \fIinitializer-list \fB, }
.ES
.SY
initializer-list:
expression
initializer-list \fG,\fI initializer-list
{ initializer-list }
.ES
The `=' is a new addition to the syntax, intended to alleviate
potential ambiguities.
The current compiler allows it to be omitted when the rest of the initializer is
a very simple expression (just a name, string, or constant)
or when the rest of the initializer is enclosed in braces.
.PP
All the expressions in an initializer
for a static or external variable must be constant
expressions, which are described in \(sc15,
or expressions which reduce to the address of a previously
declared variable, possibly offset by a constant expression.
Automatic or register variables may be initialized by arbitrary
expressions involving previously declared variables.
.MC
.PP
Static and external variables which are not initialized
are guaranteed to start off as
.Bd 0 "" ";"
automatic and register variables which are not initialized
are guaranteed to start off as garbage.
.mc
.PP
When an initializer applies to a
.It scalar
(a pointer or an object of arithmetic type),
it consists of a single expression, perhaps in braces.
The initial value of the object is taken from
the expression; the same conversions as for assignment are performed.
.PP
When the declared variable is an
.It aggregate
(a structure or array)
then the initializer consists of a brace-enclosed, comma-separated list of
initializers for the members of the aggregate,
written in increasing subscript or member order.
If the aggregate contains subaggregates, this rule
applies recursively to the members of the aggregate.
If there are fewer initializers in the list than there are members of the aggregate,
then the aggregate is padded with 0's.
.MC
It is not permitted to initialize unions or automatic aggregates.
.mc
.PP
Braces may be elided as follows.
If the initializer begins with a left brace, then
the succeding comma-separated list of initializers initialize
the members of the aggregate;
it is erroneous for there to be more initializers than members.
If, however, the initializer does not begin with a left brace,
then only enough elements from the list are taken to account
for the members of the aggregate; any remaining members
are left to initialize the next member of the aggregate of which
the current aggregate is a part.
.PP
A final abbreviation allows a
.Bd char
array to be initialized by a string.
In this case successive members of the string
initialize the members of the array.
.PP
For example,
.PR
int x[ ] = { 1, 3, 5 };
.EP
declares and initializes
.It x
as a 1-dimensional array which has three members, since no size was specified
and there are three initializers.
.PR
float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};
.EP
is a completely-bracketed initialization:
1, 3, and 5 initialize the first row of
the array $y [ 0 ]$,
namely $y [ 0 ] [ 0 ]$, $y [ 0 ] [ 1 ]$, and $y [ 0 ] [ 2 ]$.
Likewise the next two lines initialize
$y [ 1 ]$ and $y [ 2 ]$.
The initializer ends early and therefore $y [ 3 ]$ is initialized with 0.
Precisely the same effect could have been achieved by
.PR
float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7,
};
.EP
The initializer for
$y$ begins with a left brace, but that for $y [ 0 ]$ does not,
therefore 3 elements from the list are used.
Likewise the next three are taken successively for $y [ 1 ]$
and $y [ 2 ]$.
Also,
.PR
float y[4][3] = {
{ 1 }, { 2 }, { 3 }, { 4 }
};
.EP
initializes the first column of
.It y
(regarded as a two-dimensional array)
and leaves the rest 0.
.PP
Finally,
.PR
char msg[\|] = "Syntax error on line %s\en";
.EP
shows a character array whose members are initialized
with a string.
.SH
8.7 Type names
.LP
In two contexts (to specify type conversions explicitly, and as an argument of
.Bd sizeof)
it is desired to supply the name of a data type.
This is accomplished using a `type name,' which in essence
is a declaration for an object of that type which omits the name of
the object.
.SY
type-name:
type-specifier abstract-declarator
.ES
.SY
abstract-declarator:
empty
\fG( \fIabstract-declarator \fG)
\fG\** \fIabstract-declarator\fI
abstract-declarator \fG( )\fI
abstract-declarator \fG[ \fIconstant-expression\*(op \fG]
.ES
To avoid ambiguity,
in the construction
.SY
\fG( \fIabstract-declarator \fG)
.ES
the
abstract-declarator
is required to be nonempty.
Under this restriction,
it is possible to identify uniquely the location in the abstract-declarator
where the identifier would appear if the construction were a declarator
in a declaration.
The named type is then the same as the type of the
hypothetical identifier.
For example,
.PR
int
int \**
int \**[3]
int (\**)[3]
.EP
name respectively the types `integer,' `pointer to integer,'
`array of 3 pointers to integers,'
and
`pointer to an array of 3 integers.'
As another example,
.PR
int i;
. . .
sin( (double) i);
.EP
calls the
.It sin
routine (which accepts a
.Bd double
argument)
with an argument appropriately converted.
.SH
8.8 Typedef
.LP
Declarations whose `storage class' is
.Bd typedef
do not define storage, but instead
define identifiers which can be used later
as if they were type keywords naming fundamental
or derived types.
Within the scope of a declaration involving
.Bd typedef,
each of the identifiers appearing as part of
any declarators therein become syntactically
equivalent to type keywords
naming the type
associated with the identifiers
in the way described in \(sc8.4.
.SY
typedef-name:
identifier
.ES
For example,
after
.PR
typedef int MILES, \**KLICKSP;
typedef struct { double re, im;} complex;
.EP
the constructions
.PR
MILES distance;
extern KLICKSP metricp;
complex z, \**zp;
.EP
are all legal declarations; the type of
.It distance
is
.Bd `int',
that of
.It metricp
is `pointer to
.Bd int,'
and that of
.It z
is the specified structure.
.It zp
is a pointer to such a structure.
.PP
.It Typedef
does not introduce brand new types, only synonyms for
types which could be specified in another way.
Thus
.Id distance
in the example above
.It distance
is considered to have exactly the same type as
any other
.Bd int
variable.