User-defined structures may be padded depending on the compiler settings and platform. In your example, the layout of AStructure would most likely be [A(4), B(1), C(1), padding(2)]. The size of the structure is usually padded so that its members would be correctly aligned if the structure were used in an array. In this case, int would probably have 4-byte alignment, and so there would need to be 2 bytes of padding so that in an array the A member of AStructure begins on a 4-byte boundary.
There are other places than the end where padding can exist. Some examples:
struct BStructure
{
char A;
int B;
char C;
};
struct BStructure // (compiled)
{
char A;
char padding[3];
int B;
char C;
char padding[3];
};
struct CStructure
{
int A;
long long B; // 8 bytes
};
struct CStructure // (compiled)
{
int A;
char padding[4];
long long B;
};
// Padding can be disabled by telling compiler to pack members to 1-byte alignment. This may have a performance hit, but is useful for consistency accross platforms and DLL boundaries.
#pragma pack(1)
struct BStructure
{
char A;
int B;
char C;
};
struct BStructure // (compiled)
{
char A;
int B;
char C;
};
Generally you want to order the members of your structs/classes so that the larger ones come first. This reduces the amount of padding that the compiler inserts.
Class functions are not stored in the class object. They are stored in a global location in the executable, shared among all class instances. You can interpret normal (non-virtual) member functions as being implemented like this:
struct MyStruct
{
void doThing( int a, int b );
int A;
int B;
};
// Compiler implementation
void MyStruct_doThing( MyStruct* thisPointer, int a, int b );
Virtual functions require the insertion of a vtable address in the class object, so that the function address can be determined for a class object.
struct MyVirtualStruct
{
virtual ~MyVirtualStruct() {} // Don't forget this
virtual void doThing( int a, int b );
int A;
int B;
};
// Compiler implementation
struct MyVirtualStruct
{
void** vtable; // initialized to address of MyVirtualStruct_vtable
int A;
int B;
};
// global vtable for MyVirtualStruct
void* MyVirtualStruct_vtable[2] =
{
MyVirtualStruct::~MyVirtualStruct,
MyVirtualStruct::doThing
};
// Call virtual function "doThing" by getting vtable address, and using the entry index corresponding to "doThing".
MyVirtualStruct instance;
((void(*)(int,int))instance.vtable[1])( &instance, a, b )
//-------------------------------------------
// If we had a child type that overrided "doThing", it gets its own vtable, which has entries for all parent class virtual functions, as well as any child virtual functions (added at the end).
struct MyChildVirtualStruct : public MyVirtualStruct
{
virtual void doThing( int a, int b ) override;
virtual void doThing2( int a, int b );
int C;
int D;
};
// Compiler Implementation
struct MyChildVirtualStruct
{
void** vtable; // initialized to address of MyChildVirtualStruct_vtable
int A;
int B;
int C;
int D;
};
// global vtable for MyVirtualStruct
void* MyChildVirtualStruct_vtable[3] =
{
MyVirtualStruct::~MyVirtualStruct, // Use base destructor directly, since we didn't define one
MyChildVirtualStruct::doThing,
MyChildVirtualStruct::doThing2
};