C语言中的位域

在内存中存取数据的最小单位一般是字节,但是有时候并不需要一个字节来存储。例如对于只有真假两个值得一个变量来说,就只须一位就可以了。有比如说在某些情况下内存是极其宝贵的资源,比如说一些嵌入式设备,因此要格外的注意节约。C语言可以在一个结构体中以位来存储数据,这种用位为单位的成员称为位域或位段。

1
2
3
4
5
 struct bit_data  
{
int a:2;
char c:3;
};

虽然位域的机制提供了一种使用非字节数内存的方法,但是位域在使用的时候还是要注意一些问题的,比如说,位域成员的长度不能超过其自身声明类型的长度char不能超过8,下面是两个比较主要的问题:
(1)成员值的问题
声明一个位域如下

1
2
3
4
struct bool  
{
int a:1;
};

希望声明一个这样的位域用他来充当bool型,但是实际的情况却和想象的不一样。赋1,输出得到的切实-1。这是因为在二进制补码中,最高有效位也称为符号位,当被设置为1时,表示负数,而当被设置为0时,值为非负。这里只有一位,那么他自身就是最高有效位,那么赋1他就是负数,在这里是-1,而0则是0这个非负数。下面是一个完整的测试程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>  
#include <stdlib.h>

int main()
{

struct inti{
int a:2;
};
struct inti data;
int i;
for (i = 0; i < 4; i++)
{
data.a = i;
printf("%2d ",data.a);
printf("%u\n",data.a);
}
return 0;
}

输出如下:
0 0
1 1
-2 4294967294
-1 4294967295
所赋数值的表示
赋的数值 二进制表示 内存中值(32bit) 有符号数值 无符号数值
0 00 0x00000000 0 0
1 01 0x00000001 1 1
2 10 0xFFFFFFFE -2 4294967294
3 11 0xFFFFFFFF -1 4294967295
把他们的二进制表示的值按照有符号数的补码对应的值计算出来就是有符号数值。这应该就是输出中第一列的原因,第二列的输出应该还和分配的内存有关。
(2)内存分配与对齐
1、只包含一种基本类型的。按位域中说声明的基本类型对齐,比如在32位机(机器字长)上的char,分配1byte。int分配4byte等。不超过一个基本类型长度的,分配一个基本类型长度,超过的取其基本长度的N倍,N与位域实际长度的差不大于一个基本长度。

1
struct bit1{  
    int a:2;  
    int b:2;  
};

sizeof(struct bit1)返回4;

1
struct bit2{  
    int a:18;  
    int b:18;  
};

sizeof(struct bit2)返回8;
2、包含多种基本类型的。按位域中声明的最长基本类型的长度对其,比如说一个包含char和int两种基本类型的位域,按int的长度对齐。会进行拼凑以节约内存。比如:

1
struct bit3{  
    int e:2;  
    long long d:36;  
    int a:32;  
    int b:32;  
    char c:7;  
};

sizeof(struct bit3)返回20。
总结(普通32位pc)
(1)用位域来实现bool型变量与用一个int型来实现其实是一样的。并不能起到节约内存的作用,所以想要使用单个的位域时并不会节约内存,如果声明一个位域的数组就是另一种情况了。
(2)编译器为优化内存结构,会进行对齐,这个过程也不是节约内存的。在内存足够的情况下,编译器更注重访问速度,而不是节约内存。

分享到 评论