A structure consists of a list of members whose storage is allocated in an ordered sequence. A union consists of a sequence of members whose storage overlaps. Structure and union declarations have the same form, as follows:
struct-or-union identifier(opt) { struct- declaration-list} struct-or-union identifier
struct union
struct-declaration struct-declaration-list struct-declaration
specifier-qualifier-list struct-declarator-list ;
type-specifier specifier-qualifier-list(opt) type-qualifier specifier-qualifier-list (opt)
struct-declarator struct-declarator-list , struct-declarator
declarator declarator(opt) : constant-expression
Neither a structure nor union member can have a function type or an incomplete type. Structures and unions cannot contain instances of themselves as members, but they can have pointers to instances of themselves as members. The declaration of a structure with no members is accepted; its size is zero.
Each structure or union definition creates a unique structure or
union type within the compilation unit. The struct
or
union
keywords can be followed by a tag, which gives
a name to the structure or union type in much the same way that an
enum
tag gives a name to an enumerated type. The tag
can then be used with the struct
or union
keywords to declare variables of that type without repeating a long
definition.
The tag is followed by braces { } that enclose a list of member declarations. Each declaration in the list gives the data type and name of one or more members. The names of structure or union members can be the same as other variables, function names, or members in other structures or unions; the compiler distinguishes them by context. In addition, the scope of the member name is the same as the scope of the structure or union in which it appears. The structure or union type is completed when the closing brace completes the list.
An identifier used for a structure or union tag must be unique among the visible tags in its scope, but the tag identifier can be the same as an identifier used for a variable or function name. Tags can also have the same spellings as member names; the compiler distinguishes them by name space and context. The scope of a tag is the same as the scope of the declaration in which it appears.
Structures and unions can contain other structures and unions. For example:
struct person { char first[20]; char middle[3]; char last[30]; struct /* Nested structure here */ { int day; int month; int year; } birth_date; } employees, managers;
Structure or union declarations can take one of the following forms:
struct person { char first[20]; char middle[3]; char last[30]; }; struct person employee; /* The tag (person) identifies employee as */ a structure with members shown in */ the declaration of person */
struct person { char first[20]; char middle[3]; char last[30]; } employees, managers;
struct { char first[20]; char middle[3]; char last[30]; } employees, managers;
struct person employees, managers;
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.
Structures and unions share the following characteristics:
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.
The difference between structures and unions lies in the way their members are stored and initialized, as follows:
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
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 (
See your platform-specific DEC C
documentation for specific structure alignment requirements and
examples. #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.
#pragma [no]member_alignment
) and
#pragma pack
preprocessor directives are also
supported on this platform.
A pointer to a union member, converted to the proper type, points to the beginning of the union object.
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:
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:
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 DEC C documentation for platform- specific information on bit-field alignment within a structure.
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.
#include <stdio.h> main() { int m, n; static struct { char ch; int i; float c; } ar[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:
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
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; }