1. 如何计算类的大小
class A1 {
char c; // char类型占1字节,从第一个字节开始存储
int a; // int类型占4字节,向后偏移3个字节,从第4个字节起开始存储
double d; // double类型占8字节,存满默认的8字节
}; // VS默认对齐8,Linux默认对齐4
int main() {
cout << sizeof(A1) << endl; // 16,为默认字节对齐数8的整数倍,即返回16
system("pause");
return 0;
}
class A1 {
char c; // char类型占1字节,从第一个字节开始存储
double d; // double类型占8字节,存满默认的8字节,两者加起来即为16
int a; // int类型占4字节,总共占20字节,但不为默认字节对齐数的整数倍
// 即离它最近的为24,即返回24
};
int main() {
cout << sizeof(A1) << endl; // 24
system("pause");
return 0;
}
class A1 {
class A2 { // 嵌套类 A2占8个字节
int a;
char c;
};
char c;
double d;
int a;
};
int main() {
cout << sizeof(A1) << endl; // 24,其实A2在A1中定义,注意:仅为定义所以A1不包含A2成员
system("pause"); // 光定义不会占A1类本身的空间,若定义A2的数据成员的话字节数发生变化。
return 0;
}
2. 结构体内存对齐规则
1)第一个成员在与结构体偏移量为0的地址处。
2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的对齐数为8,gcc中的对齐数为4
3)结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
3. 常见面试题
1)结构体怎么对齐? 为什么要进行内存对齐?
struct A {
char c;
int i;
}
若无内存对齐情况下,按照连续存储时,1234 5678作为8字节,在结构体中,char c会存储在1号位上,而int i会存储在2345位上,而CPU在读取在访问c的时候,每次访问4个字节,没有什么问题,会先拿出1---4,再拿出5---8,但是int i被切割开了,仍需要做字节切割及字节拼接,效率很低。
而进行内存对齐时,将char c存放在1号位,再偏移3个字节,将int i存储在5--8号位,这样CPU进行访问的时候,不必做字节上的拼接和切割,效率会大大提高。是一种典型的空间换时间以提高效率的方式。
2)如何让结构体按照指定的对齐参数进行对齐?
#pragma pack(4) // 指定默认对齐数
3)如何知道结构体中某个成员相对于结构体起始位置的偏移量?
class A1 {
public:
int a;
char c;
float f;
};
int main() {
A1 a;
float* pf = &a.f; // 拿到f的地址
float* pa = (float*)&a; // 做强转保持同类型
cout << (pf - pa) * sizeof(float*) << endl; // 乘以sizeof(类型字节数)获取字节偏移量
cout << &(((A1*)0)->f) << endl; // 特殊用法:获取成员相对起始位置的偏移量
system("pause");
return 0;
}
4)什么是大小端?如何测试某台机器是大端还是小端,有没有遇到过要考虑大小端的场景?
大端:正常的阅读顺序,即高位存低地址。
小端:低位存低地址。
给一个int类型赋值为1,将其转为char类型,char类型仅占1个字节,若char类型结果为1的话,说明低地址存在低位,低地址存在高位为小端。反之char类型如果为0的话,为大端。