前言
C语言中,结构体内存对齐问题算是比较常见的问题,虽然理解起来不难,但很多时候一不小心就会算错。比如给你一个 struct,让你 sizeof 计算一下需要占用多少字节,往往得到的结果比等于 struct 里面数据成员所占用的字节之和。
例:
#include <stdio.h> struct { int a; // 4 char b; // 1 } test; int main(void) { printf("%d/n", sizeof(test)); return 0; }
输出:
8
分析:
C语言中,int 占4个字节,char 占1个字节,理想情况下,这个结构体应该占用5个字节才对,为什么 sizeof 的结果却是8,这就不得不提到内存对齐了。
内存对齐
原则
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在 offset = 0 的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
例1:
#include <stdio.h> struct { int a; // 4 char b; // 1 } test; int main(void) { printf("%d/n", sizeof(test)); return 0; } 输出:8
分析:根据原则1、原则2,结构体中最大的成员是a,占用4字节,a存放在[0,3]中,b存放在[4],但由于原则2,需要对b进行补齐,b后面[5,7]需要填充,所以最终的结果是8
例2:
#include <stdio.h> struct { int a; // 4 char b; // 1 short c; // 2 } test; int main(void) { printf("%d/n", sizeof(test)); return 0; }
输出:8
分析:根据原则1和原则2,a存放在[0,3],b存放在[4],c存放在[6,7],b后面一个字节需要填充对齐
例3:
#include <stdio.h> struct { char a; // 1 int b; // 4 short c; // 2 } test; int main(void) { printf("%d/n", sizeof(test)); return 0; }
输出:12
分析:根据原则1和原则2,a存放在[0],b存放在[4,7],c存放在[8,9],a后面需要填充3个字节,c后面需要填充2个字节
疑问:
为什么例2和例3结构体内部成员都一致,只不过调整下顺序,内存占用却不一样?其实C语言在分配内存的时候,是根据结构体的成员属性定义从上往下进行分配的,有经验的程序员会在写结构体的时候对内存这块进行优化,从而提高代码的整体效率。
在设计结构体的时候,一般会遵照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
例4:
#include <stdio.h> struct { char b[2]; // 2 int c; // 4 double d; //8 short e; // 2 struct { int f; // 4 double g; // 8 short h; // 2 }a; int i; //4 } test; int main(void) { printf("%d/n", sizeof(test)); return 0; }
输出:56
分析:结构体中含有结构体,则整体按最大数据成员 double 所占用8个字节的整数倍进行分配
总结
在计算机中,要么时间换空间,要么空间换时间,很明显,内存对齐就是空间换时间,按照一定的对齐规则,内存对齐的作用不仅是便于cpu快速访问,同时合理的利用内存对齐可以有效地节省存储空间。简而言之,内存对齐是为了高效的内存IO。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/20641.html