views:

199

answers:

2

I have a simple class in C++ that has an integer and a vtable:

class Something {

   virtual void sampleVirtualMethod();

   int someInteger;
};

If you look at the object layout for MSVC (using the /d1reportSingleClassLayout) you get:

class Something       size(8):
        +---
 0      | {vfptr}
 4      | someInteger
        +---

Which makes complete sense. 4 bytes for the vtable pointer and 4 bytes for the integer. The weird thing is when I add a double to the class:

class Something {    
    virtual void sampleVirtualMethod();
    int someInteger;
    **double someDouble;**
};

I get this object layout:

class Something       size(24):
        +---
 0      | {vfptr}
 8      | someInteger
        | <alignment member> (size=4)
16      | someDouble
        +---

Why is the difference between the 0 offset and someInteger 8 instead of 4? Did the vtable grow to 8 bytes somehow? No matter the order of when I add a double, this happens.

Thanks.

A: 

I can't answer your question directly because there is no good excuse for the compiler's behavior. Instead, I'll indulge myself in wild speculation since there hasn't been an answer yet.

I suspect a bug in the alignment algorithm that goes something like this:

  • a structure's alignment (i.e., placement of the first member) should be at least as wide as the alignment of its widest member
  • oops, forgot to count the virtual function table pointer as a member

If this bug exists I suspect it is left over from the compiler's early days as a 4-byte-aligned C compiler. Now the default for the compiler is /Zp8 meaning that every structure is aligned at least to 8 bytes, so there would be no need to fix the alignment of the "first" member in this case anyway.

Regards, Sherm

Sherm
+1  A: 

I suspect that this answer has something to do with it. To quote from dirkgently's answer, quoting the GCC manual:

Note that the alignment of any given struct or union type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct or union in question.

According to that rule, once you've added an 8-byte double, the compiler has to arrange everything on 8-byte multiples. You can override that with #pragma pack(), of course, although it will be less efficient if you end up with an 8-byte member on something other than an 8-byte boundary.

aalpern
No, it doesn't. The only thing that quote says is that because of `double` member *the entire struct* should be aligned at 8-byte boundary. But it doesn't explain why the alignment of integer member changed. This struct should fit into 16 bytes with all alignment requirements met perfectly. But the compiler produced 24. Why?
AndreyT
Interestingly, this page of alignment examples for x64 code in VC++ 8 shows the same thing with a struct containing an int and a double, but unfortunately doesn't say why. http://msdn.microsoft.com/en-us/library/71kf49f1(VS.80).aspxClearly, the compiler is choosing to align members on the lowest-common-denominator, which is 8, hence the need for the alignment. Probably room for interpretation in that sentence.
aalpern
aaplem - that link shows the same thing with an int and double. That's probably because you want doubles to be 8 byte aligned. I always thought that having 4 byte aligned doubles is super slow on x86.
Mason
@Mason - agreed; I definitely want my doubles to be 8-byte aligned! I'm just wondering if the alignment of the data members is handled independently from the vtbl pointer. MS's docs talk about the data member alignment, but I haven't found anywhere where they indicate whether the vtbl is considered one of those members or not. And it's been 10 years since I worked on a project that forced me to think about vtbl layout, so I've forgotten most of what I once knew :-) (and that was for VC++ 4.2, anyway, it may be different now).
aalpern