The alignment and size of a structure is affected by the alignment requirements and sizes of the structure components for each DEC C platform. A structure can begin on any byte boundary and occupy any integral number of bytes. However, individual architectures or operating systems can specify particular alignment and padding requirements.
DEC C on VAX processors does not require that structures or structure members be aligned on any particular boundaries.
The components of a structure are laid out in memory in the order they are declared. The first component has the same address as the entire structure. On VAX processors, each additional component follows its predecessor in the immediately following byte.
For example, the following type is aligned as shown in Figure 4-1:
struct {char c1; short s1; float f; char c2; }
The alignment of the entire structure can occur on any byte boundary, and no padding is introduced. The float variable f may span longwords, and the short variable s1 may span words.
The following pragma can be used to force specific alignments:
#pragma member_alignment
Structure alignment for DEC C for OpenVMS Systems on VAX processors is achieved by the default, #pragma nomember_alignment, which causes data structure members to be byte- aligned (with the exception of bit-field members).
Structure alignment for DEC C for OpenVMS Systems on Alpha processors is achieved by the default, #pragma member_alignment, which causes data structure members to be naturally aligned. This means that data structure members are aligned on the next boundary appropriate to the type of the member, rather than on the next byte.
For more information on the #pragma member_alignment preprocessor directive, see Section 5.4.9.
Bit fields can have any integral type. However, the compiler issues a warning if /STANDARD=ANSI89 is specified, and the type is something other than int, unsigned int, or signed int. Bit fields are allocated within the unit from low order to high order. If a bit field immediately follows another bit field, the bits are packed into adjacent space, even if this overflows into another byte. However, if an unnamed bit field is specified to have length 0, filler is added so the bit field immediately following starts on the next byte boundary.
For example, the following type is aligned as shown in Figure 4-2:
struct {int i:2; int ii:2; unsigned int ui: 30; }
Bit field ii is positioned immediately following bit field i. Because there are only 28 bit positions remaining and ui requires 30 bits, the first 28 bits of ui are put into the first longword, and the remaining two bits overflow into the next longword.
The DEC C compiler initializes bit fields in structs differently than VAX C does. The following program compiles without error using both compilers but the results are different. DEC C skips over unnamed bits but VAX C does not.
#include <stdio.h> int t() { static struct bar {unsigned :1; unsigned one : 1; unsigned two : 1; }; struct bar foo = {1,0}; printf("%d %d\n",foo.one,foo.two); return 1; }
When compiled with DEC C, this example produces the following output:
1 0
When compiled with VAX C, this example produces the following output:
0 0
Variant structures and unions are DEC C extensions available in VAX C compatibility mode only, and they are not portable.
Variant structure and union declarations allow you to refer to members of nested aggregates without having to refer to intermediate structure or union identifiers. When a variant structure or union declaration is nested within another structure or union declaration, the enclosed variant aggregate ceases to exist as a separate aggregate, and DEC C propagates its members to the enclosing aggregate.
Variant structures and unions are declared using the variant_struct and variant_union keywords. The format of these declarations is the same as that for regular structures or unions, with the following exceptions:
Initialization of a variant structure or union is the same as that for a normal structure or union.
As with regular structures and unions, in VAX C compatibility mode, variant structures and unions in an assignment operation need only have the same size in bits, rather than requiring the same members and member types.
To show the use of variant aggregates, consider the following code example that does not use variant aggregates:
/* The numbers to the right of the code represent the byte offset * * from the enclosing structure or union declaration. */ struct TAG_1 { int a; /* 0-byte enclosing_struct offset */ char *b; /* 4-byte enclosing_struct offset */ union TAG_2 /* 8-byte enclosing_struct offset */ { int c; /* 0-byte nested_union offset */ struct TAG_3 /* 0-byte nested_union offset */ { int d; /* 0-byte nested_struct offset */ int e; /* 4-byte nested_struct offset */ } nested_struct; } nested_union; } enclosing_struct;
If you want to access nested member d, then you need to specify all the intermediate aggregate identifiers:
enclosing_struct.nested_union.nested_struct.d
If you try to access member d without specifying the intermediate identifiers, then you would access the incorrect offset from the incorrect structure. Consider the following example:
enclosing_struct.d
The compiler uses the address of the original structure (enclosing_ struct), and adds to it the assigned offset value for member d (0 bytes), even though the offset value for d was calculated according to the nested structure (nested_struct). Consequently, the compiler accesses member a (0-byte offset from enclosing_struct) instead of member d.
The following code example shows the same code using variant aggregates:
/* The numbers to the right of the code present the byte offset * * from enclosing_struct. */ struct TAG_1 { int a; /* 0-byte enclosing_struct offset */ char *b; /* 4-byte enclosing_struct offset */ variant_union { int c; /* 8-byte enclosing_struct offset */ variant_struct { int d; /* 8-byte enclosing_struct offset */ int e; /* 12-byte enclosing_struct offset */ } nested_struct; } nested_union; } enclosing_struct;
The members of the nested_union and nested_struct variant aggregates are propagated to the immediately enclosing aggregate (enclosing_ struct). The variant aggregates cease to exist as individual aggregates.
Since the nested_union and nested_struct variant aggregates do not exist as individual aggregates, you cannot use tags in their declarations, and you cannot use their identifiers (nested_union, nested_struct) in any reference to their members. However, you are free to use the identifiers in other declarations and definitions within your program.
To access member d, use the following notation:
enclosing_struct.d
Using the following notation causes unpredictable results:
enclosing_struct.nested_union.nested_struct.d
If you use normal structure or union declarations within a variant aggregate declaration, the compiler propagates the structure or union to the enclosing aggregate, but the members remain a part of the nested aggregate. For example, if the nested structure in the last example was of type struct, the following offsets would be in effect:
struct TAG_1 { int a; /* 0-byte enclosing_struct offset */ char *b; /* 4-byte enclosing_struct offset */ variant_union { int c; /* 8-byte enclosing_struct offset */ struct TAG_2 /* 8-byte enclosing-struct offset */ { int d; /* 0-byte nested_struct offset */ int e; /* 4-byte nested_struct offset */ } nested_struct; } nested_union; } enclosing_struct;
In this case, to access member d, use the following notation:
enclosing_struct.nested_union.nested_struct.d