一. 结构体字节对齐
1. 什么是字节对齐
要了解字节对齐这个概念,先通过下面的代码进行观察:
struct Test
{
char a;
int b;
char c;
};
printf("sizeof(Test)=%d\n", sizeof(Test));
// 输出:
// sizeof(Test)=12
按照结构体中变量的字节大小,我们认为sizeof(Test)=6
,但输出结果与我们预想并不一样,所以在结构体中变量所占的字节大小发生了变化,这就叫做字节对齐。
2.为什么字节对齐
计算机一般读取内存中的数据一般都喜欢以2的整数倍去进行读取,在结构体中数据都存储栈中,在读取数据时,如果结构体中的变量存储字节大小长短不一,计算机在读取数据时不能一次就将数据读取出来。就大大损失了计算机的性能。所以我们扩大每个字节的所占的字节数,来提高计算机读取速度。以空间换取时间。
3. 如何计算字节对齐
这里有关于字节对齐的四大概念,计算字节对齐紧扣概念:
1. 自身类型有一个对齐值(一般按照下方的成员进行对齐)
2. 自定义类型有一个对齐值(定义的结构体类型,一般按照成员中最大的一个对齐)
3. 程序有一个指定的对齐值 (#pragma pack(n))
4. 程序又一个有效对齐值(如果指定对齐值,按照指定值和成员中最小的一个进行对齐)
通过几个例子解释上述概念:
// 概念1 2
struct S1 // 12
{
char c1; // 1+3 根据下方成员对齐
int i; // 4
char c2; // 1+3 自定义类型按照成员中较大的对齐 这三个成员加起来为9 不是int型的整数倍,所以末尾补3
};
// 概念1
struct S2 // 8
{
char c1; // 1
char c2; // 1+2 根据下方成员对齐
int i; // 4
};
// 上同
struct S3 // 16
{
double a; // 8
char c; // 1 + 3 按照下方成员对齐
int i; // 4
};
struct S4 // 32
{
char c; // 1+7 按照结构体成员中最大的对齐 S3中最大字节为8 所以按照8对齐
struct S3 s3; // 16
double d; // 8
};
// 概念3 4:
#pragma pack(4) // 指定程序按照4字节对齐
struct Test1 // 8
{
char a; // 1 + 1 // 虽然指定了对齐值,但是程序又一个有效对齐值,按照内部成员较小值对齐,所以按照short对齐
short b; // 2
int c; // 4
};
// 概念3 4:
#pragma pack(2)
struct Test2 // 14
{
char a; // 1+ 1
double b; // 8
int c; // 4
};
struct Test3 // 40
{
short a; // 2 + 6 按照内部结构体中的最大值对齐 按照double对齐
struct
{
int b; // 4 + 4
double c; // 8
char d; // 1 + 7
};
long e; // 4 + 4
};
#pragma pack(2)
struct Test4 // 20
{
short a; // 2 有指定对齐字节数 所以要按照较小值对齐所以按照2对齐
struct
{
int b; // 4
double c; // 8
char d; // 1 + 1
};
long e; // 4
};
#pragma pack(4)
struct Test5 // c++: 8字节 c: 32字节
{
int a; // 4
struct test // 这是类型定义 再c++中不占空间
{
double b; // 8
char c[10]; // 10 + 2
int d; // 4
};
long e; // 4
};
#pragma pack(8)
struct Test6 // 3
{
char a; // 1 按照结构体中最小的成员对齐
char b; // 1
char c; // 1
};
4. 位段
位段是给整型类型指定所占位数的一种概念。
位段的特点:1. 不能跨字节存储 2.不能跨类型存储
struct Test1 // 1字节
{
char a : 2; // 指定a 占用2位
char b : 5; // 指定b 占用5位
char c : 1; // 指定c 占用1位
};
// 所占空间8位 刚好一个字节
struct Test2 // 2字节
{
char a : 2;
char b : 5;
// 不能跨字节存储 因为前两个变量已经占用了第一个字节的7位,c不能拆分分别放入两个字节中,所以要开辟一个新的字节进行存储
char c : 2;
};
struct Test3 // 8字节
{
// 不同类型 不能一起存储 并且还需要按照结构体字节对齐规则进行对齐
char a : 1; // 1 + 3
int b : 4; // 4
};
定义了位段的类型,是如何在内存中进行存储的:
// 数据如何在位段中进行存储的
// 存储时根据位数进行存储,如果数据超出位数表示范围则进行截断
struct Test4 // 占用3个字节
{
char a : 3;
char b : 4;
char c : 5;
char d : 4;
};
int main()
{
Test4 s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
}
上述程序,存储示例: