C++内存大小布局

oneNeko 于 2022-06-22 发布

C中可以使用sizeof关键字获取变量内存大小,C++继承了这个关键字。

要特别注意的是,sizeof在编译期计算,另外其计算的是内存大小要注意区分数组和指针,例如

#include<stdio.h>
int main(){
    int nums0[100];
    int* nums1=new int(100);
    printf("%ld\n",sizeof(nums0));
    printf("%ld\n",sizeof(nums1));
    printf("%ld\n",sizeof(*nums1));
    return 0;
}
400
8
4

解释:400是指数组内存大小100*4个字节,8是指指针大小8个字节(64位程序),4是指nums1[0]这个int数据大小4个字节

类的大小

空类

C和C++略有不同,例如

#include <stdio.h>
struct A
{
};
int main()
{
    struct A a, b;
    printf("%d %x\n", sizeof(a), &a);
    printf("%d %x\n", sizeof(b), &b);
    return 0;
}

为什么C++一个空类的大小是1而C语言是0呢?

C语言可以理解空的大小就是0

而C++语言标准中规定了这样一个原则:“no object shall have the same address in memory as any other variable”,即任何不同的对象不能拥有相同的内存地址。因此编译器会在其中添加一个隐含的字节以确保每个实例拥有不同的内存地址

内存对齐

#include <stdio.h>
class A
{
    char c1;
};

class B:A{
    int n1;
};

class C:B{
    char c2;
};

class D{
    char c1;
    char c2;
    int n1;
};

class E{
    char c1;
    int n1;
    char c2;
};

int main()
{
    printf("sizeof(A)=%ld\n", sizeof(A));
    printf("sizeof(B)=%ld\n", sizeof(B));
    printf("sizeof(C)=%ld\n", sizeof(C));
    printf("sizeof(D)=%ld\n", sizeof(D));
    printf("sizeof(E)=%ld\n", sizeof(E));
    return 0;
}
sizeof(A)=1
sizeof(B)=8
sizeof(C)=12
sizeof(D)=8
sizeof(E)=12

A的大小是1,可以理解,是一个char的大小

B的大小是8,为什么呢?因为B类中有一个int类型(4个字节),为了方便取值时计算地址,所以进行了内存对齐,n1不会紧挨着c1存放,而是向后挪动4个字节而不是1个字节

C的大小是12,内存对齐之后不是应该是8才对吗?参考D和E,C是继承B,先存放B的变量,再存放c2,所以内部布局类似于E进行内存对齐

虚函数

如果类中包含有虚函数,那么这个类会有一张虚函数表,类内部会有一个虚函数指针,8个字节(64位系统)

#include <cstdio>
class A
{
public:
    virtual void f1() {}
    char c;
};
class B : A
{
};
class C : A
{
};
class D : B, C
{
};
class E : virtual A
{
};
class F : virtual A
{
};
class G : E, F
{
};

int main()
{
    printf("sizeof(A)=%ld\n", sizeof(A));
    printf("sizeof(B)=%ld\n", sizeof(B));
    printf("sizeof(C)=%ld\n", sizeof(C));
    printf("sizeof(D)=%ld\n", sizeof(D));
    printf("sizeof(E)=%ld\n", sizeof(E));
    printf("sizeof(F)=%ld\n", sizeof(F));
    printf("sizeof(G)=%ld\n", sizeof(G));
    return 0;
}

sizeof(A)=16
sizeof(B)=16
sizeof(C)=16
sizeof(D)=32
sizeof(E)=24
sizeof(F)=24
sizeof(G)=32

E、F是虚继承,会多一个虚基类指针vbptr比B、C多8个字节

visual studio使用cl main.cpp /d1reportSingleClassLayoutA 查看内存A布局

class A size(16):
        +---
 0      | {vfptr}
 8      | c
        | <alignment member> (size=7)
        +---

class B size(16):
        +---
 0      | +--- (base class A)
 0      | | {vfptr}
 8      | | c
        | | <alignment member> (size=7)
        | +---
        +---

class C size(16):
        +---
 0      | +--- (base class A)
 0      | | {vfptr}
 8      | | c
        | | <alignment member> (size=7)
        | +---
        +---

class D size(32):
        +---
 0      | +--- (base class B)
 0      | | +--- (base class A)
 0      | | | {vfptr}
 8      | | | c
        | | | <alignment member> (size=7)
        | | +---
        | +---
16      | +--- (base class C)
16      | | +--- (base class A)
16      | | | {vfptr}
24      | | | c
        | | | <alignment member> (size=7)
        | | +---
        | +---
        +---

class E size(24):
        +---
 0      | {vbptr}
        +---
        +--- (virtual base A)
 8      | {vfptr}
16      | c
        | <alignment member> (size=7)
        +---

class F size(24):
        +---
 0      | {vbptr}
        +---
        +--- (virtual base A)
 8      | {vfptr}
16      | c
        | <alignment member> (size=7)
        +---

class G size(32):
        +---
 0      | +--- (base class E)
 0      | | {vbptr}
        | +---
 8      | +--- (base class F)
 8      | | {vbptr}
        | +---
        +---
        +--- (virtual base A)
16      | {vfptr}
24      | c
        | <alignment member> (size=7)
        +---