一、引言:
sizeof是c语言中的一个运算符,用来求某个变量或者类型的长度,CSDN有篇文章介绍sizeof的特点介绍的比较详细,我写这篇文章主要是介绍struct的数据成员对齐。C语言的struct成员对齐与操作系统有关,在window与linux上的表现不同,先来看一个例子:
1 #include
2 typedef struct{
3 int num1;
4 int num2;
5 double num3;
6
7 }A;
8 typedef struct{
9 int num1;
10 double num3;
11 int num2;
12 }B;
13
14 int main(void) {
15 printf("A:%d\n",sizeof(A));
16 printf("B:%d\n",sizeof(B));
17 return 0;
18 }
二、windows的对齐情况
上面这段程序在windows下执行打印的是:
A:16
B:24
为什么数据成员一样,只是成员的顺序不同,导致结构体所占的空间会不同,这就是数据对齐的原因,为了提高存储器的访问效率,避免读一个成员数据访问多次存储器,操作系统对基本数据类型的合法地址做了限制,要求某种类型对象的地址必须是某个值K的整数倍(K=2或4或8)。Windows给出的对齐要求是:任何K(K=2或4或8)字节的基本对象的地址都必须是K的整数倍。在上面的示例中,num1和num2为int占4个字节,num3为double占8了字节,结构体A、B的数据对齐情况分别如下:

上面的是结构体A的对齐情况,下面的是结构体B的对齐情况,图中的灰色部分为对齐填充部分,不代表有效数据。可以看到A的分布很紧凑,没有留下空隙,而B中,有两段空隙,因为数据A不需要填充就能满足K(这里K=4、8)字节的对象的起始地址是K的整数倍了。而B中,第一个数据成员是num1,大小为四个字节,接下来的是num3,大小为8个字节,num3不能紧接在num1的后面,因为4不是8的整数倍,因此需要填充4个字节,这样num3的起始地址就在8上,满足要求,之后的num2接在num3后,起始地址为16。有人会问,为什么B占用的是24个字节,而不是20个字节,从上面的图中,也看出,用20个字节刚好装下了num1、num2、num3这三个元素,并且这三个元素都满足了对齐要求,为什么num2后面还要填充4个字节 事实上,如果只有一个B对象,20字节确实是满足对齐要求的,但如果我们声明一个类型为B的数据:B b ,每个B对象只用20字节,则其数据偏移情况如下:

可以看到,b 的num3的起始地址是28,不满足8的整数倍的要求,这就是B为什么要24字节的原因,为了所有的数据满足”任何K(K=2或4或8)字节的基本对象的地址都必须是K的整数倍“的要求,必须是结构体的整体大小必须是最大的K的整数倍。
三、linux的对齐情况
以上是windows的对齐要求,如果在linux上执行前面的示例,输出A、B所占的字节数都是16。Linux的对齐要求是:2字节类型的数据(如short)的起始地址必须是2的整数倍,而较大(int *,int double ,long)的数据类型的地址必须是4的整数倍。linux的对齐要求比windows宽松,这样会更加充分的利用存储空间,但是访问效率没有window好。linux下结构体B的对齐情况如下:

这里是针对32位的系统,对于X86-64,linux与windows一样,要求K字节的数据类型是K的倍数。
以上是对struct的数据对齐的简单介绍,我想,这个数据对齐可以出两个面试题,一个是已知道结构体定义,求成员的起始地址和结构体大小;另一个是,已知结构体定义,如何排列成员变量的顺序,使得整个结构体占有的存储空间最小。
四、计算结构体的大小和各个成员的起始地址
这个题目是比较简单的,只要把对齐要求理解了,我们只前往后处理每一个变量,只要当前放入的变量满足对齐要求,然后递归求后门的变量,直接上代码了:
#include
#include
#include
#define MAX 100
char *vars[MAX];//保存变量名
int lens[MAX];//保存变量的字节长度
int start[MAX];//保存变量的起始地址
int ALIGN;
/*********扫描成员变量和长度,每一组输入由变量名和长度组成**********/
int scanfStruct(){
char var[20],*p;
int len;
int index = 0;
printf(">>");
while(scanf("%s %d",var,&len) && var[0] != '$'){
p = (char *)malloc(strlen(var));
strcpy(p,var);
vars[index] = p;
lens[index] = len;
printf(">>");
// printf("%s:%d\n>>",vars[index],lens[index]);
index ++;
}
return index;
}