什么是字节对齐?
字节(Byte)是计算机信息技术用于计量存储容量和传输容量的一种计量单位,一个字节等于8位二进制数,在UTF-8编码中,一个英文字符等于一个字节。字节按照一定规则在空间上排列就是字节对齐。C / C ++中的每种数据类型都有对齐要求(事实上它是由处理器架构强制实现的,而不是由语言强制实施)。处理器将具有与数据总线大小相同的处理字长。在32位机器上 ,处理字大小为4个字节。
为什么要字节对齐?(在32位机器中)
事实上,存储器是字节可寻址的并且顺序排列。如果存储器被安排为一个字节宽度的单个存储体,则处理器需要发出4个存储器读取周期来获取整数。但是这样大大的浪费了处理器的资源,效率低下。其实在一个存储周期中读取所有4个字节的整数这样效率更高,为了获取这样的优势,存储器将被安排为4组一列,如下图所示:
其中,如果整数分配的地址不是4的倍数,则它跨越两行的存储区,如下图所示,这样整数就需要俩个存储器读取周期来获取整数数据。
还有一个原因:
平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址 处取某些特定类型的数据,否则抛出硬件异常
在32位机器上,各个数据类型的自然对齐字节数:
类型 | 32位机器 |
---|---|
char | 1 |
short | 2 |
int | 4 |
float | 4 |
double | 8 |
long | 4 |
long long | 8 |
从代码角度验证字节对齐
在C语言中结构体属于自定制数据类型,那么结构体的大小怎么算呢?我们来详细看一下。
//以下运行的结果sizeof(St) = 4字节
typedef struct St{
char a;
char b;
short c;
} St;
//以下运行的结果sizeof(St1) = 6字节
typedef struct St1{
char a;
short c;
char b;
} St1;
//以下运行结果sizeof(St2) = 8字节
typedef struct St2{
char a;
char b;
int c;
} St2;
//一下运行的结果sizeof(St3) = 12字节
typedef struct St3{
char a;
int c;
char b;
} St3;
从内存分布角度分析字节对齐:(这里拿St2和St3进行分析)
红色为所填充的对齐字节数;
最后再来练习俩个例子:
//程序运行sizeof(St4) = 20字节
typedef struct St4{
char a;
int c;
char arr[10];
} St4;
因为最大对齐变量为int(4),则char a后面填充了3字节,char arr[10]后面填充了2字节,所有加起来正好是20位int(4)的整数倍。
//程序运行sizeof(St3) = 32字节
typedef struct St3{
char d;
struct St8{
char a;
int b;
char c;
};
double e;
char f;
} St3;
因为最大对齐数为double(8),St8对齐数为4,计算得St8位12字节,不是8的倍数,char d自动填充3字节,前面就为16字节,double e为8字节,char f自动在后边填充7字节,总共为32字节。
从运行的结果以及内存分布来看,得出结论:
- 结构体定义时,变量成员的顺序会影响结构体的大小
- 成员变量在内存中的排列是有间隔的不是紧密排列
- 结构体最大字节数为每个结构体中最大变量的整数倍(例如以上的例子中,只要为int的整数倍就好,其他的short,double也是类似的)
- 如果有相同的变量,可以放在一起,可以减小结构体的空间
- 编译器对于内存对齐方式可以选择,vs默认8,Linux默认4
- 字节对齐是在用空间换取时间(速度)
自定义对齐数:
在程序运行代码前加上#pragma pack(1)
设置为以1字节对齐,那么以下字节数为:
//程序运行sizeof(St3) = 16字节
typedef struct St3{
char d;
struct St8{
char a;
int b;
char c;
};
double e;
char f;
} St3;
要取消改为默认则只需添加#pragma pack()
就好;
//占16字节
#pragma pack(1)
typedef struct St3{
char d;
struct St8{
char a;
int b;
char c;
};
double e;
char f;
} St3;
//占12字节
#pragma pack()
typedef struct St2{
char a;
int b;
char c;
} St2;