现代计算机中内存空间都是按照字节~(Byte)~划分的。从理论上讲似乎对内存的访问可以从任何地址开始,但现实是存储在某些特别地址上的数据需要多次访问,
经过特殊处理后才能访问到。为了提高访问速度,需要数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是字节对齐~(Byte alignment)。

各个硬件平台对存储空间的处理是不同的。一些平台对某些特定类型的数据只能从某些特定地址开始访问。比如~ARM~要求访问内存的地址必须是~4~字节对齐的,ARM~的
数据总线是~32Bit。假设访问一个~4~字对齐的~32Bit~整型变量,只需要一次读操作,即可完成读取;如果访问一个非对齐的~32Bit~整型变量,ARM~须将含有这个变量
的高字节按对齐方式读取出来,然后再将低字节按对齐方式读取出来,再把两次结果拼在一起得到这个整型变量。显然在访问效率上,非对齐的变量访问差很多。不是所有
系统都需要字节对齐,如~80C51~类似的~8~位单片机,本身就是按一个字节对齐的,就不存在这样的问题。x86~的硬件可以处理地址未对齐的问题,
字节对齐对于一般的~x86~程序,不是那么重要。

编译器为了增加结构体等数据的访问速度,它会使用一定的方法对齐数据成员。如下列~C/C++~代码:



如果不考虑对齐因素,结构体~TA、TB~和~TC~的尺寸是一致的,占用~7~个字节。事实上,编译器编译后,
这三个结构体的尺寸是不一致的,TA~占用~8~个字节;TB~占用~12~个字节;TC~占用~7~个字节。先了解以下四个概念:


1.数据类型自身的对齐值:基本数据类型的自身对齐值,char~型数据,
其自身对齐值为~1~字节,short~型为~2~字节,对于~int、long、float、double~类型,
为~4~字节,long long~类型为~8~字节;
2.指定对齐值:#pragma pack (value)时的指定对齐值~value;
3.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的值。


有效对齐值是最终用来决定数据存放地址方式的值。数据结构中的数据变量都是按定义的先后顺序来存储的,
第一个数据变量的起始地址就是数据结构的起始地址,内部成员变量存放地址都要按照有效对齐值对齐,
结构体成员变量占用字节数也需要是结构体有效对齐值的整数倍。

假设~TA~从地址~0x0~开始存放,a~地址是~0x0,占用~4~个字节;b~地址从~0x4~开始,占用~1~个字节;c~地址需要
按~2~对齐,故从~0x6~开始,占用~2~个字节;总体共占用~8~个字节,正好是结构体自身对齐值~4~的倍数。故~TA~
最终占用~8~个字节。假设~TB~也从~0x0~开始存放,b~地址是~0x0,占用~1~个字节;a~地址需要按~4~字节对齐,
故从~0x4~开始,占用~4~字节;c~地址需要按~2~字节对齐,当前地址~0x8~正好是对齐的,占用两个字节;总体共占用
~10~个字节,但是~10~不是本结构体自身对齐值~4~的整倍倍数,故对齐后最终占用~12~个字节。同理不难得出,TC~占用
~7~个字节。

从讨论来看,对齐问题属于一个空间和时间平衡的问题,在设计嵌入式系统时,字节对齐是一个必须考虑的问题。
为了避免空间的浪费,又要提高代码的运行速度时,要手工调整一些设计细节。尤其是协议类的代码,为了避免出现字节空洞,
协议的报文往往是按照~1~个字节对齐,极易出现非对齐地址,设计上应尽可能避免。