13
votes

Given a POD-struct (in C++03) or a standard layout type (in C++11), with all members having a fundamental alignment requirement, is it true that every member is guaranteed to be aligned according to its alignment requirement?

In other words, for all members m_k in { m0 ... mn } of standard layout type S,

  struct S {
    T0 m0;
    T1 m1;
    ...
    TN mn;
  };

is the following expression guaranteed to evaluate to true?

  (offsetof(S,m_k) % alignof(decltype(S::m_k))) == 0

Please give answers for both C++03 and C++11 and cite the relevant parts of the standard. Supporting evidence from C standards would also be helpful.


My reading of the C++03 standard (ISO/IEC 14882:2003(E)) is that it is silent regarding the alignment of members within a POD-struct, except for the first member. The relevant paragraphs are:

In the language of the specification, an object is a "region of storage":

1.8 The C + + object model [intro.object]

1.8/1 The constructs in a C + + program create, destroy, refer to, access, and manipulate objects. An object is a region of storage. ...

Objects are allocated according to their alignment requirement:

3.9 Types [basic.types]

3.9/5 Object types have alignment requirements (3.9.1, 3.9.2). The alignment of a complete object type is an implementation-defined integer value representing a number of bytes; an object is allocated at an address that meets the alignment requirements of its object type.

Fundamental types have alignment requirements:

3.9.1 Fundamental types [basic.fundamental]

3.9.1/3 For each of the signed integer types, there exists a corresponding (but different) unsigned integer type: "unsigned char", "unsigned short int", "unsigned int", and "unsigned long int," each of which occupies the same amount of storage and has the same alignment requirements (3.9) as the corresponding signed integer type;...

Padding may occur due to "implementation alignment requirements":

9.2 Class members [class.mem]

9.2/12 Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

Does the word "allocated" in 9.2/12 have the same meaning as "allocated" in 3.9/5? Most uses of "allocated" in the spec refer to dynamic storage allocation, not struct-internal layout. The use of may in 9.2/12 seems to imply that the alignment requirements of 3.9/5 and 3.9.1/3 might not be strictly required for struct members.

The first member of a POD-struct will be aligned according to the alignment requirement of the struct:

9.2/17 A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [Note: There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment. ]

[Emphasis added in all of the above quotes.]

2
The word "object" in the reference to alignment requirements means "object", not just "most compounded object". (viz. the definition of compound types in which it says "classes containing a sequence of objects of various types".)rici
@rici: thanks. I've added the definition of object from the spec: "a region of storage".Ross Bencina
Ross: correct. A region of storage. Not an independent region of storage. An object within a compound object is still an object and therefore still subject to alignment requirements. The conditional ("might") in 9.2/17 is simply a recognition that the alignment requirement might or might not require padding; it is not permission to avoid the alignment requirement.rici

2 Answers

10
votes

Each element of a POD struct is itself an object, and objects can only be allocated in accordance with the alignment requirements for those objects. The alignment requirements may change, though, due to the fact that something is a sub-object of another object ([basic.align]/1, 2:

1 Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated. An object type imposes an alignment requirement on every object of that type; stricter alignment can be requested using the alignment specifier (7.6.2).

2 A fundamental alignment is represented by an alignment less than or equal to the greatest alignment supported by the implementation in all contexts, which is equal to alignof(std::max_align_t) (18.2). The alignment required for a type might be different when it is used as the type of a complete object and when it is used as the type of a subobject. [Example:

struct B { long double d; };
struct D : virtual B { char c; }

When D is the type of a complete object, it will have a subobject of type B, so it must be aligned appropriately for a long double. If D appears as a subobject of another object that also has B as a virtual base class, the B subobject might be part of a different subobject, reducing the alignment requirements on the D subobject.—end example ] The result of the alignof operator reflects the alignment requirement of the type in the complete-object case.

[emphasis added]

Although the examples refer to a sub-object via inheritance, the normative wording just refers to sub-objects in general, so I believe the same rules apply, so on one hand you can assume that each sub-object is aligned so that it can be accessed. On the other hand, no you can't necessarily assume that will be the same alignment that alignof gives you.

[The reference is from N4296, but I believe the same applies to all recent versions. C++98/03, of course, didn't have alignof at all, but I believe the same basic principle applies--members will be aligned so they can be used, but that alignment requirement isn't necessarily the same as when they're used as independent objects.]

6
votes

There are two distinct alignment requirements for each type. One corresponds to complete objects, the other one to subobjects. [basic.align]/2:

The alignment required for a type might be different when it is used as the type of a complete object and when it is used as the type of a subobject. [ Example:

struct B { long double d; };
struct D : virtual B { char c; };

When D is the type of a complete object, it will have a subobject of type B, so it must be aligned appropriately for a long double. If D appears as a subobject of another object that also has B as a virtual base class, the B subobject might be part of a different subobject, reducing the alignment requirements on the D subobject. —end example ] The result of the alignof operator reflects the alignment requirement of the type in the complete-object case.

I can't think of any specific example for member subobjects - and there assuredly is none! -, but the above paragraph concedes the possibility that a member subobject has weaker alignment than alignof will yield, which would fail your condition.