为什么字节对齐?
因为cpu一次存取的大小是4字节(32bit计算机)或8字节(64bit),所以为了更高效的读取要进行字节对齐。比如在结构体中第一个成员占一个字节,第二个成员占4个字节,那么cpu在读取第二个成员时要先读取前面4个字节中的后三个字节,在读取当前4个字节中的1个字节,在拼起来。
- 结构体
在计算类或者结构体大小的时候,只有变量占用大小(存在内存中),函数是不占用的(存储在磁盘中),但是虚函数不存储在磁盘中,所以要额外考虑。
内存的数据经过对齐后,那么CPU可以整块地将内存数据读取进来,提高效率。结构体内存分析满足两点:
(1)计算变量偏移量:该变量的偏移量必须是min(默认对齐参数,其类型所占字节数)的整数倍。
(2)计算结尾偏移量:最终所占字节数必须是成员所占最大字节数的整数倍。
所以说,结构体或者类中成员变量按照所占字节递增或递减顺序排放是最省内存的。
默认对齐参数:在window VS中为8; 在linux中为4.
也可以人为设置
#pragma pack(n) //n是已有类型的所占字节数1,2,4,8..
例如
struct A
{
int a;
char b;
double c;
char d;
};
int a从0偏移开始,占四个字节,即占用0,1,2,3,现在可用偏移为4偏移,接下来存char b; 由于4是1的倍数,故而,b占用4偏移,接下来可用偏移为5偏移,接下来该存double c; 由于5不是8的倍数,所以向后偏移5,6,7,都不是8的倍数,偏移到8时,8是8的倍数,故而c从8处开始存储,占用8,9,10,11,12,13,14,15偏移,现在可用偏移为16偏移,最后该存char d ;因为16是1的倍数,故d占用16偏移,接下来在整体向后偏移一位,现处于17偏移,min(默认对齐参数,类型最大字节数)=8;因为17不是8的倍数,所以继续向后偏移18…23都不是8的倍数,到24偏移处时,24为8的整数倍,故而,该结构体大小为24个字节。
struct stu
{
char a; // 偏移位置0
int b;//偏移4
short c;//偏移8
};//最后为10补2,能整除4,所以为12
#pragma pack(2)
struct stu
{
char a;
int b;
short c;
};//8
- 类
在C++11中,结构体和类已经没有区别,但
(1)含有虚函数的类需要注意。如果类含有虚函数或者从父类中继承虚函数,那么就会在类中建立虚函数表来存放虚函数地址,每个类有一个虚函数表,每个虚表有一个虚标指针指向相应虚表。
所以带虚函数的类所占内存:成员对齐后大小 + 4*(虚函数指针的个数n)。
class Base1{
virtual void fun1(){}
virtual void fun11(){}
public:
virtual ~Base1();
};
class Base2{
virtual void fun2(){}
};
class DerivedFromTwo: public Base1, public Base2
{
virtual void fun3(){}
};
DerivedFromTwo所占大小=4*2。因为继承自两个虚函数类,会在类内部建立两个虚函数表,自己定义的虚函数是放在这两个虚函数表中的。
(2)静态成员
因为静态成员在类外初始化,不占用类大小。
空类的大小为1,因为C++规定任何对象不能有相同内存地址,如果大小为0,内存地址就出现相同的了。
class A
{
public:
void func() { cout << "stattuic"; }
const static int var;
static int var1;
}; //sizeof(A) == 1,类似空类
class A
{
public:
virtual void func() { cout << "stattuic"; }
const static int var;
static int var1;
};//sizeof(A) == 4,虚标指针大小
- 联合体(Uion)
联合体又叫共用体,是指成员共用内存的偏移地址。所以共用体的内存为成员所占字节数的最大值,然后加上若干偏移值使得能够整除类型所占字节数的最大值。
union U1
{
int n;
char s[11];
double d;
}; //11--》16能够整除8,所以是16
参考:
https://blog.csdn.net/dai_wen/article/details/78304568