C语言的struct的数据成员对齐
一、引言:
sizeof是c语言中的一个运算符,用来求某个变量或者类型的长度,CSDN有篇文章介绍sizeof的特点介绍的比较详细,我写这篇文章主要是介绍struct的数据成员对齐。C语言的struct成员对齐与操作系统有关,在window与linux上的表现不同,先来看一个例子:
复制代码
1 #include <stdio.h>
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[3],每个B对象只用20字节,则其数据偏移情况如下:
可以看到,b[1]的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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#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;
}
/*********计算linux的对齐长度*********/
int getLinuxAlign(int *lens, int n){
int i=0;
int align = 1;
for(i = 0;i< n;i++){
if(align < 2 && *(lens +i) == 2)
align = 2;
if(align <4 && *(lens +i) >= 4)
align = 4;
}
return align;
}
/*********计算windows的对齐长度*********/
int getWindowsAlign(int *lens, int n){
int i=0;
int align = 1;
for(i = 0;i< n;i++){
if(align < 2 && *(lens +i) == 2)
align = 2;
if(align <4 && *(lens +i) == 4)
align = 4;
if(align <8 && *(lens +i) == 8)
align = 8;
}
return align;
}
/******
递归计算各个变量的数据偏移以及结构体大小
i表示正在处理第i个变量,curAddr表示当前地址
size表示结构体的大小,n表示变量个个数
******/
void getStart(int i,int *curAddr, int *size, int n){
if(i >= n)
return;
start[i] = *curAddr;//第i个变量的首地址为当前地址
*curAddr += lens[i];
*size += lens[i];
if( *curAddr % ALIGN ==0)//当前地址能被对齐长度整除,直接递归处理下一个变量
getStart(i+1,curAddr,size,n);
else{//当前地址不能被对齐长度整除
int blank = ALIGN - (*curAddr % ALIGN);//需要填充的大小
if(i == n-1){//已经是最后一个变量,只需将结构体大小扩充一下
*size += blank;
return;
}else if(lens[i+1] > blank){//下一个变量的大小大于填充大小,填充后,递归处理下一个变量
*curAddr += blank;
*size += blank;
getStart(i+1,curAddr,size,n);
}else{
i++;
while(lens[i] <= blank){//只要下一个变量的大小小于填空空白大小
start[i] = *curAddr;
*curAddr += lens[i];
*size += lens[i];
blank = ALIGN -(*curAddr % ALIGN);
if(i == n-1){
*size += blank;
return ;
}
i++;
}
getStart(i,curAddr,size,n);
}
}
}
void printStart(int n){
int i=0;
for(i=0; i<n; i++)
printf("%s(%d):%d/n",vars[i],lens[i],start[i]);
}
int main(){
int n;
int curAddr = 0;
int size = 0;
n = scanfStruct();
//ALIGN = getLinuxAlign(lens,n);
ALIGN = getWindowsAlign(lens,n);
getStart(0,&curAddr,&size,n);
printf("align:%d/n",ALIGN);
printf("/nsize:%d/n",size);
printStart(n);
return 0;
}
复制代码
五、排列成员顺序,使得结构体占有存储空间最小
这个题目更有意思,我这里暂时不贴,有想法的欢迎回复。
>更多相关文章
- 09-29如何通过wrap malloc定位C/C++程序的内存泄漏
- 02-25打车软件大战升级,补贴还能维持多久?
- 12-23BMP文件右旋90度[c语言]
- 12-23寻找直方图中面积最大的矩形(C语言版)
- 12-23[ndk,2]ndk开发案例和错误处理
- 12-23[ndk,1]ndk开发,C语言入门讲解
- 12-23C语言连续存储实现队列机制
- 12-23Objective-c 数据类型
首页推荐
佛山市东联科技有限公司一直秉承“一切以用户价值为依归
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 12-05亚马逊推出新一代基础模型 任意模态生成大模
- 12-05OpenAI拓展欧洲业务 将在苏黎世设立办公室
- 12-05微软质疑美国联邦贸易委员会泄露信息 督促其
- 12-05联交所取消宝宝树上市地位 宝宝树:不会对公
- 12-04企业微信致歉:文档打开异常已完成修复
相关文章
24小时热门资讯
24小时回复排行
热门推荐
最新资讯
操作系统
黑客防御