Size of classes with virtual functions GCC/Xcode

2019-04-16 00:16发布

Can anyone explain to me what is going on here? First off, I think most programmers know that a class with a virtual function has a vtbl and thus has 4 extra bytes on the top of it. As far as I know, that's fairly standard. I've tested this and taken advantage of this fact before to do load in place from a binary file with patched vtbls. For the last 6 months, I've been working in Xcode and just recently came across the need to do some load in place stuff, so I was looking into patching vtbls again. Just to make sure my understanding was correct, I wrote a sample program. Here it is:

class A
{
    public:
    virtual int getData()
    {
        return a;
    }

    virtual void print()
    {
        printf("hello\n");
    }
    int a;
};

class B : public A
{
public:
    int getData()
    {
        return b;
    }
    int b;
};

class C : public B
{
public:
    int getData()
    {
        return c;
    }

    void print()
    {
        printf("world\n");
    }
    int c;
 };

class D
{
public:
    int a;
    int b;
};

int main (int argc, const char * argv[])
{
    A* tA = new A();
    tA->a = 1;

    printf("A: %d\n", sizeof(A));
    printf("A data: %d\n", tA->getData());

    B* tB = new B();
    tB->a = 2;
    tB->b = 4;

    printf("B: %d\n", sizeof(B));
    printf("B data: %d\n", tB->getData());

    C* tC = new C();
    tC->c = 8;

    printf("C: %d\n", sizeof(C));
    printf("C data: %d\n", tC->getData());

    A* aC = tC;
    aC->print();

    printf("D: %d\n", sizeof(D));

    return 0;
}

My expected output was:

A: 8

A data: 1

B: 12

B data: 4

C: 16

C data: 8

world

D: 8

However, the output I'm getting is:

A: 16

A data: 1

B: 16

B data: 4

C: 24

C data: 8

world

D: 8

Anybody have any idea what's going on here? Thanks!

1条回答
别忘想泡老子
2楼-- · 2019-04-16 00:32

Instances of classes A through C contain a vptr, a pointer to the virtual function table for the dynamic type. This pointer occupies 8 bytes on your 64-bit machine (or 4 bytes on a 32-bit machine). Each int member takes up 4 bytes.

The minimum value of sizeof(Class) is the sum of sizeof(member) for all members. If it were like that, then

sizeof(A) = 8 (vptr) + 4 (int a) = 12
sizeof(B) = 8 (vptr) + 4 (int a) + 4 (int b) = 16
sizeof(C) = 8 (vptr) + 4 (int a) + 4 (int b) + 4 (int c) = 20
sizeof(D) = 4 (int a) + 4 (int b) = 8

However, this is only the minimum size. Compilers usually increase this size to a multiple of sizeof(void*), which is 8 bytes here. This process is called aligning. It may look like this wastes memory, but this is outweighed by a performance gain: The CPU can read aligned data much faster than non-aligned data.

By the way, your expected result would have been correct if you were on a 32-bit machine. Pointers (esp. vptr) are 4 bytes wide there, and alignment is also to multiples of 4 bytes. Since all data members of the classes in question are 4 bytes big then, alignment wouldn't do anything there.

查看更多
登录 后发表回答