内存字节对齐
1. 什么是内存字节对齐
计算机中内存空间都是按字节划分的,从理论上讲对任何类型的变量的访问可以从任何地址开始,但是在程序实际编译过程中,编译器会对数据类型在编译过程中进行优化对齐,编译器会将各种类型数据按照一定的规则在空间上排列,而不是顺序的排放,这就是内存字节对齐。
2. 为什么要内存字节对齐
不同硬件平台对存储空间的处理是不同的。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如某些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐。其他平台可能没有这种情况,但是最常的情况是,如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据,这样数据读取效率就会很差。
3. 如何实现内存字节对齐
3.1基本数据类型的自然边界对齐
各种数据类型的自然边界对齐值如下:
3.2对齐的规则
为结构体分配内存时,分配的内存大小至少是各个字段的长度和。通常,分配的结构体的长度会大于结构体各个字段的长度和,因为结构体需要对齐,即结构体各字段之间需要填充。
缺省情况下,编译器为结构体的每个成员按其自然边界对齐方式分配空间,按照每个成员被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构体的地址相同。结构体整体的默认字节对齐值是结构体中的所有成员中对齐参数最大的一个的值,结构体长度的计算必须取所用过的所有对齐参数的整数倍。
结构体整体对齐方式取决于结构体中所有成员的自然边界对齐值的最大值和指定对齐值两者中最小的值。
3.3如何指定对齐方式
__attribute__ ((packed)); //取消结构在编译过程中的优化对齐。
__attribute__ ((aligned (n))); //让所作用的结构体、类的成员对齐在n字节自然边界上,如果结构中有成员的长度大于n,则按照机器字长来对齐。n=1,2,4,8,16…
GCC中荐使用这种方式实现字节对齐,关于#pragma pack不推荐使用,因此本文不作论述。
这里强调下取消编译过程中的对齐优化:
__attribute__ ((packed)); //取消结构在编译过程中的优化对齐。
无论是Linux中的GCC,还是windows中的vc编译器,都是非紧凑模式的编译器,默认有字节对齐的优化。前面我们讲过不同平台关于字节对齐的处理可能是不同的,因此在涉及到跨平台通信时,不同的字节对齐方式,可能引起数据解读的问题。
取消编译过程中的优化正是应用在这样的场合,它使得数据成员间紧凑排列,而不会去对齐优化。下面进行举例说明
4.例子
#include <stdio.h>
struct tst1
{
char a;
short b;
int c;
};
struct tst2
{
char a;
short b;
int c;
}__attribute((aligned(4)));
struct tst3
{
char a;
short b;
int c;
}__attribute((aligned(8)));
struct tst4
{
char a;
short b;
int c;
}__attribute((packed));
struct tst5
{
char a;
int c;
short b;
};__attribute((aligned(4)));
int main(void)
{
printf("sizeof(struct tst1):%d\r\n", sizeof(struct tst1));
printf("sizeof(struct tst2):%d\r\n", sizeof(struct tst2));
printf("sizeof(struct tst3):%d\r\n", sizeof(struct tst3));
printf("sizeof(struct tst4):%d\r\n", sizeof(struct tst4));
printf("sizeof(struct tst5):%d\r\n", sizeof(struct tst5));
return 0;
}
结果如下:
[fiona@fiona test]$ gcc attribute.c -o attribute
[fiona@fiona test]$ ./attribute
sizeof(struct tst1):8
sizeof(struct tst2):8
sizeof(struct tst3):8
sizeof(struct tst4):7
sizeof(struct tst5):12
-
从tst1和tst2来看,此结构体的自然对齐是4字节,与成员中最大对齐int一致
-
从tst2和tst3来看,印证前面的结论结构体整体对齐方式取决于结构体中所有成员的自然边界对齐值的最大值和指定对齐值两者中最小的值。
-
从tst2和tst4来看,packed参数起了作用,取消了编译过程中的对齐,使结构体紧凑排列
-
从tst2和tst来看,如果相邻成员放在一起能更接近但不大于对齐方式指定的字节位,编译器是会把它们放在一起看作整体,再作填充,以满足对齐方式,所以据此编排结构体成员可以节省一些空间
5.参考
https://blog.51cto.com/9291927/1790295