C++ Data Member Alignment and Array Packing
Solution 1
An array of objects is required to be contiguous, so there's never padding between the objects, though padding can be added to the end of an object (producing nearly the same effect).
Given that you're working with char's, the assumptions are probably right more often than not, but the C++ standard certainly doesn't guarantee it. A different compiler, or even just a change in the flags passed to your current compiler could result in padding being inserted between the elements of the struct or following the last element of the struct, or both.
Solution 2
It would definitely be safer to do:
sizeof(foo) * SOME_NUM
Solution 3
If you copy your array like this you should use
memcpy(pBuff,listOfFoos,sizeof(listOfFoos));
This will always work as long as you allocated pBuff to the same size. This way you are making no assumptions on padding and alignment at all.
Most compilers align a struct or class to the required alignment of the largest type included. In your case of chars that means no alignment and padding, but if you add a short for example your class would be 6 bytes large with one byte of padding added between the last char and your short.
Solution 4
I think the reason that this works because all of the fields in the structure are char which align one. If there is at least one field that does not align 1, the alignment of the structure/class will not be 1 (the alignment will depends on the field order and alignment).
Let see some example:
#include <stdio.h>
#include <stddef.h>
typedef struct {
unsigned char a;
unsigned char b;
unsigned char c;
} Foo;
typedef struct {
unsigned short i;
unsigned char a;
unsigned char b;
unsigned char c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;
#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )
int main(void) {
printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}
When executed, the result is:
Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2
You can see that Bar and F_B has alignment 2 so that its field i will be properly aligned. You can also see that Size of Bar is 6 and not 5. Similarly, the size of B_F (5 of Bar) is 30 and not 25.
So, if you is a hard code instead of sizeof(...)
, you will get a problem here.
Hope this helps.
Solution 5
For situations where stuff like this is used, and I can't avoid it, I try to make the compilation break when the presumptions no longer hold. I use something like the following (or Boost.StaticAssert if the situation allows):
static_assert(sizeof(foo) <= 3);
// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp) static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line) static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}
Related videos on Youtube
![Adam Holmberg](https://i.stack.imgur.com/zXFJg.jpg?s=256&g=1)
Adam Holmberg
Updated on April 16, 2022Comments
-
Adam Holmberg about 2 years
During a code review I've come across some code that defines a simple structure as follows:
class foo { unsigned char a; unsigned char b; unsigned char c; }
Elsewhere, an array of these objects is defined:
foo listOfFoos[SOME_NUM];
Later, the structures are raw-copied into a buffer:
memcpy(pBuff,listOfFoos,3*SOME_NUM);
This code relies on the assumptions that: a.) The size of foo is 3, and no padding is applied, and b.) An array of these objects is packed with no padding between them.
I've tried it with GNU on two platforms (RedHat 64b, Solaris 9), and it worked on both.
Are the assumptions above valid? If not, under what conditions (e.g. change in OS/compiler) might they fail?
-
rmeador over 14 yearsnot just safer, but clearer and gets rid of a magic number. +1
-
David Thornley over 14 yearsIt certainly wouldn't surprise me if a compiler decided it liked things on four-byte boundaries, and put a byte of padding at the end.
-
Adam Holmberg over 14 yearsYes, I agree with that. I guess I was more trying to get at the padding and array organization. Thanks.
-
nschmidt over 14 yearsthis does not account for padding between array elements though.
-
nschmidt over 14 yearssee my answer below. the safest way is to use sizeof(listOfFoos)
-
Jerry Coffin over 14 years@nschmidt: padding between array elements is not allowed in either C or C++.
-
nschmidt over 14 years@Jerry Coffin: you are right. The standard requires arrays to be contiguous. Padding occurs only within the struct/class.
-
JHBonarius over 2 yearsI know this is an old question, but I'm wondering: where in the c++ standard is it stated that padding can be added to the end of the object, and not the beginning? I read that it must be contiguous and that an array new-expressing may assign more space then required, but I cannot find any info that e.g. that the array object and the first element have the same address. [basic.compound] note 4 say this, must it doesn't seem to be a requirement. The standard doesn't seem to give clear explicit guarantees.
-
Jerry Coffin over 2 years@JHBonarius: The current standard only gives this guarantee with respect to standard layout objects and non-bitfield members. The normative text is at [class.mem]/26: "If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member if that member is not a bit-field."