HPlogo HP C/HP-UX Programmer's Guide: Workstations and Servers > Chapter 2 Storage and Alignment Comparisons

Alignment Rules

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Index

This discussion of alignment rules divides them into sections on scalar types, arrays, structures and unions, bit-fields, and typedefs.

Alignment of Scalar Types

Scalar types are integral types, floating types, and pointer types. Alignment of scalar types that are not part of a structure, union, or typedef declaration are not affected by the alignment mode. Therefore, they are aligned the same way in all alignment modes.

Table 2-2 Aligning Scalar Types

Data Type

Size (bytes)

Alignment (bytes)

char, signed char, unsigned char, char enum

1

1

short, unsigned short, signed short, short enum

2

2

int, signed int, unsigned int, int enum

4

4

long, signed long, unsigned long, long enum

4

4

enum

4

4

long long

8

8

pointer

4

4

long pointer

8

4

float

4

4

double

8

8

long double

16*

8

 

*8 bytes on DOMAIN

NOTE: Except for the HPUX_NATURAL and DOMAIN_NATURAL modes, the alignment of scalar types inside a structure or union may differ. (See “Alignment of Structures and Unions” below.) Also, a type that is defined via a typedef to any of the scalar types below may have a different alignment (see “Alignment of Typedefs ” below.)

Alignment of Arrays

An array is aligned according to its element type. For example, a double array is aligned on an 8-byte boundary; and a float array within a struct is aligned on a 4-byte boundary.

Alignment of array elements is not affected by the alignment mode, unless the array itself is a member of a structure or union. An array that is a member of a structure or union is aligned according to the rules for structure or union member alignment (see “Alignment of Structures and Unions” below for more information.)

An array's size is computed as:

(size of array element type) × (number of elements)

For instance, the array declared below is 400 bytes (4 × 100) long:

int arr[100];

The size of the array element type is 4 bytes and the number of elements is 100.

Alignment of Structures and Unions

In a structure, each member is allocated sequentially at the next alignment boundary corresponding to its type. Therefore, the structure might be padded internally if its members' types have different alignment requirements. In a union, all members are allocated starting at the same memory location. Both structures and unions can have padding at the end, in order to make the size a multiple of the alignment.

NOTE: These rules are not true if the member type has been previously declared under another alignment mode. The member type will retain its original alignment, overriding other modes in effect. See “Using the HP_ALIGN Pragma ” below for information on controlling alignment of structures and unions.

Table 2-3 “Aligning Structure or Union Members ” lists the alignments for structure and union members.

Table 2-3 Aligning Structure or Union Members

Data Type

Size (bytes)

HPUX_WORD DOMAIN_WORD

HPUX_NATURAL DOMAIN_NATURAL

HPUX_ NATURAL_S500

NATURAL

char, signed char, unsigned char, char enum

1

1

1

1

1

short, unsigned short, signed short, short enum

2

2

2

2

2

int, signed int, unsigned int, int enum

4

2

4

4

4

long, signed long, unsigned long, long enum

4

2

4

4

4

enum

4

2

4

4

4

long long

8

2

8

4

8

pointer

4

2

4

4

4

long pointer

8

2

4

4

4

float

4

2

4

4

4

double

8

2

8

4

8

long double

16

2

8

4

8

arrays

Follows alignment of array type inside a structure or union.

struct, union

Follows alignment of its most restricted member.

 

NOTE: In NOPADDING alignment mode, the alignment boundary is 1 byte in all cases except where bitfields are used.

HPUX_WORD/DOMAIN_WORD Alignments

For HPUX_WORD and DOMAIN_WORD alignments, all structure and union types are 2-byte aligned. Member types larger than 2 bytes are aligned on a 2-byte boundary. Padding is performed as necessary to reach a resulting structure or union size which is a multiple of 2 bytes.

For example:

     struct st {
char c;
long l;
char d;
short b;
int i[2];
} s;

Compiling with the +m option to show the offsets of the identifiers, you will get the following output. Offsets are given as "byte-offset" @ "bit-offset" in hexadecimal.

       Identifier         Class       Type              Address
- - -
s ext def struct st
c member char 0x0 @ 0x0
l member long int 0x2 @ 0x0
d member char 0x6 @ 0x0
b member short int 0x8 @ 0x0
i member ints [2] 0xa @ 0x0

The resulting size of the structure is 18 bytes, with the alignment of 2 bytes, as illustrated in Figure 2-1 “Example of HPUX_WORD/DOMAIN_WORD Alignment for Structure s ”

Figure 2-1 Example of HPUX_WORD/DOMAIN_WORD Alignment for Structure s

[Example of HPUX_WORD/DOMAIN_WORD Alignment for Structure s]

HPUX_NATURAL/DOMAIN_NATURAL Alignments

For HPUX_NATURAL and DOMAIN_NATURAL alignments, the alignment of structure and union types is the same as the strictest alignment of any member. Therefore, they may be aligned on 1-, 2-, 4-, or 8-byte boundaries. Padding is performed as necessary so that the size of the object is a multiple of the alignment size.

For example, the declaration shown in the previous section will now be aligned:

       Identifier         Class       Type              Address
- -
s ext def struct st
c member char 0x0 @ 0x0
l member long int 0x4 @ 0x0
d member char 0x8 @ 0x0
b member short int 0xa @ 0x0
i member ints [2] 0xc @ 0x0

In this case, the size of the structure is 20 bytes, and the entire structure is aligned on a 4-byte boundary since the strictest alignment is 4 (from the int and long types), as illustrated in Figure 2-2 “Example of HPUX_NATURAL/DOMAIN_NATURAL Alignment for Structure s ”.

Figure 2-2 Example of HPUX_NATURAL/DOMAIN_NATURAL Alignment for Structure s

[Example of HPUX_NATURAL/DOMAIN_NATURAL Alignment for Structure s]

HPUX_NATURAL_S500 Alignments

For HPUX_NATURAL_S500 alignments, series 500 computers align structures on 2- or 4-byte boundaries, according to the strictest alignment of its members. As with the other alignment modes, padding is done to a multiple of the alignment size.

For example, the following code:

     struct {
char c;
double d;
} s1;

compiled with the +m option produces:

       Identifier         Class       Type              Address
- - -
s1 ext def struct
c member char 0x0 @ 0x0
d member double 0x4 @ 0x0

The entire structure is 4-byte aligned, with a resulting size of 12 bytes.

NATURAL Alignments

For NATURAL alignments, structures and unions are aligned on 2-, 4-, or 8-byte boundaries, according to the strictest alignment of its members. Padding is done to a multiple of the alignment size.

NOPADDING Alignments

For NOPADDING alignments, structure or union members are byte aligned; therefore, struct and union types are byte aligned. This alignment mode does not cause compressed packing where there are zero bits of padding. It only ensures that there will be no full bytes of padding in the structure or union, unless bit-fields are used. There may be bit paddings or even a full byte of padding between members if there are bit-fields. Refer to “Alignment of Bit-Fields ” for more information.

Consider the following code fragment:

     #pragma HP_ALIGN NOPADDING
typedef struct s {
char c;
short s;
} s1;

s1 arr[4];

The size of s1 is 3 bytes, with 1-byte alignment. Therefore, the size of arr is 12 bytes, with 1-byte alignment. There is no padding between the individual array elements; they are all packed on a byte boundary (see Figure 2-3 “Example of NOPADDING Alignment for Structure s1 ”).

Figure 2-3 Example of NOPADDING Alignment for Structure s1

[Example of NOPADDING Alignment for Structure s1]

Note that if a member of a structure or union has been declared previously under a different alignment mode, it will retain its original alignment which may not be byte alignment. The NOPADDING alignment will not override the alignment of the member, so there may be some padding done within the structure, and the structure may be greater than byte aligned.

Refer to “Aligning Structures Between Architectures” below for examples on on structure alignment for different systems.

Alignment of Bit-Fields

The alignment modes for bit-fields are grouped differently than they are for the other types. The three groups are:

  • HPUX_NATURAL/HPUX_NATURAL_S500

  • DOMAIN_WORD/DOMAIN_NATURAL/NATURAL/NOPADDING

  • HPUX_WORD (combination of the previous two)

HPUX_NATURAL/HPUX_NATURAL_S500 Alignments

For HPUX_NATURAL and HPUX_NATURAL_S500 alignments, no bit-field can cross a "natural" boundary. A bit-field that immediately follows another bit-field is packed into adjacent bits, unless the second bit-field crosses a natural boundary according to its type. For example:

      struct {
int a:5;
int b:15;
int c:17;
char :0;
char d:5;
char e:5;
} foo;

when compiled with the +m option produces:

       Identifier           Class       Type              Address
- -
foo ext def struct
a member int 0x0 @ 0x0
b member int 0x0 @ 0x5
c member int 0x4 @ 0x0
<NULL_SYMBOL> member char 0x7 @ 0x0
d member char 0x7 @ 0x0
e member char 0x8 @ 0x0

The size of the structure is 12 bytes, with 4-byte alignment as illustrated in Figure 2-4 “Example of HPUX_NATURAL/HPUX_NATURAL_S500 Alignment for Structure foo ”.

Figure 2-4 Example of HPUX_NATURAL/HPUX_NATURAL_S500 Alignment for Structure foo

[Example of HPUX_NATURAL/HPUX_NATURAL_S500 Alignment for Structure foo]

Since b (being an int type) does not cross any word boundaries, a and b are adjacent. c starts on the next word because it would cross a word boundary if it started right after b. The zero length bit-field forces no further bit-field to be placed between the previous bit-field, if any, and the next boundary described by the zero-length bit field's type. Thus, if we are at bit 5 and see a zero length bit-field of type int, then the next member will start at the next word boundary (bits 5-31 will be empty). However, if we are at bit 5 and see a zero length bit-field of type char, then the next member will start at least at the next byte (bits 5-7 will be empty), depending on whether the next member can start at a byte-boundary.

DOMAIN_WORD/DOMAIN_NATURAL/NATURAL and NOPADDING Alignments

For DOMAIN_WORD, DOMAIN_NATURAL, NATURAL, and NOPADDING alignments:

  • All integral types are treated identically; that is, the packing for char a:17 (this is legal) is the same as for int a:17.

  • Bit-fields can cross "natural" boundaries, unlike for HPUX_NATURAL. That is, for int a:30; int b:7;, b will start at bit 30.

  • No bit-field can cross more than one 2-byte boundary. Thus, for int a:14; int b:18;, b will start at bit 16. If it started at bit 14, it would illegally cross both the 2- and 4-byte boundaries.

  • The use of any type and size of bit-field alone will only cause the entire structure to have 2-byte alignment (1-byte for NOPADDING).

NOTE: NOPADDING of bit-fields follows the DOMAIN alignment scheme. This may result in a full byte of padding between two bit-fields.

For example:

     struct {
char c;
int i:31; <-- At offset 2 bytes.
} bar;

The above structure bar will align the bit-field at offset 2 bytes, so that there is a full byte of padding between c and i, even with NOPADDING alignment mode (see Figure 2-5 “Example of NATURAL Alignment for Structure bar ”.)

Figure 2-5 Example of NATURAL Alignment for Structure bar

[Example of NATURAL Alignment for Structure bar]

HPUX_WORD Alignments

For HPUX_WORD alignments:

  • Alignment for char and short bit-fields is identical to that of HPUX_NATURAL.

  • Alignment for any other bit-fields (int, long long, enum, for example) is identical to DOMAIN bit-field alignment.

Note that alignment of a char or short bit-field may not be the same as alignment of a char or short enum bit-field under the same circumstances.

For example:

     #pragma HP_ALIGN HPUX_WORD

char enum b {a};
struct s {
int int_bit :30;
char char_bit :5;
};
struct t {
int int_bit :30;
char enum b char_enum_bit: 5;
};

int main()
{
struct s basic_str;
struct t enum_str;
}

Compilation with the +m option gives the following map:

       Identifier           Class       Type              Address
- -
basic_str auto struct s SP-48
int_bit member int 0x0 @ 0x0
char_bit member char 0x4 @ 0x0
enum_str auto struct t SP-42
int_bit member int 0x0 @ 0x0
char_enum_bit member enum 0x3 @ 0x6

Both structures have a resulting size of 6 bytes, with 2-byte alignment as shown in Figure 2-6 “Example of Structures basic_str and enum_str ”.

Figure 2-6 Example of Structures basic_str and enum_str

[Example of Structures basic_str and enum_str]

Notice that char_bit follows the HPUX_NATURAL alignment scheme, but char_enum_bit follows the DOMAIN_WORD alignment scheme, even though the length of their bit-field types are equivalent.

Alignment of Typedefs

Alignment for typedefs is slightly different than alignment for structures. Within a structure, the member itself is affected by the alignment mode. However, with a typedef, the alignment of the type that the typedef name is derived from is affected, not the typedef name itself. The typedef name is then associated with the derived type.

When a typedef is seen, a new type is created by:

  1. Taking the innermost type from which the typedef name is derived (which may be another derived type).

  2. Setting its alignment to what it would be if it were used inside a structure or union declaration.

  3. Creating a derived type from that new type, associating it with the typedef name.

Let us start with a simple example of a declaration under NOPADDING:

       typedef int my_int;

Since an int will be 1-byte aligned inside a structure under NOPADDING, my_int will be 1-byte aligned.

Consider a pointer typedef with NOPADDING alignment:

       typedef int **my_double_ptr;

my_double_ptr is derived from an integer type; therefore, a new integer type of 1-byte alignment is created. my_double_ptr is defined to be a 4-byte aligned pointer to another 4-byte aligned pointer which points to a byte-aligned int.

Consider another example, this time with HPUX_WORD:

    typedef int *my_ptr;
typedef my_ptr *my_double_ptr;

In the first typedef, my_ptr will be a 4-byte aligned pointer to a 2-byte aligned int. The second typedef will create another type for my_ptr which is now 2-byte aligned, since my_double_ptr is derived from my_ptr. So my_double_ptr is a 4-byte aligned pointer to a 2-byte aligned pointer which points to a 2-byte aligned int.

Similar declarations inside a structure will not have the same resulting alignment. Consider the following declaration:

       #pragma HP_ALIGN NOPADDING

typedef int **my_double_ptr;

struct s {
int **p;
};

In the above example, my_double_ptr is a 4-byte aligned pointer type pointing to another 4-byte aligned pointer which points to a 1-byte aligned int. However, struct s member p is a 1-byte aligned pointer which points to a 4-byte aligned pointer which points to 4-byte aligned int. Inside a structure, the member itself is affected by the alignment mode. However, with a typedef, the typedef name is not directly affected. The innermost type from which the typedef name is derived is affected by the alignment mode.