|
|
HP C/HP-UX Programmer's Guide: Workstations and Servers > Chapter 2 Storage and Alignment ComparisonsThe HP_ALIGN Pragma |
|
The HP_ALIGN pragma controls data storage allocation and alignment of structures, unions, and type definitions, using typedefs. It enables you to control the alignment mode when allocating storage space for data. It is especially important when used to control the allocation of binary data that is transmitted among machines having different hardware architectures. The HP_ALIGN pragma takes a parameter indicating which alignment mode to use. Not all modes are available on all HP platforms; the NATURAL alignment mode is the most widely available on HP-UX. This mode is the recommended standard. The syntax for the HP_ALIGN pragma is: #pragma HP_ALIGN align_mode
[ PUSH ] where align_mode is one of the following:
The HP_ALIGN pragma affects struct and union definitions as well as typedef declarations. It causes data objects that are later declared using these types to have the size and alignment as specified by the pragma. The alignment pragma in effect at the time of data type declaration has significance. The alignment pragma in effect at the time of data type declaration has precedence over the alignment pragma in effect when space for a data object of the previously declared type is allocated. The HP_ALIGN pragma allows you to control data storage allocation and alignment of structures, unions, and typedefs.
The HP_ALIGN pragma takes a parameter that specifies the alignment mode for example:
There is also an optional parameter PUSH, which saves the current alignment mode before setting the specified mode as the new alignment mode. For example, in the following sequence:
the current alignment mode is saved on the stack. It is then set to the new alignment mode, NOPADDING. The PUSHed alignment mode can be retrieved later by doing a
If the last alignment mode PUSHed on the stack was NOPADDING, the current alignment mode would now be NOPADDING. If only one alignment mode is used throughout the entire file, this pragma is straightforward to use and to understand. However, when a different mode is introduced in the middle of the file, you should be aware of its implications and effects. The key to understanding HP_ALIGN is the following concept: typedefs and struct or union types retain their original alignment mode throughout the entire file. Therefore, when a type with one alignment is used in a different alignment mode, it will still keep its original alignment. This feature may lead to confusion when you have a typedef, structure or union of one alignment nested inside a typedef, structure or union of another alignment. Here are some examples of the most common misunderstandings. The alignment pragma will affect typedef, struct, and union types. Therefore, in the following declaration:
int32 is not equivalent to int. To illustrate:
Compiling this with -Aa -c will give two warnings:
These warnings occur because the actual pointer value of bad may not be as strictly aligned as the pointer type routine expects. This may lead to run-time bus errors in the called function if it dereferences the misaligned pointer. In the WORD alignment modes, the members of a structure whose sizes are larger than 2 bytes are aligned on a 2-byte boundary. However, this is only true if those member types are scalar or have been previously declared under the same alignment mode. If the member type is a typedef, struct, or union type which has been declared previously under a different alignment mode, it will retain its original alignment, regardless of current alignment mode in effect. For example:
Although the size of my_int is greater than 2 bytes, because it was declared previously under HPUX_NATURAL with the alignment of 4 bytes it will be aligned on a 4-byte boundary, causing the entire struct st to be aligned on a 4-byte boundary. Compiling with the +m option to show the offsets of the identifiers (offsets given as "byte-offset @ bit-offset" in hexadecimal), you will get the following output:
The resulting size of foo is 8 bytes, with 4-byte alignment. If you change the type of member i in struct st to be a simple int type, then you will get the following result:
This time, the resulting size of foo is 6 bytes, with 2-byte alignment. Often, you might mix typedefs and alignments without being aware of the actual alignment of the data types. What may appear to be correct usages of these data types may turn out to be causes for misaligned pointers and run-time bus errors, among other things. For example, consider the following program.
This code is not written safely. Although struct s is declared under NOPADDING alignment mode, it has 2-byte alignment due to the typedef for ushort. However, a pointer to struct s can be assigned an address that can point to anywhere in the char array (including odd addresses). If the function get_index always returns an even number, you will not run into any problems, because it will always be 2-byte aligned. However, if the index happens to be an odd number, &myBuffer[index] will be an odd address. Dereferencing that pointer to store into a 2-byte aligned member will result in a run-time bus error. Below are some examples of what you can do to avoid such behavior.
As mentioned above, the HP_ALIGN pragma must have a global scope; it must be outside of any function or enclosing structure or union. For example, suppose you have the following sequence of pragmas:
Variables declared of type struct string_1, are aligned according to the HPUX_WORD alignment mode. Variables declared of type struct car, are aligned according to the HPUX_NATURAL alignment mode. Variables declared of type struct bus are aligned according to HPUX_WORD. Be careful when using pointers to access non-natively aligned data types within structures and unions. Alignment information is significant, as pointers may be dereferenced with either 8-bit, 16-bit, or 32-bit machine instructions. Dereferencing a pointer with an incompatible machine instruction usually results in a run-time error. HP C permanently changes the size and alignment information of typedefs defined within the scope of an HP_ALIGN pragma. It makes data objects, such as pointers, declared by using typedefs, compatible with similar objects defined within the scope of the pragma. For example, a pointer to an integer type declared with a typedef that is affected by the HP_ALIGN pragma will be dereferenced safely when it points to an integer object whose alignment is the same as that specified in the pragma. The typedef alignment information is persistent outside the scope of the HP_ALIGN pragma. An object declared with a typedef will have the same storage and alignment as all other objects declared with the same typedef, regardless of the location of other HP_ALIGN pragma statements in the program. There is a slight performance penalty for using non-native data alignments. The compiler generates slower but safe code for dereferencing non-natively aligned data. It generates more efficient code for natively aligned data. The following program generates a run-time error because a pointer that expects word-aligned data is used to access a half-word aligned item:
The following program works as expected because the pointer has the same alignment as the structure:
An alternative to using the HP_ALIGN pragma and typedefs to control non-natively aligned pointers is to use the +ubytes compiler option of HP C/HP-UX. The +ubytes forces all pointer dereferences to assume that data is aligned on 8-bit, 16-bit, or 32-bit addresses. The value of bytes can be 1 (8-bit), 2 (16-bit), or 4 (32-bit). This option can be used when accessing non-natively aligned data with pointers that would otherwise be natively aligned. This option can be useful with code that generates the compiler warning message
and aborts with a run-time error. The +ubytes option affects all pointer dereferences within the source file. It can have a noticeable, negative impact on performance.
One way to avoid trouble caused by differences in data alignment is to define structures so they are aligned the same on different systems. To do this, use padding bytes — that is, dummy variables to align fields the same way on different architectures. For example, use:
|
|