|
Compaq C
Compaq C Language Reference Manual
struct person
{
char first[20];
char middle[3];
char last[30];
} employees, managers;
|
If the tag is omitted, the structure or union definition applies
only to the identifiers that follow in the declaration. For example:
struct
{
char first[20];
char middle[3];
char last[30];
} employees, managers;
|
The tag can refer to a structure or union type defined elsewhere.
The definition is then applied to the variable identifiers that follow
the tag name in the declaration, as in the following example:
struct person employees, managers;
|
Another form uses only the
struct
or
union
keyword and the tag to override other identical tags in the scope, and
to reserve the tag for a later definition within a new scope. A
definition within a new scope overrides any previous tag definition
appearing in an outer scope. This use of declaring tags is called
tentative structure tag declaration. Using such declarations,
you can eliminate ambiguity when making a forward reference to tag
identifiers. The following example shows such a case:
struct A {...}; /* Definition of external struct A */
{
struct A; /* Tentative structure tag declaration. */
/* First declaration of A (in external scope) is
hidden. This structure will be defined later */
struct inner
{
struct A *pointer; /* Declare a structure pointer by */
. /* forward referencing. */
.
.
};
struct A {...}; /* Tentative declaration of internal struct A is
defined here. */
/* External struct A is unaffected by this definition*/
}
|
In the example, the pointer to the structure defined using the tag
A
points to the internal definition of
A
, not the external definition.
4.8.1 Similarities Between Structures and Unions
Structures and unions share the following characteristics:
- Their members can be objects of any type, including other
structures and unions or arrays. A member can also consist of a bit
field.
- The only operators valid for use with entire structures and unions
are the simple assignment (=) and
sizeof
operators. In particular, structures and unions cannot appear as
operands of the equality (
==
), inequality (
!=
), or cast operators. The two structures or unions in the assignment
must have the same members and member types.
- A structure or a union can be passed by value to functions and
returned by value by functions. The argument must have the same type as
the function parameter. A structure or union is passed by value just
like a scalar variable; that is, the entire structure or union is
copied into the corresponding parameter.
Note
When passing structures as arguments, they might or might not terminate
on a longword boundary. If they do not, Compaq C aligns the
following argument on the next longword boundary.
|
4.8.2 Differences Between Structures and Unions
The difference between structures and unions lies in the way their
members are stored and initialized, as follows:
-
Within a structure, the members have addresses that increase as the
declarators are read left-to-right. That is, the members of a structure
all begin at different offsets from the base of the structure. The
offset of a particular member corresponds to the order of its
declaration; the first member is at offset 0.
A pointer to a
structure points to its first member, so no unnamed holes can reside at
the beginning of a structure.
On OpenVMS VAX systems, nonbit-field structure members are
byte-aligned by default. However, the
#pragma [no]member_alignment
and
#pragma pack
preprocessor directives are provided to switch from byte preprocessor
directive is provided to switch from byte alignment to natural
alignment. On Alpha systems, nonbit-field structure members
are naturally aligned; each successive nonbit-field structure member
begins at the next byte boundary that matches the alignment appropriate
to its type. For example, a short integer is aligned on a 2-byte
boundary and a long integer is aligned on a 4-byte boundary, so there
may be unnamed holes in a structure. The length of a
naturally-aligned structure on a Alpha processors must be a multiple of
the greatest alignment requirement of any of its members. For example,
a structure containing characters, short integers, and longwords will
be a multiple of four in length to match the multiple of four bytes for
the longword. The (
#pragma [no]member_alignment
) and
#pragma pack
preprocessor directives are also supported on this platform. See
your platform-specific Compaq C documentation for specific
structure alignment requirements and examples.
- In a union, every member begins at offset 0 from the address of
the union. The size of the union in memory is the size of its largest
member. The value of only one member can be stored in a union object at
a time. When the storage space allocated to the union contains a
smaller member, the extra space between the end of the smaller member
and the end of the allocated memory remains unaltered. The rules for
alignment of union members are the same as for structure members (see
your platform-specific Compaq C documentation).
A pointer to a
union member, converted to the proper type, points to the beginning of
the union object.
- Several members of a structure can be initialized at once; only the
first member of a union can be given an initializer.
4.8.3 Bit Fields
One of the advantages of structures is the ability to pack data into
them bit-by-bit.
A structure member often is an object with a basic type size. However,
you can also declare a structure member that is composed only of a
specified number of bits. Such a member is called a bit field;
its length, an integral nonnegative constant expression, is set off
from the field name by a colon, as shown by the following syntax:
struct-declarator:
declarator: constant-expression
:constant-expression
|
Bit fields provide greater control over the structure's storage
allocation and allow tighter packing of information in memory. By using
bit fields, data can be densely packed into storage.
A bit field's type must be specified (except with unnamed bit fields),
and a bit field can have the
int
,
unsigned int
, or
signed int
type. The bit field's value must be small enough to store in an object
of the declared size.
In the compiler's default mode, the
enum
,
long
,
short
, and
char
types are also allowed for bit fields.
A bit field can be named or unnamed. A bit-field declaration without a
declarator (for example,
:10
) indicates an unnamed bit field, which is useful for padding a
structure to conform to a specified layout. If the bit field is
assigned a width of 0, it indicates that no further bit fields should
be placed in the alignment unit, and it cannot name a declarator. Use a
colon (:) to separate the member's declarator (if any) from a constant
expression that gives the field width in bits. No field can be longer
than 32 bits (1 longword).
Since nonbit-field structure members are aligned on at least byte
boundaries, the unnamed form can create unnamed gaps in the structure's
storage. As a special case, an unnamed field of width 0 causes the next
member (normally another field) to be aligned on at least a byte
boundary; that is, a bit-field structure member with zero width
indicates that no further bit field should be packed into an alignment
unit.
The following restrictions apply to the use of bit fields:
- You cannot declare arrays of bit fields.
- The ampersand operator (&) cannot be applied to fields, so
there cannot be pointers to bit fields.
Sequences of bit fields are packed as tightly as possible. In C, bit
fields are assigned from right to left; that is, from low-order to
high-order bit.
To create bit fields, specify an identifier, a colon, and the
identifier's width (in bits) as a structure member.
In the following example, three bit fields are created in the structure
declaration:
struct {
unsigned int a : 1; /* Named bit field (a) */
unsigned int : 0; /* Unnamed bit field = 0 */
unsigned int : 1; /* Unnamed bit field */
} class;
|
The first and third bit fields are one bit wide, the second is zero
bits wide, which forces the next member to be aligned on a natural or
byte boundary.
Bit fields (including zero-length bit fields) not immediately declared
after other bit fields have the alignment requirement imposed by their
type, but never a lesser alignment requirement than that of
int
. In a declaration of a bit field that immediately follows another bit
field, the bits are packed into adjacent space in the same alignment
unit, if sufficient space remains; otherwise, padding is inserted and
the second bit field is put into the next alignment unit.
See your Compaq C documentation for platform-specific information
on bit-field alignment within a structure.
4.8.4 Initializing Structures
All structures can be initialized with a brace-enclosed list of
component initializers. Structures with automatic storage class can
also be initialized by an expression of compatible type.
Initializers are assigned to components on a one-to-one basis. If there
are fewer initializers than members for a structure, the remaining
members are initialized to 0. Listing too many initializers for the
number of components in a structure is an error. All unnamed structure
or union members are ignored during initialization.
Separate initializing values with commas and delimit them with braces {
}. The following example initializes two structures, each with two
members:
struct
{
int i;
float c;
} a = { 1, 3.0e10 }, b = { 2, 1.5e5 };
|
The compiler assigns structure initializers in increasing member order.
Note that there is no way to initialize a member in the middle of a
structure without also initializing the previous members. Example 4-1
shows the initialization rules applied to an array of structures.
Example 4-1 The Rules for Initializing
Structures |
#include <stdio.h>
main()
{
int m, n;
static struct
{
char ch;
int i;
float c;
} ar[2][3] =
(1) {
(2) {
(3) { 'a', 1, 3e10 },
{ 'b', 2, 4e10 },
{ 'c', 3, 5e10 },
}
};
printf("row/col\t ch\t i\t c\n");
printf("-------------------------------------\n");
for (n = 0; n < 2; n++)
for (m = 0; m < 3; m++)
{
printf("[%d][%d]:", n, m);
printf("\t %c \t %d \t %e \n",
ar[n][m].ch, ar[n][m].i, ar[n][m].c);
}
}
|
Key to Example 4-1:
- Delimit an array row initialization with
braces.
- Delimit a structure initialization with braces.
- 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 the C99 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
C99 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 C99 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:
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.
- 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.
- 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.
- 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
|
- 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 };
|
- 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.
- 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;
|
- 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
:
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;
|
|
|
|
|