7

Take this example

class A
{
public:
  int a; 
  char b;
  int c;
};

Every compiler (for x86, 32 or 64 bit) I see allocates 12 bytes for class A, instead of 9. So they are aligning b to the integer boundary or bus boundary you can say. My question is if this is in C++ standard to do so and if there are any compilers who does not do like that.

pythonic
  • 20,589
  • 43
  • 136
  • 219

5 Answers5

17

The C++ standard specifies that:

  • objects have an alignment requirement of which their size is a multiple (so if int is 4 bytes wide, then it requires an alignment of 1, 2 or 4 bytes, depending on the implementation).
  • member objects (if they are not separated by access specifiers such as public) are all allocated in the order they're declared
  • and they are allocated respecting their alignment requirements.

So no, the standard doesn't say exactly that the class should have a size of 12 bytes.

But it does say that b should be allocated after a, and that c should be allocated after b.

On a platform where int is 4 bytes wide, and requires 4-byte alignment, this leaves 12 bytes as the smallest valid size:

  • a takes the first 4 bytes
  • b takes one byte
  • c needs 4 bytes, but must be allocated on a 4-byte boundary. b ended one byte past such a boundary, so the next valid position to place c at is found by inserting 3 bytes of padding.

So the total size of the class ends up being the size of the members (4 + 1 + 4 = 9) plus three bytes of padding, for a total of 12.

There is another rule which has no effect here, but which would matter if you had defined the members in the order a, c, b instead.

The containing class (A) inherits the alignment requirement from the strictest-aligned member object. That is, because it contains an int, it has the same alignment requirement as an int does. And because the object's total size must be a multiple of its alignment requirement, a class containing the members in the order a, b, c would still require 12 bytes of storage. It'd just shift the 3 bytes of padding to the end of the class, instead of between b and c.

However, in some other cases, reordering members in descending order of size can sometimes reduce the size of a class.

Suppose we'd had a class like this instead:

class B {
  char a;
  double b;
  int c;
};

This would have required 24 bytes of storage (1 bytes for a, 8 byte for b, and 4 bytes for c, but then to ensure b ends up on an 8-byte boundary, we'd need 7 bytes of padding between a and b, and to ensure that the whole class ends up with a size that is a multiple of 8, we need another 4 bytes after c.

But reordering the members according to size, like this:

class B {
  double b;
  int c;
  char a;
};

results in a class requiring only 16 bytes:

the same 1 + 4 + 8 bytes for the member objects themselves, but now c is already aligned on a 4-byte boundary (because it comes after b which ends on an 8-byte boundary), and a never needs any alignment, so the only alignment we need is to ensure that B has a size that is a multiple of 8. The members take 13 bytes, so we can add 3 bytes of padding, and the class ends up at 16 bytes, 33% smaller than the first version.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 1
    You might also point out that on most architectures, accessing a mis-aligned object will result in some sort of a bus error, which will cause the program to crash. On an Intel, the program won't crash, but it will still run a lot slower if the data is misaligned. – James Kanze Nov 08 '12 at 15:27
1

It is possible to control structure packing at compile-time, using pragma directives. See #Pragma Pack

For example, the following does not align the members, and places them right next to each other.

#pragma pack(push, 1)
struct A4
{
  char a;
  double b;
  char c;
};
#pragma pack(pop)

Example from here.

GCC also supports pragma pack. The directive isn't the part of some standard, but a lot of compilers do support it.


However, there shouldn't be a reason to do the above. The compiler aligns them to speed up access to members, and there should be no reason to change that.

Anirudh Ramanathan
  • 46,179
  • 22
  • 132
  • 191
  • But is it defined in C++ standard that the default case would to be to align to bus boundary. – pythonic Nov 08 '12 at 14:07
  • 1
    [**GCC**](http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html) supports a set of #pragma directives, but they support that because MS's compilers do as well. The `pack` directive isn't a part of any standard AFAIK. – Anirudh Ramanathan Nov 08 '12 at 14:13
  • 3
    In fact, `#pragma` _anything_ is by definition not part of any standard. – MSalters Nov 08 '12 at 14:16
  • @user1018562 It's defined in C++ that an implementation can add padding pretty much where ever it wants, except at the beginning of a struct. (In practice, of course, no implementation will add padding unless it feels it necessary, either to avoid crashing the program or, in the case of Intel, to avoid unnecessarily slowing the process down.) – James Kanze Nov 08 '12 at 15:29
  • 1
    *"There shouldn't be a reason to do the above"* There are use cases for this, such as [packing structures for network transport](http://stackoverflow.com/a/1593691/265575). – Justin ᚅᚔᚈᚄᚒᚔ Nov 08 '12 at 15:47
0

Standard allows the compilers to padd more bytes if necessary. It basically depends on the architecture of the host rather than compiler.

balki
  • 26,394
  • 30
  • 105
  • 151
0

Yes, alignment is mentioned everywhere in the standard, mainly section 3.11 (Alignment). It is platform-dependent, so any program that depends on the actual size of an object is inherently non-portable.

Gorpik
  • 10,940
  • 4
  • 36
  • 56
0

In your case 'b' is always correctly aligned. It's padded to align c in 32-bit boundaries. Even though there is some room for specific implementations, most compilers follow the rule of aligning 2 and 4-byte variables to 2 and 4 byte boundaries.

In 32-bit systems also doubles and long ints (8 bytes) are aligned to 4-byte boundaries, but aligned to 8-byte in 64-bit systems.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
  • How the systems aligns different types is very implementation dependent. I've used 32 bit systems where doubles were aligned on 8 byte boundaries, and I've seen all sorts of alignments for `long double`. – James Kanze Nov 08 '12 at 15:31
  • Well yes, maybe long double is pushing the limits of compatibility. – Aki Suihkonen Nov 08 '12 at 19:40