之前牛客网做C++测试题遇到结构体对齐和类大小的问题,没怎么在意,以为就记住了。可是今天看面试问题的时候又看到了,有的地方就想不起来了,所以决定开篇文章记录一下。
类大小的问题
1.空类
class A
{
};
sizeof(A); //1
解析:类的实例化就是为每个实例在内存中分配一块地址;每个类在内存中都有唯一的标识,因此空类被实例化时,编译器会隐含地为其添加一个字节,以作区分。
2.虚函数类
class A
{
virtual void Fun();
};
sizeof(A); //4
解析:当一个类中包含虚函数时,会有一个指向其虚函数表的指针vptr,系统为类指针分配大小为4个字节(即使有多个虚函数)。
3.普通数据成员
class A
{
int a;
char b;
};
sizeof(A); //8
解析:普通数据成员,按照其数据类型分配大小,由于字节对齐(4),所以a+b=8字节。
4.静态数据成员
class A
{
int a;
static int b;
};
sizeof(A); //4
解析:**静态数据成员存放的是全局数据段,即使它是类的一个成员,但不影响类的大小;**不管类产生多少实例或者派生多少子类,静态成员数据在类中永远只有一个实体存在。而类的非静态数据成员只有被实例化时,才存在,但类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在,类的静态数据成员可以说是一种特殊的全局变量。
5.普通成员函数
class A
{
void Fun();
};
sizeof(A); //1
解析:类的大小与它的构造函数、析构函数以及其他成员函数无关,只与它的数据成员相关。
6.普通继承
class A
{
int a;
};
class B:public A
{
int b;
};
sizeof(B); //8
解析:普通类的继承,类的大小为本身数据成员大小+基类数据成员大小。
7.虚函数继承
virtual class A
{
int a;
};
class B:virtual public A
{
int b;
};
sizeof(B); //12
解析:虚函数类的继承,派生类大小=派生类自身成员大小+基类数据成员大小+虚拟指针大小(即使继承多个虚基类,也只有一个指向其虚函数表的指针vptr,大小为4字节)。
字节对齐
通过#pragma pack(4)实现,没到4的会补到4 比如char 是1 但szieof会输出4
#pragma pack的基本用法为:#pragma pack( n ),n为字节对齐数,其取值为1、2、4、8、16,默认是8
也能使用#pragma pack(push,1)来使字节以1个来对齐 , 使用#pragma pack(pop)来还原默认
字节对其对齐的作用和原因:
各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。
C++11新加关键字alignas(n)
__ attribute __((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐
参考文献:
https://www.cnblogs.com/sz-leez/p/7119232.html
https://www.jb51.net/article/84553.htm