c++程序的内存布局及类的大小讨论

1.在c++中,我们了解一个数据类型在内存中所占的内存大小一般都是使用sizeof函数,他可以计算基本数据类型在内存所占的字节数。当然,对于复合的数据类型也是可以的。比如说c++中最典型的数据类型:类。使用sizeof函数也是可以返回该类所占用的空间大小。当然,类的大小和基本的数据类型的大小时有区别的,也并非是类中所有数据类型大小的简单相加。它遵循一定的规律。今天我们就来讨论一下这个问题。

2.首先我们需要了解一下c++程序的内存布局。一个c++文件·经过编译之后在内存中大致可以分为以下几个区域:

(1)程序代码区:该区存放这个程序中函数的二进制代码。

(2)栈区(stack):该区存放这个程序中所有的局部变量,参数,临时变量等。该区内存的管理由操作系统完成。

(3)堆区(heap):允许程序员自行动态的申请分配内存,内存的申请和释放由程序员自己管理。该区的操作由尤其容易造成内存的提前释放与内存泄露。

(4)全局/静态区。该区用来存放程序中的全局变量以及静态变量。程序在创建这一块区域的时候会自动的将所有的字节清零。所以全局变量或者静态变量的默认值时0.该区的释放由操作系统完成。

(5)常量区:用来储存程序中的文字常量以及字符串常量。该区域的内存释放也是由操作系统完成。

3.下面我们来看一下的一段代码:

#include<iostream>
using namespace std;
class one {
    //无任何数据
};
class two {
    int a;//只用一种数据
};
class two_1 {
    int a;//两种相同的数据
    int b;
};
class two_2 {
    int a;//一种数据加上一个函数
    void print();
};
class two_3 {
    int a;
    double b;//两种不同的数据
};
class three {
    char a;
    int b;//三种不同的数据
    double c;
};
class three_1 {
    char a;
    double c;//三种不同的数据,但是较于上一个排序不同。
    int b;
};
int main()

{

            //注释部分为输出的结果

    cout << sizeof(one) << endl;//1
    cout << sizeof(two) << endl;//4
    cout << sizeof(two_1) << endl;//8
    cout << sizeof(two_2) << endl;//4
    cout << sizeof(two_3) << endl;//16
    cout << sizeof(three) << endl;//16
    cout << sizeof(three_1) << endl;//24
    return 0;
}

现在我们来分析这一结果:对于类这一复合数据类型,c++标准其实有解释:

(1)不存在大小为0的数据类型,即使是类中不含任何数据,那么它的大小是1,而不是零。

(2)对象的大小只与数据成员有关,与函数成员关。其实,在从上面的知识我们也可以推导出来,因为成员函数的储存位置与数据成员的储存位置不在一个区域,sizeof不计算程序代码区的内存。如此看来,实际上这个类的大小是比sizeof显示的要大的。

(3)对象的大小计算是这样规定的:1:各个数据成员与对象起始地址的偏移量必须是该数据类型的整数倍。2:类的大小必须是该类中最大数据类型的整数倍。这叫做内存对齐,内存对齐机制实际上是为了cpu更快的读取内存。

知道了这些,我们来分析一下最后两个类的大小区别的原因所在:观察这两个类可知,只是类中的数据成员的排序不同。首先,第一都是char,那么距离类的起始地址的偏移量是0,那么它首先占用一个字节。之后对于第一个类来说就是int型,它的大小为四个字节(我们在这了讨论的基础是基于32为操作系统),它对于类的起始地址偏移量是1,因为前边有一个char类型的数据占据了一个字节。由于他要相对于类的起始地址的偏移量应该是自己本身大小的整数倍,所以他应该隔三个字节之后在占用内存。也就是说,char类型与int型中间会出现三个空的字节,以此来弥补int类型的地址到类的起始地址偏移量的不足。好了,那么到现在前两个一共占据了八个字节,对于第三个double来说,刚好满足到类起始地址的偏移量等于自己大小的整数倍。所以doule直接就紧接着int开始占据之后的八个字节。那么这个类的大小就是16个字节。对于倒数第一个类,分析也是一样,首先是char占据1个字节,紧接着double相对于类的起始地址的偏移量是1,那么需要补满七个字节,也就是说,char与double之间会有七个空字节的间隔。紧接着int会占据四个字节,但是由于c++规定类的大小必须是该类最大数据成员大小的整数倍,那么int之后还是会补足四个字节,则这个类的大小就是24个字节。

3.今天的分享就到这里,其实c++中对象的内存布局比这些还要复杂一点。因为c++支持多态的机制,导致了在虚函数存在的情况下编译器会给这个该对象添加一个指针(我们叫虚指针),所以对象的大小会增加四个字节。而且,对于静态成员数据也是不影响对象的大小的。其实,sizeof函数计算的成员是在堆栈上的,对于其他内存区不予计算。

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/80725570