A Query Regarding Structure Alignment

closed account (zb0S216C)
I'm trying to get my head around the whole structure padding thing. I know that it helps the CPU to read data from memory in words, but the issue I'm having is how the data members are aligned.

I read an article[1] that basically said if the structure is divisible by 4, the structure is aligned. Here's a quote:

"For example, if you have 1 char variable (1-byte) and 1 int variable (4-byte) in a struct, the compiler will pads 3 bytes between these two variables. Therefore, the total size of this struct variable is 8 bytes, instead of 5 bytes. By doing this, the address of this struct data is divisible evenly by 4."

Based on the latter quote, I came to this:

1
2
3
4
5
6
// A
struct Data
{
    char A;
    int B;
};

The compiler will insert 3-bytes between A and B so that A is properly aligned to B. The structure is divisible by 4.

1
2
3
4
5
6
// B
struct Data
{
    int A;
    char B;
};

The compiler will add 3-bytes between A and B so that B is aligned to A. The structure is again divisible by 4.

So, in general, if the size of A is greater than B, B is aligned to A. If the size of A is smaller than B, A is aligned to B.

Am I right on this? I get the sneaky suspicion that I'm not, because I swear these grey hairs weren't here before.

References:
[1] http://www.songho.ca/misc/alignment/dataalign.html


Wazzak
It appears that they're indicating that the struct gets aligned to the largest data member of the the struct.
The struct (or union, class) member variables must be aligned to the highest bytes of the size of any member variables to prevent performance penalties.


So in:
1
2
3
4
5
6
7
// A
struct Data
{
    char A;
        // 3 bytes of padding
    int B; // Aligned to B (4 bytes)
};


And:
1
2
3
4
5
6
7
// B
struct Data
{
    int A; // Aligned to A (4 bytes)
    char B;
        // 3 bytes of padding
};


EDIT: Heh...post number 666.
Last edited on
closed account (zb0S216C)
Thanks for your reply, ciphermagi.

OK, that's confirmed, then. In example B, is the padding inserted after B or before it? For example:

1
2
3
4
5
6
7
8
// B
struct Data
{
    int A;
    // 3-Bytes padding here?...
    char B;
    // ...or here?
};


Songho wrote:
The struct (or union, class) member variables must be aligned to the highest bytes of the size of any member variables to prevent performance penalties.

So what about this:

1
2
3
4
5
6
7
8
9
// C
struct Data
{
    char A;
    // 3-Bytes...
    int B;
    // ?-Bytes...
    double C;
};

My compiler yields 16-bytes for the size of example C, which leads me to believe that there's no padding between B and C. Hmmm...

Wazzak
I got the impression that the padding was inserted after, but looking at it again, I think I was looking at the misaligned data picture there, so...I'm not sure that that's really very well covered.

As far as example C, since it's aligning to 8, and without the specificity of when the data gets padded, I suppose that there could be any combination of padding, incl.:
3,0,0,0
2,1,0,0
2,0,1,0
2,0,0,1
1,1,1,0
1,1,0,1
1,0,1,1
etc...
closed account (zb0S216C)
So, it's not possible to determine where the padding goes, but it is possible to determine the size of a structure?

Wazzak
Not sure. I can't see anything definitive in the article one way or the other about whether or not the determination can be made. From my end, it would be speculation.
> it's not possible to determine where the padding goes

The alignment requirements of a type is implementation dependent; for a particular implementation, all information about the layout can be determined:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <type_traits>
#include <cstddef>
#include <iostream>

#define print(a) ( std::cout << #a << ": " << a << '\n' )

int main()
{
    struct A { char c ; int i ; short s ; } ;

    print( std::alignment_of<char>::value ) ; // 1 by definition
    print( std::alignment_of<int>::value ) ; // implementation defined
    print( std::alignment_of<short>::value ) ; // implementation defined
    print( std::alignment_of<A>::value ) ;

    print( sizeof(A) ) ;

    print( offsetof(A,c) ) ; // 0 by definition
    print( offsetof(A,i) ) ;
    print( offsetof(A,s) ) ;
}


On my implementation:
1
2
3
4
5
6
7
8
std::alignment_of<char>::value: 1
std::alignment_of<int>::value: 4
std::alignment_of<short>::value: 2
std::alignment_of<A>::value: 4
sizeof(A): 12
offsetof(A,c): 0
offsetof(A,i): 4
offsetof(A,s): 8
closed account (zb0S216C)
Hmmm.... This subject seems to go deeper than I initially thought.

Thanks, ciphermagi, and JLBorges :) I'll see what I can dig up.

Thanks again.

Wazzak
Perhaps this will help:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

struct Data  {  char c;  int i1;  int i2; } Test1;
struct Data2 {  int  i1; char c;  int i2; } Test2;

int main()
{
    std::cout << std::hex;
    std::cout << (void*)&Test1.c << std::endl;
    std::cout << &Test1.i1       << std::endl;
    std::cout << &Test1.i2       << std::endl;
    std::cout << &Test2.i1       << std::endl;
    std::cout << (void*)&Test2.c << std::endl;
    std::cout << &Test2.i2       << std::endl;
}
01353378 (Space between char and int is 4 -- Padding is AFTER char)
0135337C (Space between int and int is 4 -- No padding)
01353380
0135336C (Space between int and char is 4 -- No padding)
01353370 (Space between char and int is 4 -- Padding AFTER char)
01353374
Press any key to continue . . .


Edit: Regarding the size of the structure:
1
2
3
	cout << std::dec;
	cout << sizeof(Data)    << endl;
	cout << sizeof(Data2)   << endl;
12
12
Press any key to continue . . .

Therefore the structure is 4 X sizeof(int). Whether that is because an int is 4 bytes or because it is the largest container, I don't know.

Edit2:
1
2
3
4
5
6
7
#include <iostream>
struct Data {	char c;	int i;	double d; } Test1;

int main()
{
	std::cout << sizeof(Data) << std::endl;
}
16
Press any key to continue . . .

This means that it isn't padded to the next 4 bytes. It's padded to a multiple of the largest container size.

I would imagine that this is compiler-specific though unless it's actually dictated in the C++ standard.
Last edited on
> It's padded to a multiple of the largest container size.

Size has got nothing to do with it; alignment has got everything to do with it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <cstddef>
#include <iostream>

#define print(a) ( std::cout << #a << ": " << a << '\n' )

int main()
{
    struct A { short s[9] ; } ;
    struct B { A a ; double d ; int i ; } ;

    print( std::alignment_of<A>::value ) ;
    print( std::alignment_of<double>::value ) ;
    print( std::alignment_of<int>::value ) ;
    print( std::alignment_of<B>::value ) ; // max of the above
    std::cout << '\n' ;

    print( sizeof(A) ) ;
    print( sizeof(double) ) ;
    print( sizeof(int) ) ;
    print( sizeof(B) ) ; // >= sum of the above
    std::cout << '\n' ;


    print( offsetof(B,a) ) ;
    print( offsetof(B,d) ) ;
    print( offsetof(B,i) ) ;
}


On my implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
std::alignment_of<A>::value: 2
std::alignment_of<double>::value: 8
std::alignment_of<int>::value: 4
std::alignment_of<B>::value: 8

sizeof(A): 18
sizeof(double): 8
sizeof(int): 4
sizeof(B): 40

offsetof(B,a): 0
offsetof(B,d): 24
offsetof(B,i): 32
closed account (zb0S216C)
Thanks for the examples & explanations, Stewbond & JLBorges, I really appreciate it :)

Thanks again.

Wazzak
Topic archived. No new replies allowed.