Structure member alignment; unions

Default alignment

By default, members in a structure (a structure is a variable of an user-defined type) are placed (in memory) in the order they are defined. In the following example, offset of a is 0, offset of b is 4 (because size of a is 4), and size of structure is 8.

 

type T1 int'a int'b
T1 t
out "%i %i   %i" &t.a-&t &t.b-&t sizeof(t)

 

In some cases there may be gaps between members, because members are aligned so that the offset of a member is divisible by the size of the member. In the following example, offset of a is 0, offset of b is 4 (although size of a is 1), and size of structure is 8. There are 3 unused bytes between a and b, because b must be placed at nearest offset that is divisible by 4 (size of a).

 

type T2 byte'a int'b
T2 t
out "%i %i   %i" &t.a-&t &t.b-&t sizeof(t)

 

The address and size of a structure is divisible by the size of the largest integral member (max. 8 bytes). If a structure is a local variable, however, it is aligned on 4 bytes (like all local variables and function arguments).

Explicit alignment; unions

You can explicitly specify offsets of some members. It allows you to define unions (structures with overlapped members of different types) and structures with nonstandard member alignment. The member declaration must be preceded by the offset (integer number) in square brackets. Example:

 

type LHWORD int'i [0]word'lo [2]word'hi
LHWORD lh.i=0x00050007
out "LOWORD is %i, HIWORD is %i" lh.lo lh.hi
 Output: LOWORD is 7, HIWORD is 5

 

If offset begins with +, it is interpreted as offset from offset of previous member. Empty brackets means same offset as of previous member. Example:

 

type LHWORD int'i []word'lo [+2]word'hi

 

If offsets of all members are explicitly set, the size of the type also is explicitly set. In the following example it is 6 (normally it would be 8, because i would be at 4-byte offset):

 

type TYPE [0]word'w [2]int'i

 

QM 2.2.0. Implicit and explicit constructor/destructor/copy functions are not called for union members (except for the intrinsic VARIANT type). A warning is shown. It is rather a bug fix. QM cannot know which union member is valid at the time. Clearing wrong member may be catastrophic. These members should be cleared explicitly. For example, if an union member is an interface pointer, and you know it is valid at the time, call its hidden function Release to clear it. For BSTR, call SysFreeString. For ARRAY - SafeArrayDestroy. Also, there are special functions for some types, for example ReleaseStgMedium clears variables of STGMEDIUM type. All this is documented in the MSDN Library.

Anonymous types within types

You probably already know that members of user-defined types can be other user-defined types. Example:

 

type T1 int'a int'b
type T2 int'x T1'y
T2 t
t.y.a=5

 

It is also possible to define types within types (QM 2.2.0). It can be used to simplify conversion between C++ and QM declarations, because in C++ it is used to define complex unions. These nested types don't have a type name and a member name, but can have specified offset. Their members are accessed directly. To define such nested types, enclose their members in {}. Examples:

 

type T2 int'x {int'a int'b}
T2 t
t.a=5

type LHWORD int'i []{word'lo [2]word'hi}
LHWORD lh.i=0x00050007
out "LOWORD is %i, HIWORD is %i" lh.lo lh.hi