Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ data alignment /member order & inheritance

How do data members get aligned / ordered if inheritance / multiple inheritance is used? Is this compiler specific?

Is there a way to specify in a derived class how the members (including the members from the base class) shall be ordered / aligned?

like image 535
genesys Avatar asked Jan 05 '10 14:01

genesys


People also ask

What is structure member alignment in C?

Data structure alignment is the way data is arranged and accessed in computer memory. Data alignment and Data structure padding are two different issues but are related to each other and together known as Data Structure alignment.

What does 4 byte aligned mean?

For instance, in a 32-bit architecture, the data may be aligned if the data is stored in four consecutive bytes and the first byte lies on a 4-byte boundary. Data alignment is the aligning of elements according to their natural alignment.

Why is aligned memory faster?

So, as everyone thinks memory is cheap, they just made the compiler align the data on the processor's chunk sizes so your code runs faster and more efficiently at the cost of wasted memory.

What is data processing alignment?

Data alignment is the process of harmonisation of the underlying concepts and definitions of Variables or units in order to produce values for measurement of Variables, which can be related to the maximum extent possible.


1 Answers

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.

like image 114
Beanz Avatar answered Oct 12 '22 07:10

Beanz