C++ Data Alignment /Member Order & Inheritance

c++ data alignment /member order & inheritance

Really you’re asking a lot of different questions here, so I’m going to do my best to answer each one in turn.

First you want to know how data members are aligned. Member alignment is compiler defined, but because of how CPUs deal with misaligned data, they all tend to follow the same

guideline that structures should be aligned based on the most restrictive member (which is usually, but not always, the largest intrinsic type), and strucutres are always aligned such that elements of an array are all aligned the same.

For example:

struct some_object
{
char c;
double d;
int i;
};

This struct would be 24 bytes. Because the class contains a double it will be 8 byte aligned, meaning the char will be padded by 7 bytes, and the int will be padded by 4 to ensure that in an array of some_object, all elements would be 8 byte aligned (the size of an object is always a multiple of its alignment). Generally speaking this is compiler dependent, although you will find that for a given processor architecture, most compilers align data the same.

The second thing you mention is derived class members. Ordering and alignment of derived classes is kinda a pain. Classes individually follow the rules I described above for structs, but when you start talking about inheritance you get into messy turf. Given the following classes:

class base
{
int i;
};

class derived : public base // same for private inheritance
{
int k;
};

class derived2 : public derived
{
int l;
};

class derived3 : public derived, public derived2
{
int m;
};

class derived4 : public virtual base
{
int n;
};

class derived5 : public virtual base
{
int o;
};

class derived6 : public derived4, public derived5
{
int p;
};

The memory layout for base would be:

int i // base

The memory layout for derived would be:

int i // base
int k // derived

The memory layout for derived2 would be:

int i // base
int k // derived
int l // derived2

The memory layout for derived3 would be:

int i // base
int k // derived
int i // base
int k // derived
int l // derived2
int m // derived3

You may note that base and derived each appear twice here. That is the wonder of multiple inheritance.

To get around that we have virtual inheritance.

The memory layout for derived4 would be:

void* base_ptr // implementation defined ptr that allows to find base
int n // derived4
int i // base

The memory layout for derived5 would be:

void* base_ptr // implementation defined ptr that allows to find base
int o // derived5
int i // base

The memory layout for derived6 would be:

void* base_ptr // implementation defined ptr that allows to find base
int n // derived4
void* base_ptr2 // implementation defined ptr that allows to find base
int o // derived5
int i // base

You will note that derived 4, 5, and 6 all have a pointer to the base object. This is necessary so that when calling any of base's functions it has an object to pass to those functions. This structure is compiler dependent because it isn't specified in the language spec, but almost all compilers implement it the same.

Things get more complicated when you start talking about virtual functions, but again, most compilers implement them the same as well. Take the following classes:

class vbase
{
virtual void foo() {}
};

class vbase2
{
virtual void bar() {}
};

class vderived : public vbase
{
virtual void bar() {}
virtual void bar2() {}
};

class vderived2 : public vbase, public vbase2
{
};

Each of these classes contains at least one virtual function.

The memory layout for vbase would be:

void* vfptr // vbase

The memory layout for vbase2 would be:

void* vfptr // vbase2

The memory layout for vderived would be:

void* vfptr // vderived

The memory layout for vderived2 would be:

void* vfptr // vbase
void* vfptr // vbase2

There are a lot of things people don't understand about how vftables work. The first thing to understand is that classes only store pointers to vftables, not whole vftables.

What that means is that no matter how many virtual functions a class has, it will only have one vftable, unless it inherits a vftable from somewhere else via multiple inheritance. Pretty much all compilers put the vftable pointer before the rest of the members of the class. That means that you may have some padding between the vftable pointer and the class's members.

I can also tell you that almost all compilers implement the pragma pack capabilities which allow you to manually force structure alignment. Generally you don't want to do that unless you really know what you are doing, but it is there, and sometimes it is necessary.

The last thing you asked is if you can control ordering. You always control ordering. The compiler will always order things in the order you write them in. I hope this long-winded explanation hits everything you need to know.

C struct inheritance pointer alignment

Kudos for your presentation.

I think your implementation should work fine, because C guarantees that the address of a struct is the address of its initial member. Put aside the statements C makes about alignment of struct-members, this guarantee should mean that, as long as your implementation always puts Link as first member, this should not cause alignment issues.

from here: C99 §6.7.2.1:

13 Within a structure object, the non-bit-field members and the units
in which bit-fields reside have addresses that increase in the order
in which they are declared. A pointer to a structure object, suitably
converted, points to its initial member (or if that member is a
bit-field, then to the unit in which it resides), and vice versa.
There may be unnamed padding within a structure object, but not at its
beginning

This should be what you meant to say about Base * and Derived *, although no such thing exists in pure C. They are just structs which happen to have the same memory layout.

However I think it is a bit brittle to implement it like this, because Node and Link directly depend on each other. If you were to change the structure of Node, your code would become invalid. At the moment I don't see the point in having an extra struct Link, apart from you being able to just write a new Node for a new type reusing Link.

There is actually a linked list implementation that immediately came to mind when I saw your post and works in a way very similar to the way you intend to use your list: the kernel list

It uses the same List-element (list_head):

struct list_head {
struct list_head *next, *prev;
};

It contains this function macro:

#define list_for_each_entry(pos, head, member)                          \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))

If you look at the way the macro is implemented, you will see that it offers iteration over the entries of a list without knowing anything about the layout of the entries the list is contained in. Assuming I interpret your intention right, I think this is the way you would want it to be.

C++ Member and vtable order in diamond (multiple) virtual inheritance

what is the correct ordering of member variables and vtable pointers?

There is no "correct ordering". This is not specified in the C++ standard. Each compiler is free to arrange the memory layout of this class hierarchy in any fashion that's compliant with the C++ standard.

A compiler may choose the layout of the class, in this situation, according to some fixed set of rules. Or, a compiler may try to optimize amongst several possibilities and pick a layout that can take advantage of hardware-related factors, like preferred alignment of objects, to try to minimize any required padding. This is entirely up to the compiler.

Furthermore: if you review the text of the C++ standard you will not find even a single mention of anything called a "vtable". It's not there. This is just the most common implementation mechanism for virtual class methods and virtual inheritance. Instead of a pointer, it would be perfectly compliant with the C++ standard for a C++ compiler to use a two-byte index into a table, instead of an entire pointer -- into a table containing records that define each particular class's virtual properties. This would work perfectly fine as long as the number of all classes with virtual properties does not exceed 65536.

In other words: you have no guarantees, whatsoever, what this class's layout will be, in memory, or what its size is. It is not specified in the C++ standard and is entirely implementation-defined.

Confused about c++ base class layout

In the struct case consider this program:

void f(C& cx)
{
cx.c = 'x';
}

int main()
{
D d{};
d.D::val = 'y';
f(d);
std::cout << d.D::val << '\n';
}

This code has to output y.

On your system, the A and C structs have size of 8 since there is a member with size 4 and a char, and the struct must be correctly aligned for its largest member. These structs have 4 bytes of int, 1 byte of char, and 3 padding bytes.

The assignment cx.c = 5; is allowed to modify the padding (any struct assignment can modify the struct padding). Therefore that padding cannot be used to store base class elements.

However no analogous example is possible with A and B because the data members of A are private. There cannot be a function void f(A& ax) { ax.c = 'x'; } so this concern does not arise, and the compiler can get away with using the padding area of A to store derived class members.


NB: Neither class is standard layout due to having data members in both the base and derived classes.

Misaligned address using virtual inheritance

Since alignment of std::function<void()> is 16 and size is 48 lets simplify. This code has the same behavior but is easier to understand:

struct alignas(16) A
{ char data[48]; };

struct B
{ char data; };

struct C : public virtual A, public B
{};

struct D : public virtual C
{};

int main()
{
D();
}

We have the following alignments and sizes:

                     |__A__|__B__|__C__|__D__|
alignment (bytes): | 16 | 1 | 16 | 16 |
size (bytes): | 48 | 1 | 64 | 80 |

Now lets see how this looks like in memory. More explanation on that can be found in this great answer.

  • A: char[48] + no padding == 48B
  • B: char[1] + no padding == 1B
  • C: A* + B + A + 7 bytes of padding (align to 16) == 64B
  • D: C* + C + 8 bytes of padding (align to 16) == 80B

Now it is easy to see that the offset of C inside D is 8 bytes, but C is aligned to 16. Thus error, which is helpfully accompanied by this pseudo-graphic

00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00
^

Here each zero is 1 byte.

UPDATE:
Where and how to place padding is up to a C++ compiler. Standard does not specify it. It looks like with the size of padding it has, clang is unable to align everything in D. One way to mitigate the misalignment is to design your classes carefully so that they have the same alignment (e.g., 8 bytes).

Memory alignment in C-structs

At least on most machines, a type is only ever aligned to a boundary as large as the type itself [Edit: you can't really demand any "more" alignment than that, because you have to be able to create arrays, and you can't insert padding into an array]. On your implementation, short is apparently 2 bytes, and int 4 bytes.

That means your first struct is aligned to a 2-byte boundary. Since all the members are 2 bytes apiece, no padding is inserted between them.

The second contains a 4-byte item, which gets aligned to a 4-byte boundary. Since it's preceded by 6 bytes, 2 bytes of padding is inserted between v3 and i, giving 6 bytes of data in the shorts, two bytes of padding, and 4 more bytes of data in the int for a total of 12.

C++ POD struct inheritance? Are there any guarantees about the memory layout of derived members

No, the layout is not guaranteed. The only guarantees are for standard-layout classes; and one of the conditions of such a class is that it

either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members

In other words, all data members must be in the same class, not in more than one.

C++ Standard On The Address of Inherited Members

Standard, section 1.8 is about the C++ object model.

It doesn't say much: the object is a region of memory (but it may contain unused zones for alignment purpose) and can contain subobjects (member subobjects or base class subobjects, or array subobjects). From the definition of a complete object you can infer that subobjects are included in the memory region of their object. And it says that two distinct objects that are neither bitfields nor base class subobjects shall have distinct adresses.

Section 9.2/15 gives some additional information about the order of the addresess within an object :

Nonstatic data members of a (non-union) class with the same access
control are allocated so that later members have higher
addresses within a class object. The order of allocation of non-static
data members with different access control is unspecified.
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 and
virtual base classes.

There are a couple of sentences about unions when all the union members are structs starting with the same sequence of types. Then it is allowed to "inspect the common parts", from which you can deduce that they must have the same address.

Finally, I've found a last one in 9.2/21:

A pointer to a standard-layout 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.

And basically, that's it. You see there is a lot of implementation defined stuff here, about the exact layout of each objects.

Unfortunately, you can't even say much about the address of a base class subobject and it's derived object: there could be multiple inheritance as well. So the standard doesn't use address assumptions: it rathers states things like : "If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class"

The effects of data (mis)alignment

All your code does is to test to see how quickly the processor can copy memory. The more memory, the slower the copy. The alignment of the individual members within the structure is irrelevant to the speed of the copy, only the size of the structure matters.

If you want to see the effect of the alignment, you need to write code that actually access individual unaligned structure members. For instance, you could write a loop to increment the data3 members of each structure. Depending on the architecture the compiler may realise that it has to use different instructions to perform the arithmetic; on x86 this is usually not the case and the compiler will emit natural looking code because the processor is capable with dealing with unaligned accesses. Some processors can actually read and write unaligned data at the same speed as aligned data. A trivial example of this is the 8088 as it only has an 8-bit data bus so all 16-bit instructions are emulated using two loads anyway, but the latest processors spend most of their time reading from cache lines and so the only time unaligned data might make a difference is when the data crosses a cache line.

If you want to induce a crash by misalignment then normally you need to cast pointers between different types. The compiler then may not always realise that your pointer may be misaligned and will not generate the correct instructions for a misaligned access. For instance you could attempt to invoke an SSE instruction on a cast char* pointer.



Related Topics



Leave a reply



Submit