前一段笔试一直考,一直不会做……前两天虎牙面试,问这个也不会,说是float占8个字节,对齐也完全不知道,就很尴尬……补一下吧,以后能记住也好……
一、各变量类型占用内存:
char:1个字节
wchar_t:2个字节
bool:1个字节
short:2个字节
int:4个字节
float:4个字节
double:8个字节
long long:8个字节
32位系统:
long:4个字节
指针:4个字节
64位系统:
long:8个字节
指针:8个字节
unsigned不影响sizeof的取值。
对函数使用sizeof,再编译阶段会被函数返回值的类型取代,如int f1()会返回4,double f2()会返回8.
二、数组占用内存
数组大小就是各维数的乘积*数组元素的大小。
char a[] = "abcdef"; int b[20] = {3, 4}; char c[2][3] = {"aa", "bb"}; cout<<sizeof(a)<<endl; // 7 cout<<sizeof(b)<<endl; // 20*4=80 cout<<sizeof(c)<<endl; // 6
数组a的大小在定义时未指出,编译时分配给它的空间按照初始化的值确定,未元素个数加1,为7;
b为分配的20个元素*4=80;
c为2*3=6
看这一段程序:
int *d = new int[10]; cout << sizeof(d) << endl;
d是动态数组,它实质上是一个指针,所以sizeof(d)的值为4.
三、结构体占用内存
对齐:
现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐
用空间换时间,会出现空闲空间浪费的情况。
结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。
例1:
结构体定义如下:
struct A
{
char a;
short b;
int c;
};
a占用一个字节之后,为使b满足2要求,需要再a之后填充一个字节。总的占用为8字节内存。
例2:
结构体定义如下:
struct A
{
short b;
int c;
char a;
};
为了使c满足第二条准则,需要在给b填充两个字节之后再次填充两个字节。同时为了满足第三条准则,需要在给a填充一个字节之后再在后方填充三个字节。总占用内存为12个字节。
例3:
结构体定义如下:
typedef struct
{
char a;
short b;
char c;
int d;
char e[3];
}T_Test;
sizeof(T_Test)为16;
char a占用1个字节,
short b占用2个字节,在a与b之间填充1个字节
char c占用1个字节
int d占用4个字节,在c与d之间填充3个字节
char e[3]占用3个字节,之后补充1个字节,
sizeof(T_Test)共16个字节。
例4:
有指定时其对齐的规则是:每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐,并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。
程序如下:
#pragma pack(2) //指定按2字节对齐
struct C
{
char b;
int a;
short c;
};
#pragma pack() //取消指定对齐,恢复缺省对齐
分析:
b占用1个字节
a占用4个字节,大于2,分成两个3字节,在b与a之间填充1个字节,c占用2个字节,一共8个字节。
sizeof(struct C) = 8.
添加一个后来看到的结构体里嵌套结构体的sizeof大小求解问题:
struct stu { int i; struct { char c; int j; }ss; int k; }; cout << sizeof(stu) << endl;
这段程序运行结果为16,求最大的类型占用空间时,将嵌套的struct结构体ss展开了,最大为int占用的4位,
但是内部嵌套的struct结构体在计算空间时不与外部元素相合并。所以总的大小为:4+4+4+4=16.
再看一个嵌套结构体的例子:
struct stu { char i; struct { char c; int j; }ss; char a; char b; char d; char e; char f; }; cout << sizeof(stu) << endl;
这段程序运行的结果为20,最大为int占4位,第一个char i占4位,ss内char c占4位,int j占4位,
a,b,d,e占4位,f占4位,一共占20位没毛病。
再看一个嵌套数组的例子:
struct stu { float f; char p; int adf[3]; }; cout << sizeof(stu) << endl;
输出结果为20,同嵌套结构体一样,确定最大占用空间类型时将数组拆分开,而不是用整个数组占用空间大小作为标准,
所以最大为int或float的4位,4+4+4*3=20。
四、类占用内存
1.
sizeof空类大小为1.
2.
class A { public: int a; private: char b; }; cout << sizeof(A) << endl;
简单类和求结构体的sizeof一样,考虑偏移和对齐,结果为8.
3.带虚函数的类
class A { public: int a; virtual void f() {} }; cout << sizeof(A) << endl;
虚函数放在虚表中,类中定义了虚函数,需要存放一个指向虚表的指针。
sizeof(A)=sizeof(A.a)+sizeof(指针)=4+4=8;(指针在32位系统为4位)。
当存在多个虚函数时,仍然只需要一个指向虚表的指针:
class A { public: int a; virtual void f() {} virtual void f1() {} virtual void f2() {} }; cout << sizeof(A) << endl;
结果仍然为8.
4.继承的类
普通继承的空间计算结果是sizeof(基类)+sizeof(派生类)。对齐考虑的是所有类中占用空间最大的数据类型。
class A { public: int a; char b; }; class B:public A { char c; }; cout << sizeof(B) << endl;
结果取决于编译器,vs中结果为12,说明将char b和char c是分开的,4+4+4=12.
class A { public: int a; }; class C :public A { public: int c; double e; }; cout << sizeof(C) << endl;
但是到这里结果又变成了16了,这不就是将a和c合并在一个8的空间了吗?按照上一个例子,a和c应该各自占用8个空间呀……不是很懂,有点玄学……
class A { public: int a; virtual void get() {} }; class B:public A { public: int c; virtual void set() {} }; cout << sizeof(B) << endl;
基类和继承类中均包含虚函数时,同样只存放于一个虚表中,因此只需要一个指针,所以结果是4+4+4=12.
多重继承的情况:
class A { public: int a; }; class B :public A { public: int b; }; class C :public A { public: int c; }; class D :public B, public C { public: int d; }; cout << sizeof(D) << endl;sizeof(D)=sizeof(B)+sizeof(C)=(4+4)+(4+4)+4=20
五、其他
union的sizeof:
union u { double a; int b; }; union u1 { char a[13]; int b; }; union u2 { char a[13]; char b; }; cout << sizeof(u) << endl; cout << sizeof(u1) << endl; cout << sizeof(u2) << endl;
结果为8,16,13.
解析:
union的大小取决于它所有的成员中,占用空间最大的一个成员的大小。
u中最大的成员就是double a,sizeof(u)=8.
u1中占用最大的是char[13],但是需要与int对齐,所以最大为16.
u2占用最大的是char[13],所以为13.
部分参考于:点击打开链接