浅谈位域

作为C语言新手,只能凭自己看的一些资料谈一谈自己的理解,错误之处还望不吝指正。

位域(位段)
位域是指信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
—–摘自《百度百科》

1、位域的定义

位域的定义和位域变量与结构体的定义类型,其结构为:

struct  位域结构名{
    位域类型  位域名:位域长度;
    位域类型  :位域长度;
    ...
};

在K&R中,规定的位域类型只能是ints,应该是包括了signed和unsigned的char、short和int。而且最好注明是signed还是unsigned类型。虽然在C99中增加了新的支持类型,不过为了兼容性,还是遵循以前的标准吧。
我觉得,位域只是长度缩小了的位域类型,能对位域类型进行的操作,对位域也可以。比如对int型的位域,也可以用printf("%d", 位域名)来输出。

位域变量的说明和结构体变量的说明方式相同,可采用先定义后说明,同时定义说明或者直接说明三种方式,例如:

struct bf{
    int a:8;
    int b:2;
    int c:6;
}test;

说明test是bf的变量,共占两个字节。其中位域a占8位,b占2位,c占6位。

2、位域压缩与对齐

1.压缩:如果相邻位域字段的位域类型相同,各个位域字段只占定义时的bit长度。

struct bf{
    char a : 2;
    char b : 3;
    char c : 1;
}test;
其大小为 1 bytes,ab、c在一个char中。

2.压缩和字段对齐:一个位域必须储存在同一个字节中,不能跨两个字节。如果一个字节剩余的空间不够存放另一个位域的时候,应该从下一个单元起存放该位域。剩余空间补0例如:

struct bf{
    char a : 2;
    char b : 3;
    char c : 7;    //从下一单元开始存放
}test;

test.a = 2;
test.b = 2;
test.c = 2;
其大小为2 bytes,a和b在一个char中,c在第二个char

%d输出test可以发现,其结果为522,那么这是为什么呢,我们用一个图来表达:
这里写图片描述
本来位域C应该是继续往前放的,但是由于一个单元(字节)只有8位,不够了,所以它只能放到第二个单元(字节)里,而第一个单元的剩余位用0补齐,所以最后的答案就是522了。

3.位域类型对齐:如果相邻的位域字段的类型不同,在不同的位域类型间,按通用的对齐规则进行不同数据类型间的对齐(注意,struct的长度是其内部最宽类型的整数倍);同时在相同位域类型的字段间按以上两条规则对齐。

struct bf{
    char a : 1;
    char : 2;
    long b : 3;
    char c : 2;
}test;
其大小为 12 bytes。

因为结构体的对齐规则中,其中如果是上图这种情况,那么最长数据类型为long,占4个字节,其在内存空间存放方式如下:
|–char a–|—-|—-|—-| 4字节
|———-long———-| 4字节
|–char c–|—-|—-|—-| 4字节

4.终止压缩:如果位域字段中间穿插着非位域字段,则不进行压缩。

struct bf{
    char a : 2;
    long a1;
    char b : 3;
    long b1;
    char c : 7;
}test;
其大小为 20 bytes,如果注释掉//long a1;则为 12 bytes。

5.位域可以无位域名:这时它只是来作填充或者调整位置的。无名的位域是不能使用的。例如:

struct bf{
    int a : 4;
    int : 2;    //无位域名,该2位不能使用,只是调整位域b的位置
    int b : 4;
}

3、总结和问题

1.在查阅资料的过程中,发现有很多的都说,位域的长度是不能跨字节的,我个人认为这个是不对的,在实际的代码中就可以看出问题,比如:

#include<stdio.h>

struct bf{
    int a : 2;
    int b : 3;
    int c : 18;
}test;

int main(){
    test.a = 2;
    test.b = 2;
    test.c = 50 * 1024;
    printf("%d\n", test.c);
    printf("%d\n", test);
    return 0;
}
输出的test.c是40960,test是1310730,可以看出c是可以取到18bits的。

2.因为在一个byte中,也是要分big-endian和little-endian的,所以位域也不是那么地好用。

3.题目实战

#include<stdio.h>

struct mybitfields
{
    unsigned short a : 4;
    unsigned short b : 5;
    unsigned short c : 7;
} test;

int main()
{
    int i;
    test.a = 2;
    test.b = 3;
    test.c = 0;

    i = *((short *)&test);
    printf("%d\n", i);
    return 0;
}

那么这道题的答案就是50,至于为什么,就留给大家自己思考了,哈哈哈哈哈哈!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值