具有虚拟函数的类的大小 GCC/Xcode

发布于 2024-11-27 09:04:07 字数 1781 浏览 1 评论 0原文

谁能向我解释一下这是怎么回事?首先,我认为大多数程序员都知道具有虚函数的类有一个 vtbl,因此在其顶部有 4 个额外字节。据我所知,这是相当标准的。我已经对此进行了测试,并利用这一事实,从带有修补的 vtbls 的二进制文件进行就地加载。在过去的 6 个月里,我一直在 Xcode 中工作,最近才发现需要做一些就地加载的事情,所以我再次考虑修补 vtbls。为了确保我的理解是正确的,我编写了一个示例程序。这是:

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;
}

我的预期输出是:

A: 8

A data: 1

B: 12

B data: 4 >

C: 16

C data: 8

world

D: 8

但是,我得到的输出是:

答:16

答数据:1

B:16

B 数据:4

C:24

C 数据:8

< Strong>world

D: 8

有人知道这里发生了什么吗?谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

逆蝶 2024-12-04 09:04:07

类 A 到 C 的实例包含 vptr,即指向动态类型的虚函数表的指针。该指针在 64 位机器上占用 8 个字节(在 32 位机器上占用 4 个字节)。每个int成员占用4个字节。

sizeof(Class) 的最小值是所有成员的 sizeof(member) 之和。如果是这样,那么

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

但是,这只是最小尺寸。编译器通常会将此大小增加到 sizeof(void*) 的倍数,这里是 8 个字节。这个过程称为对齐。这可能看起来浪费了内存,但性能增益抵消了这一点:CPU 读取对齐数据的速度比读取未对齐数据的速度快得多。

顺便说一下,如果您使用的是 32 位计算机,您的预期结果将是正确的。指针(尤其是 vptr)的宽度为 4 字节,对齐也是 4 字节的倍数。由于相关类的所有数据成员都是 4 字节大,因此对齐不会执行任何操作。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文