9

Visual C++ offers both a compiler switch (/Zp) and the pack pragma to affect the aligment of struct members. However, I seem to have some misconception as to how they work.

According to MSDN, for a given alignment value n,

The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.

Let's assume a pack value of 8 bytes (which is the default). Within a struct, I'd think that any member whose size is less than 8 bytes will be at an offset that is a multiple of its own size. Any member whose size is 8 bytes or more will be at an offset that is a multiple of 8 bytes.

Now take the following program:

#include <tchar.h>

#pragma pack(8)

struct Foo {
    int i1;
    int i2;
    char c;
};

struct Bar {
    char c;
    Foo foo;
};

int _tmain(int argc, _TCHAR* argv[]) {
    int fooSize = sizeof(Foo); // yields 12
    Bar bar;
    int fooOffset = ((int) &bar.foo) - ((int) &bar); // yields 4

    return 0;
}

The Foo structure is 12 bytes in size. So within Bar, I'd expect the Foo member to be at offset 8 (a multiple of 8) while actually it's at offset 4. Why is that?

Also, Foo really only has 4+4+1 = 9 bytes of data. The compiler automatically adds padding bytes at the end. But again, given an alignment value of 8 bytes, shouldn't it pad to a multiple of 8 rather than 4?

Any clarification appreciated!

2
  • Are you sure your int is only 4 bytes? What machine are you running this on? Commented Apr 21, 2012 at 10:14
  • @Tony: It's a 32 bit application. If an int was 8 bytes instead of 4, a Foo with two of those couldn't well be only 12 bytes. :-) Commented Apr 22, 2012 at 12:00

3 Answers 3

8

Your excerpt explains this, "whichever is smaller". On a 32-bit platform, an int is 4 bytes. 4 is smaller than 8. So it has a 4-byte alignment.

The pack pragma causes things to be packed, not unpacked. It won't pad unless it has a reason to.

Sign up to request clarification or add additional context in comments.

4 Comments

Wait a minute. I have a Bar struct with a Foo member. So I assumed that "the size of the member" referred to Foo's size, which is 12. Are you saying that the compiler is actually looking /inside/ Foo and (probably recursively) looking for the largest member of /primitive/ type? If so, is there some (online) reference for this behavior?
The compiler knows every type's alignment requirement. The alignment requirement for a Foo is 4. It's not the largest type, but the requirement for the outer structure, which will typically be the largest alignment requirement of any contained type, but isn't always. (It can't be the size of the full type. Otherwise, an integer followed by a 9,000 byte structure would have almost 9,000 bytes of padding. That's just silly. The MSDN page is talking about simple types whose alignment requirement is equal to their size.)
Thanks, that would explain the behavior. - Regarding your argument that it can't be the full struct size: I thought that was precisely the reason for the "whichever is smaller" rule. Your 9,000 byte struct, being larger than 8 bytes, would be aligned to a multiple of 8 instead, thereby wasting only 4 bytes rather than 8,996. So to me, the MSDN algorithm actually made sense. Too bad it seems to be only half the truth!
Oh, I gotcha. Yeah, that does make sense. But there's no reason to ever pad a structure to larger than its alignment requirement, so the compiler doesn't do that unless specifically told to pad.
6

Keep in mind why alignment matters in the first place. It is there to allow the cpu to read memory quickly without having to multiplex the bytes. The cpu nevers read a struct in one gulp, it accesses only its members. So the fact that the Foo struct is 12 bytes is immaterial. Only the alignment of its members matters. Given that no Foo member has an alignment requirement larger than 4, the Bar.foo member only needs to align to 4.

Foo being 12 bytes instead of 9 could use an explanation as well. The compiler adds 3 bytes of padding to the end so that an array of Foo still has the members aligned correctly for each array element.

Comments

4

As your quote says - to test an 8 byte alignment you needs 8 or more byte data types. Here's a sample with some explicit sized types. Also, putting the small element at the end will not show up the padding as it can be dropped from the end of the structure.

#include <stdio.h>
int
main(int argc, char *argv[])
{
    struct S {
        __int64 a;
        __int8  b;
        __int64 c;
    };
#pragma pack(push,1)
    struct T {
        __int64 a;
        __int8  b;
        __int64 c;
    };
#pragma pack(pop)
#pragma pack(push,8)
    struct U {
        __int64 a;
        __int8  b;
        __int64 c;
    };
    struct B {
        __int8 c;
        struct U s;
    };
#pragma pack(pop)

    printf("S %d T %d U %d B %d\n",
           sizeof(struct S), sizeof(struct T),
           sizeof(struct U), sizeof(struct B));
    return 0;
}

Compiling this with VC 2010:

C:\src>cl -nologo -W3 -Od packing.c && packing.exe
packing.c
S 24 T 17 U 24 B 32

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.