结构体求大小(2)

回到上文留给大家的题目:

struct test1
{
   char a;

   char b;
   int c;
}

 

struct test2
{
   char a;
   int c;

   char b;

}

 

test1和test2大小分别为多少,它们各自的内存分配情况又如何,具体对齐系数为多少?

 

结果:test1大小为8个字节,test2大小为12个字节。

同样的数据类型,只是顺序发生了变化,结构体的大小也随之发生了改变,原因在于结构自身要保持内存对齐原则。

在test1中,sizeof(test1) = sizeof(a)+sizeof(b)+2(为了遵循内存对齐原则空出2个字节)+sizeof(c)=1+1+2+4=8。

 

在test2中,sizeof(test2) = sizeof(a)+3(为了遵循内存对齐原则空出3个字节)+sizeof(c)+sizeof(b)+3(为了遵循内存对齐原则空出2个字节)+=1+3+4+1+3=12。

 

从上一例中我们看到,在test1中,b后空了2个字节,是因为内存分配到c时,由于c要占四个字节,要满足4字节对齐,而在b后空了2个字节。

按同样的原理,在test2中,内存分配到c时,前面的a虽然本来只用1个字节空间,但由于c要占4个字节,结构体内存要保持4字节内存对齐,a后空出3个字节。内存分配到b时,给b分配一个字节,此时成员变量定义结束,但就目前而言:

整体对齐系数 = max(int,char)=4,所以b后仍然要空出3个字节,保持对齐原则。

 

有了上面的分析,大家不难分析出结构体

 

struct test3
{
   char a;
   int c;

   char b;

   char d;

}

的大小仍然为12个字节。(在test2的前提下,b后空出的3个字节,可够d一个字节对齐,所以d的内存紧随其后,然后空出了2个字节)

 

对于上面的情况来看,不难看出,合理定义一个结构体是有必要的,那么如何去避免这样造成的内存浪费呢,就像test2本来只要6个字节就够了,没必要给他分配12个字节的空间。在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
  · 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。(n = 1,2,4,8,16...)
     · 使用伪指令#pragma pack (),取消自定义字节对齐方式。

例如:

#pragma pack (1)

 

 

#pragma pack ()

这样定义后,test2大小就为6个字节了。

更深入学习请看下面实例,n从1开始逐渐向后取值,结构体内存分配情况和结构体最后大小:

通过#pragma pack(n)改变“对齐系数”,然后察看sizeof(struct test_t)的值。

1、1字节对齐(#pragma pack(1))
输出结果:sizeof(struct test_t) = 8
分析过程:
1) 成员数据对齐
#pragma pack(1)
struct test_t {
int a;   /* 长度4 < 1 按1对齐;起始offset=0 0%1=0;存放位置区间[0,3] */
char b;   /* 长度1 = 1 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
short c; /* 长度2 > 1 按1对齐;起始offset=5 5%1=0;存放位置区间[5,6] */
char d;   /* 长度1 = 1 按1对齐;起始offset=7 7%1=0;存放位置区间[7] */
};
#pragma pack()
成员总大小=8(注意:这不是结构体最后的总大小,下同

2) 整体对齐
整体对齐系数 = min((max(int,short,char), 1) = 1
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 8 /* 8%1=0 */

2、2字节对齐(#pragma pack(2))
输出结果:sizeof(struct test_t) = 10
分析过程:
1) 成员数据对齐
#pragma pack(2)
struct test_t {
int a;   /* 长度4 > 2 按2对齐;起始offset=0 0%2=0;存放位置区间[0,3] */
char b;   /* 长度1 < 2 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
short c; /* 长度2 = 2 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
char d;   /* 长度1 < 2 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
};
#pragma pack()
成员总大小=9

2) 整体对齐
整体对齐系数 = min((max(int,short,char), 2) = 2
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 10 /* 10%2=0 */

3、4字节对齐(#pragma pack(4))
输出结果:sizeof(struct test_t) = 12
分析过程:
1) 成员数据对齐
#pragma pack(4)
struct test_t {
int a;   /* 长度4 = 4 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */
char b;   /* 长度1 < 4 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
short c; /* 长度2 < 4 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
char d;   /* 长度1 < 4 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
};
#pragma pack()
成员总大小=9

2) 整体对齐
整体对齐系数 = min((max(int,short,char), 4) = 4
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 12 /* 12%4=0 */

4、8字节对齐(#pragma pack(8))
输出结果:sizeof(struct test_t) = 12
分析过程:
1) 成员数据对齐
#pragma pack(8)
struct test_t {
int a;   /* 长度4 < 8 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */
char b;   /* 长度1 < 8 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
short c; /* 长度2 < 8 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
char d;   /* 长度1 < 8 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
};
#pragma pack()
成员总大小=9

2) 整体对齐
整体对齐系数 = min((max(int,short,char), 8) = 4
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 12 /* 12%4=0 */

 


5、16字节对齐(#pragma pack(16))
输出结果:sizeof(struct test_t) = 12
分析过程:
1) 成员数据对齐
#pragma pack(16)
struct test_t {
int a;   /* 长度4 < 16 按4对齐;起始offset=0 0%4=0;存放位置区间[0,3] */
char b;   /* 长度1 < 16 按1对齐;起始offset=4 4%1=0;存放位置区间[4] */
short c; /* 长度2 < 16 按2对齐;起始offset=6 6%2=0;存放位置区间[6,7] */
char d;   /* 长度1 < 16 按1对齐;起始offset=8 8%1=0;存放位置区间[8] */
};
#pragma pack()
成员总大小=9

2) 整体对齐
整体对齐系数 = min((max(int,short,char), 16) = 4
整体大小(size)=$(成员总大小) 按 $(整体对齐系数) 圆整 = 12 /* 12%4=0 */

 

 

以上实例了解两个公式最重要:

 

 

整体对齐系数 = min((max(int,short,char), n)

整体大小%整体对齐系数=0(整体大小>=成员总大小)

阅读更多
文章标签: struct 编译器 c
上一篇本周班级教学动态
下一篇登录界面中透明编辑框的实现(测试平台WINCE 5.0)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭