☰
【C/C++内存占用学习】
2026/10/27 1:18:31 网站建设 项目流程

目录

  • 常规类型
  • union联合体
  • struct结构体
  • class占用
  • 综合尝试

存储单位换算:1 字节 = 1 byte = 8 位 = 8 bit

常规类型

指针是一个uintptr_t,其所占内存字节一般是和计算机的CPU字长是一致的,64位系统指针所占的内存空间一般为8个字节,32位系统指针所占的内存空间一般为4个字节,不论char *还是int *亦或是float *。char *p 是一个指针,它保存的内容是一个地址,指向(*p),而(*p)才是那个字节。

intn=10;// 4chars1[]="Hello";// 6chars2[100]="0123456789";// 100char*p1=s1;// 8(64位系统)void*p2=malloc(100);// 8(64位系统)int*pint;char*p111;// 8、8(64位系统)

数组是函数的形参时会降为指针,故func1传入的p仍然是原指针p的拷贝,也是一个指针

voidfunc1(charp[100])//func1的p也是跟指针一样的字节数{std::cout<<sizeof(p)<<std::endl;// 8(64位系统)}voidfunc2(char(&p)[10])// p是装10个char类型数据的数组的引用,其结果类似于char p[10]; std::cout << sizeof(p);{std::cout<<sizeof(p)<<std::endl;// 10}voidfunc3(double&b)// 引用没啥好说的{std::cout<<sizeof(b)<<std::endl;// 8}

union联合体

union的大小取决于它所有的成员中,占用空间最大的一个成员的大小,并且需要内存对齐,所以以下显而易见。

unionUn1{inta;doubleb;charc;// sizeof(Un1) = sizeof(double) = 8};unionUn2{charbuffer[13];// sizeof(Un2) = sizeof(buffer) = 13};

区别来了,下面Un3、Un4默认下内存size分别为13、16。

unionUn3{charbuffer[13];charc;};unionUn4{charbuffer[13];intnumber;//由于int类型成员的存在,使u2的对齐方式变成4,也就是说,u2的大小必须在4的对界上,所以占用的空间变成了16(最接近13的对界)};

升级难度,猜猜下面union分别在#pragma pack(1) 、#pragma pack(2) 、#pragma pack(4) 、#pragma pack(8) 下内存size

unionUn4{charbuffer[13];intnumber;};

揭晓答案,分别是13、14、16、16。

struct结构体

struct就是一些元素的集合,它们在内存中的存放是有先后顺序的,并且每个元素都有自己的内存空间。

structss1{charc;// [0]....[3]inti;// [4]....[7]};// sizeof(ss1) = 8structss2{};// sizeof(ss2) = 1,与下面空类也占用1个字节的原因一致

内存对齐规则参考:内存对齐1、内存对齐2
结构体中成员的顺序直接影响内存空洞的大小,错误的排序可能导致大量内存浪费,如下。

structBadOrder{chara;// 1字节,偏移0doubleb;// 8字节对齐,需偏移8(补7字节空洞)intc;// 4字节对齐,偏移8+8=16chard;// 1字节,偏移16+4=20};// 总大小:20+1=21,需满足最大对齐值8,补3字节空洞,总大小24
structGoodOrder{doubleb;// 8字节,偏移0intc;// 4字节,偏移8chara;// 1字节,偏移12chard;// 1字节,偏移13};// 总大小:13+1=14,需满足最大对齐值8,补2字节空洞,总大小16

可借助offsetof算出偏移

#include<iostream>structBadOrder{chara;doubleb;intc;chard;};structGoodOrder{doubleb;intc;chara;chard;};intmain(){std::cout<<"BadOrder: "<<sizeof(BadOrder)<<std::endl;std::cout<<"Offset of a: "<<offsetof(BadOrder,a)<<std::endl;std::cout<<"Offset of b: "<<offsetof(BadOrder,b)<<std::endl;std::cout<<"Offset of c: "<<offsetof(BadOrder,c)<<std::endl;std::cout<<"Offset of d: "<<offsetof(BadOrder,d)<<std::endl;std::cout<<"GoodOrder: "<<sizeof(GoodOrder)<<std::endl;std::cout<<"Offset of a: "<<offsetof(GoodOrder,a)<<std::endl;std::cout<<"Offset of b: "<<offsetof(GoodOrder,b)<<std::endl;std::cout<<"Offset of c: "<<offsetof(GoodOrder,c)<<std::endl;std::cout<<"Offset of d: "<<offsetof(GoodOrder,d)<<std::endl;return0;}

class占用

一个Class对象占用内存空间为: 非静态成员变量总和 + 数据对齐处理 + 虚函数。也就是说静态成员数据不做考虑。
普通成员函数是针对类体的,成员函数的大小不在类的对象里面,同一个类的多个对象共享函数代码,共用相同的函数指针,所以自然不能归为实例的大小。
而析构函数,跟构造函数这些成员函数,是跟sizeof无关的,也不难理解因为我们的sizeof是针对实例。

我们访问类的成员函数是通过类里面的一个指针实现,而这个指针指向的是一个table,table里面记录的各个成员函数的地址。所以我们访问成员函数是间接获得地址的,这样也就增加了一定的时间开销,所以提倡把一些简短的,调用频率高的函数声明为inline形式(内联函数)。

#include<iostream>classA{};//c++要求每个实例在内存中都有独一无二的地址!!!!! 空类也会被实例化//所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。classB{public:B(){}virtual~B(){}//每个实例都有虚函数表//C++ 类中有虚函数的时候有一个指向虚函数的指针(vptr),在32位系统分配指针大小为4字节。//无论多少个虚函数,只有这一个指针,4字节。//注意一般的函数是没有这个指针的,而且也不占类的内存。voidaa(){}//普通成员函数,为各实例公有,不归入sizeof统计virtualvoidB1(){}//虚函数记入sizeof统计,多个虚函数共用一个private:chara;//占4个字节(对齐的问题) int占4字节,注意这点和struct的对齐原则很像!! char占一字节,补齐3字节char*p;//占4个字节(4字节指针)intb;//占4个字节staticintd;//非实例独占};classC:publicB{public:C(){}~C(){}virtualvoidtest(){}private://子类的大小是本身成员变量的大小加上父类的大小。intc1;//其中有一部分是虚拟函数表的原因,一定要知道父类子类共享一个虚函数指针};classAA1{};classBB1{virtualvoidfun();};classD:publicAA1,publicBB1{};//为了提高实例在内存中的存取效率.类的大小往往被调整到系统的整数倍.//并采取就近的法则,里哪个最近的倍数,就是该类的大小intmain(){std::cout<<"class A: "<<sizeof(A)<<std::endl;std::cout<<"class B: "<<sizeof(B)<<std::endl;std::cout<<"class C: "<<sizeof(C)<<std::endl;std::cout<<"class D: "<<sizeof(D)<<std::endl;return0;}

总结:
空的类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。

(一)类内部的成员变量:
1、普通的变量:是要占用内存的,但是要注意对齐原则(这点和struct类型很相似)。
2、static修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区。

(二)类内部的成员函数:
1、普通函数:不占用内存。
2、虚函数:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系

综合尝试

//#pragma pack(2)classBU{//一个Class对象占用内存空间为非静态成员变量总和+数据对齐处理+虚函数intnumber;unionUBffer{charbuffer[13];//union的大小取决于它所有的成员中,占用空间最大的一个成员的大小,并且需要内存对齐intnumber;//这里注释#pragma pack(2),那么union大小为16,因为与sizeof(int)=4对齐,如果放开#pragma pack(2),union的大小为14}ubuf;//一个Class对象占用内存空间为:非静态成员变量总和+ 数据对齐处理+虚函数。voidfoo(){}//非静态成员函数不占对象内存空间typedefchar*(*f)(void*);//f只是一个定义,不是实际上的指针,没有占字节。enum{hdd,ssd,blueray}disk;//int=4,union=16,enum=4(内存中当成int来存储),4+16+4+下面指针int*a;// 指针分平台};

算一下放开#pragma pack(2)是多少。
关于#pragma pack可参考:pragma pack

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询