1. 引言
字节对齐属于编译器的内容,决定数据实际的存放方式。主要有两个作用:1.优化数据储存,减少空间浪费 2.增加数据读取速率,本文将于以上两点展开,简述字节对齐的原理以及应用
本文参考以下链接: 什么是字节对齐,为什么要对齐?
2.字节对齐原理
几个原则与基本概念:
1.对于32位的CPU只能从4的整数倍地址读取数据,这是硬件设计上为了最大化性能而确定的(同时并行读取32位数据)
链接: 到底为什么要内存对齐?
3.编译器遵循自然对齐原则,即为声明对齐数据,存放在4的整数倍地址(不同平台有所差异)
2.数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
3.结构体的自身对齐值:其成员中自身对齐值最大的那个值。
4.指定对齐值:#pragma pack (value)时的指定对齐值value。
5.数据成员和结构体的有效对齐值:数据成员(数据类型)和数据结构的自身对齐值和指定对齐值中小的那个值。(数据成员对齐了数据结构自然也就对齐了)
6.有效对齐值最终用来决定数据地址的存放方式,有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0"
7.编译器对于结构体遵循圆整规则,即总大小%结构体有效对齐值 = 0,向上取整;
未字节对齐结构体示例:
#include
<stdio.h>
struct C
{
char b;
int a;
short c;
};
int main(int argc, char **argv) {
char Byte_Num;
Byte_Num = sizeof(struct C);
printf("Size is %d \n",Byte_Num);
}
其中由于未声明字节对齐方式,采用自然对齐存放数据(32位系统)即指定对齐值为4,存放结果如下图
其中,
- b自身对齐值为1,而指定对齐值为4,则有效对齐值为1,0x0000_0000%1 = 0,则起始存放地址为0x0000_0000;
- a自身对齐值为4,而指定对齐值为4,则有效对齐值为4,0x0000_0004%4 = 0,则起始存放地址为0x0000_0004;
- c自身对齐值2,而指定对齐值为4,则有效对齐值为2,0x0000_0008%4 = 0,则起始存放地址为0x0000_0008;
- 结构体C自身对齐值为4,而指定对齐值为4,则有效对齐值为4,0x0000_0000至0x0000_0008共9byte,根据圆整规则,9+2%4 = 0,则结构体总大小为12byte;
字节对齐结构体示例:
#include <stdio.h>
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
int main(int argc, char **argv) {
char Byte_Num;
Byte_Num = sizeof(struct C);
printf("Size is %d \n",Byte_Num);
}
其中,
- b自身对齐值为1,而指定对齐值为2,则有效对齐值为1,0x0000_0000%1 = 0,则起始存放地址为0x0000_0000;
- a自身对齐值为4,而指定对齐值为4,则有效对齐值为4,0x0000_0004%4 = 0,则起始存放地址为0x0000_0002;
- c自身对齐值2,而指定对齐值为4,则有效对齐值为2,0x0000_0008%4 = 0,则起始存放地址为0x0000_0006;
- 结构体C自身对齐值为4,而指定对齐值为4,则有效对齐值为4,0x0000_0000至0x0000_0007共8byte,根据圆整规则,8%4 = 0,则结构体总大小为8byte;
3.字节对齐应用
有了以上基础,在代码中定义变量是为了提高寻址效率以及节省空间可以遵循以下原则进行:
1.变量定义顺序按字节大小顺序排列
2.结构体定义大小使用圆整规则尽量避免编译器填充
4.总结
本文为博主个人学习总结记录,如有不正,欢迎指正