您的位置:首页 > 其它

结构体内存对齐与位域

2015-05-04 16:21 357 查看
C语言结构体的对齐问题

C语言的结构体对齐问题是常见类型的题目,在笔试和面试中也是常考的题目,例如:

struct

{

   int a;

   char b;

   long c;

   short d;

}s;

问sizeof(s) = ?

我们假设在32位机器下,要解决这个问题我们必须明确各种数据类型占用的空间是多大:

     int 类型:4字节;

  long类型:4字节;

double类型:8字节;

float 类型:4字节;

short 类型:2字节;

char  类型:1字节;

所有指针类型:4字节;

int a[] = {1,2,3,4}:sizeof(a) = 16。

好,明确了上述就开始进入正题:

一般情况下,即没有#pragma pack宏定义和使用位域的情况下,结构体对齐一般满足三个原则:

1.普通数据成员:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。

2.结构体数据成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储)。

3.结构体的总大小,必须是其内部最大元素占据字节的整数倍。

struct {
int a;
int b;
int c;

}A;

sizeof(A) = 12;//简单,内部成员均为int型,自动对齐。

  struct{

           char a;

           int b;

           short c;

           double d;

    }B;

sizeof(B)=?

a占用一个字节:

1

b占用四个字节,根据原则1,b起始地址必须为4的整数倍,现在为1,不符合条件,因此必须填充:

1*** 1111

c占用二个字节,符合原则1,根据原则3,总位数应该为4的整数倍,因此最后应该填充两个字节:

1*** 1111 11**

d占用八个字节,根据原则1,起始地址应该为8的整数倍,现在为12,需要填充:

1*** 1111 11** **** 11111111

所以sizeif(B) = 24

typedef struct

{

   int a;

   double b;

   float c;

}A1;

struct

{

  char e[2];

  int  f;

  double g; 

  short h;

  A1 sa;

}B1;

sizeof(A1)=?;sizeof(B1)=?

先来看A1,

a占用四个字节:

1111

b占用八个字节,根据原则1,b的起始地址为8的整数倍,现在为1,应该填充七个字节:

1111**** 11111111

c占用四个字节,现在满足原则1,

1******* 11111111 1111

但是根据原则3,A1的总大小必须是8的倍数,因此,还要填充四个字节:

1111****11111111 1111****

所以sizeof(A1) = 24;

现在来看B1,

首先e中有两个char型元素,占两个字节:

11

int型的f占据四个字节,根据原则1,f的起始地址为4的整数倍,填充:

11**1111

double类型的g占八个字节,根据原则1,g的起始地址为8的整数倍,符合,不用填充:

11** 11111111 1111

short类型h占据二个字节,符合原则1,根据原则3,填充:

11** 11111111 11******

最后sa占据24个字节,其内部最大成员的字节数为8,因此sa的起始地址为8的整数倍,符合

11** 11111111 11****** 111111111111111111111111

所有sizeof(B1) = 48;

上述都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义。比如上面的结构体前加#pragma pack(1),则sizeof(A1) = 16; sizeof(B1) = 32;

有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐,即可理解为内部成员占用字节的和。

那么#pragma pack(2)呢?

代表按照两个字节对齐,这种情况下,sizeof(A1) = 16; sizeof(B1) = 32

#pragma pack(4)宏定义下:
sizeof(A1) = 16; sizeof(B1) = 36

最后介绍下结构体中的位域。

字面理解位域就是说某些数据元素并不需要占据一整个字节,只需要占据几位,例如数字8,就只需要占据一个字节的四位即可表示。所谓位域,就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。

例如下面的定义:

struct

{

 int a:8;

 int b:2;

 int c:6;

}A; 

位域a占8位,位域b占2位,位域c占6位,总共占据两个字节。

关于位域的对齐,有如下几点:

1. 如果相邻位域类型相同,位宽之和小于类型的sizeof大小,则后面的字段紧邻前一个字段存储,直到不能容纳为止;

2. 如果相邻位域类型相同,位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

3. 如果相邻位域类型不同,则vc6采取不压缩方式,dev-c++ 和GCC都采取压缩方式。依然满足结构体内存对齐三个原则中的原则1,在不压缩方式下,如果前一个位域类型有填充,后面的位域类型和前面的位域类型不相同,则填充的区域不能存放放后面的位域,需另开辟空间;而在压缩方式下,填充的区域如果可以放下后者位域,则存放,放不下的情况下再另开辟空间。

4.一个位域必须存储在同一个字节中,不能跨字节;

5.如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。

 struct

    {

          char a:2;

          int b:4;

          int c:4;

    }A;

在不压缩条件下:char类型占据一个字节,而a占用其中两位;根据原则1,b的类型int占据四个字节,应该从4的整数倍处开始存放,所以char后应该填充三个字节,这三个字节虽然能够容纳b,但是必须另开空间,再开辟四个字节的空间存放int,而b占据四个字节,后面c的类型和b的类型相同,所以紧邻b存储,占据四个字节。所以在vc下,sizeof(A) = 8;

压缩条件下:char类型占据一个字节,而a占用其中两位;根据原则1,b的类型int占据四个字节,应该从4的整数倍处开始存放,所以char后应该填充三个字节,这三个字节能够容纳b和c,因此不需要重新开辟空间,直接在这三个字节上存储,所以sizeof(A) = 4;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c struct sizeof