struct和class内存大小的计算

以下均是在VS2017下的结果

结构体内存大小的计算:

用例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
union ss
{
    int a;
    char b;
};
struct MyStruct
{
    int temp1;//4个字节
    char  temp2;//一个字节,补齐3个字节
    ss aa;//4个字节
    char temp3;//一个字节
    char temp4;//一个字节,补齐两个字节
};
int main()
{
    printf("%d", sizeof(MyStruct));
    return 0;
}

  输出 16

解析:对齐规则是按照成员的声明顺序,依次安排内存,对齐字节数为最大成员变量的字节大小,偏移量为最大成员变量的字节数的整数倍,在这个程序中对齐字节数为4,计算步骤如下:

首先是temp1 四个字节,temp2 一个字节,还剩3个字节,因为后面是aa 4个字节,可以自己对齐,所以temp2需要补齐3个字节, temp3 一个字节,temp4一个字节,对齐是4个字节,还剩2个字节需要补齐。所以总共是16个字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
union ss
{
    int a;
    double b;
};
struct MyStruct
{
    int temp1;//4个字节
    char  temp2;//1个字节,对齐是8个字节,还需3个字节补齐
    ss aa;//8个字节
    char temp3;//1个字节
    short temp4;//2个字节,补齐还需要5个字节
};
int main()
{
    printf("%d", sizeof(MyStruct));
    return 0;
}

  输出结果为 24

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<stdio.h>
union ss
{
    int a;
    double b;
};
 
struct student
{
    int name;
    char sex;
    double num;
 
};
struct MyStruct
{
    int temp1;//4个字节
    char  temp2;//1一个字节,补齐3个字节
    ss aa;//8个字节
    char temp3;//一个字节
    short temp4;//2个字节,补齐5个字节
    student people;//16个字节
};
int main()
{
    printf("%d", sizeof(MyStruct));
    return 0;
}

  结果为 40

解析:此时需要注意的是,对齐字节数8个字节,而不是16个字节。student people 对象内部先进行一次对齐,然后如果如果是外面对齐字节数的整数倍就不需要

再进行对齐了,如果不是就需要再次进行对齐。

下面的是需要二次对齐的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
struct MyStruct
{
    char temp1;
    char temp2;
};
struct student
{
    int name;//4个字节
    char sex;//1个字节
    MyStruct aa;//2个字节
    char temp;//1个字节
 
};
int main()
{
    printf("%d", sizeof(student));
 
}

  输出为 8 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<stdio.h>
union ss
{
    int a;
    double b;
};
 
struct student
{
    //int name;
    //char sex;
    int num;
 
};
struct MyStruct
{
    int temp1;//4个字节
    char  temp2;//1一个字节,补齐3个字节
    ss aa;//8个字节
    char temp3;//1个字节
    short temp4;//2个字节
    student people;//4个字节,补齐一个字节
};
int main()
{
    printf("%d", sizeof(MyStruct));
    return 0;
}

  输出结果为:24 

 

总结,在一个结构体中有另一个结构体的对象时,对齐字节数与这个结构体对象的字节数无关,只与结构体中的成员变量和union 有关。

 

类的内存大小的计算

只含有成员函数的类和空类的内存大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
using namespace std;
class student
{
public:
    student()
    {
        cout << "构造函数" << endl;
    }
    //void fun();
};
int main()
{
    cout << sizeof(student) << endl;
    return 0;
}

  运行结果: 1

解析:如果是空类也会输出1。如果一个类只含有成员函数没有成员变量或者虚函数,类的大小都是1。如果含有静态成员变量或者是静态成员函数,也会是1,因为静态成员变量和静态成员函数是类的所有对象共享的,它并不存放在一个实例化对象中

为什么空类也会是1呢?

所谓类的实例化就是在内存中分配一块地址,每一个实例在内存中都有独一无二的地址,而了到达这个效果,编译器往往会给一个空类隐含的增加一个字节,这样实例化出来的每一个空类的对象都会得到一个独一无二的地址。如果空类的大小是0的话,那么实例化出来的所有空类对象都是在同样一个内存地址上,就相当于同一个对象,这不是我们想要的。

 

只含有静态成员函数和静态成员变量的类的内存大小:

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;
class student
{
    static int b;
    static void func();
};
int main()
{
    cout << sizeof(student) << endl;
    return 0;
}

  输出结果:1

解析:因为静态成员变量和静态成员函数都是类的所有对象共享的,静态成员变量存放在全局存储区,静态成员变量和静态成员函数在编译时就已经在全局区分配的内存。

含有成员变量的类的内存大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
using namespace std;
class student
{
    int a;//4个字节
    char c;//1个字节,对齐,需要补齐3个字节
    static int b;
    static void func();
};
int main()
{
    cout << sizeof(student) << endl;
    return 0;
}

  输出为 8

含有虚函数的类的内存大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
using namespace std;
class student
{
    int a;//4个字节
    char c;//1一个字节,对齐,补齐3个字节
    static int b;
public:
    static void func();
    virtual void fly();//4个字节
};
int main()
{
    cout << sizeof(student) << endl;
    return 0;
}

  

  输出为 12

解析:虚函数会产生一个虚函数指针,指针大小为4个字节,虚函数指针指向虚函数表。

但是并不是每一个虚函数都会产生一个虚函数指针,同一个类中的所有虚函数只会产生一个虚函数指针。

 

继承下的类的内存大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
class student
{
    int a;
    char c;
    static int b;
public:
    static void func();
    virtual void fly();
};
class student1: public student
{
    int d;//4个字节
};
int main()
{
    cout << sizeof(student1) << endl;
    return 0;
}

  输出 16

解析:派生类会继承父类的成员变量和虚函数等,所以子类的内存大小是在父类的内存大小的基础上加上自己增加的内存大小。

父类和子类中都有虚函数的类的内存大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;
class student
{
    int a;
    char c;
    static int b;
public:
    static void func();
    virtual void fly();
};
class student1: public student
{
    int d;
public:
    void fly();
    virtual void eat();
};
int main()
{
    cout << sizeof(student1) << endl;
    return 0;
}

  输出为:16

问题:子类声明了一个虚函数且该虚函数是父类中没有的虚函数为什么子类没有新产生一个虚函数指针。

这个涉及到内存布局问题,首先是基类产生了一个虚函数指针,这个虚函数指针会指向虚函数表,表中存放的是基类的虚函数。子类也会继承到这个虚函数指针,虚函数指针指向虚函数表,表中先是存放基类的虚函数,再存放子类的基函数,如果子类重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类对应的虚函数覆盖

 虚继承——空类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
class student
{
     
};
class student1: virtual student
{
 
};
int main()
{
    cout << sizeof(student1) << endl;
    return 0;
}

  输出结果为 4

虚继承——基类含有虚函数指针,子类不含有虚函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;
class student
{
    int a;
    char c;
    //static int b;
public:
    //static void func();
    virtual void fly();
};
class student1: virtual student
{
    int d;
public:
    void fly();
    //virtual void eat();
};
int main()
{
    cout << sizeof(student1) << endl;
    return 0;

  此时输出为:20

解析:因为虚继承会再产生一个虚函数指针,去指向对应的基类,防止子类继承多个一样的基类,造成资源浪费

 

虚继承:子类和父类都有虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;
class student
{
    int a;
    char c;
    //static int b;
public:
    //static void func();
    virtual void fly();
};
class student1: virtual student
{
    int d;
public:
    void fly();
    virtual void eat();
};
int main()
{
    cout << sizeof(student1) << endl;
    return 0;
}

  输出:24

解析:此时是子类声明的虚函数是父类没有的,则此时子类会再产生一个虚函数指针,如果此时子类的所有虚函数都是继承自基类的,则不会产生虚函数指针。

posted @   cs_wu  阅读(1379)  评论(1编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示