• C结构体之位域(位段)


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

    struct 位域结构名 
    {
    
     位域列表
    
    };

    其中位域列表的形式为:

    类型说明符 位域名位域长度

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

    struct bs
    {
      int a:8;
      int b:2;
      int c:6;
    }data; 

    说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:


    1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

    struct bs
    {
        unsigned a:4
        unsigned b:5 /*从下一单元开始存放*/
        unsigned c:4
    }

    2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度

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

    struct k
    {
        int a:1
        int :2 /*无位域名,该2位不能使用*/
        int b:3
        int c:2
    }; 

    二、位域的使用

    下面例子是参加一个公司(白领科技-青岛)的笔试遇到的,当时做错了,为了怕忘了,赶紧写下来。

     1 #include <iostream>
     2 #include <memory.h>
     3 using namespace std;
     4 struct A
     5 {
     6     int a:5;
     7     int b:3;
     8 };
     9 int main(void)
    10 {
    11     char str[100] = "0134324324afsadfsdlfjlsdjfl";
    12         struct A d;
    13     memcpy(&d, str, sizeof(A));
    14     cout << d.a << endl;
    15     cout << d.b << endl;
    16     return 0;
    17 }

    在32位x86机器上输出:

    ?
    $ ./langxun.exe
    -16
    1

    解析:在默认情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素长度都小于处理器的位数的时候,便以结构体里面最长的元素为对其单位,即结构体的长度一定是最长的数据元素的整数倍;如果有结构体内存长度大于处理器位数的元素,那么就以处理器的位数为对齐单元。由于是32位处理器,而且结构体中a和b元素类型均为int(也是4个字节),所以结构体的A占用内存为4个字节。

    上例程序中定义了位域结构A,两个个位域为a(占用5位),b(占用3位),所以a和b总共占用了结构A一个字节(低位的一个字节)。

    当程序运行到14行时,d内存分配情况:

     高位 00110100 00110011   00110001    00110000 低位
    '4' '3' '1' '0'
    其中d.a和d.b占用d低位一个字节(
    00110000),d.a : 10000, d.b : 001

     d.a内存中二进制表示为10000,由于d.a为有符号的整型变量,输出时要对符号位进行扩展,所以结果为-16(二进制为11111111111111111111111111110000)

     d.b内存中二进制表示为001,由于d.b为有符号的整型变量,输出时要对符号位进行扩展,所以结果为1(二进制为00000000000000000000000000000001)

     三、位域的对齐

      如果结构体中含有位域(bit-field),那么VC中准则是:

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

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

      3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

      系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。





    有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
    一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: 
    struct 位域结构名 
    { 位域列表 };
    其中位域列表的形式为: 类型说明符 位域名:位域长度 
    例如:

    struct bs
    {
    int a:8;
    int b:2;
    int c:6;
    }; 
    位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:

    struct bs
    {
    int a:8;
    int b:2;
    int c:6;
    }data; 
    说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:
    1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

    struct bs
    {
    unsigned a:4
    unsigned :0 /*空域*/
    unsigned b:4 /*从下一单元开始存放*/
    unsigned c:4
    }
    在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
    2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
    3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

    struct k
    {
    int a:1
    int :2 /*该2位不能使用*/
    int b:3
    int c:2
    }; 
    从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。
    二、位域的使用
    位域的使用和结构成员的使用相同,其一般形式为: 位域变量名·位域名 位域允许用各种格式输出。

    main(){
    struct bs
    {
    unsigned a:1;
    unsigned b:3;
    unsigned c:4;
    } bit,*pbit;
    bit.a=1;
    bit.b=7;
    bit.c=15;
    printf("%d,%d,%d\n",bit.a,bit.b,bit.c);
    pbit=&bit;
    pbit->a=0;
    pbit->b&=3;
    pbit->c|=1;
    printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);

    上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。
    程序的9、10、11三行分别给三个位域赋值。( 应注意赋值不能超过该位域的允许范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针 方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符"&=", 该行相当于: pbit->b=pbit->b&3位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值为 3)。同样,程序第16行中使用了复合位运算"|=", 相当于: pbit->c=pbit->c|1其结果为15。程序第17行用指针方式输出了这三个域的值。

    为了节省空间,可以把几个数据压缩到少数的几个类型空间上,比如需要表示二个3位二进制的数,一个2位二进制的数,则可以用一个8位的字符表示之。
    struct 
    {
    char a : 3;
    char b : 3;
    char c : 2;
    } ;
    这个结构体所占空间为一个字节,8位。节省了空间。

    三、位域的对齐

      如果结构体中含有位域(bit-field),那么VC中准则是:
      1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
      2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
      3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;
      系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。
  • 相关阅读:
    算法训练 表达式计算
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    New ways to verify that Multipath TCP works through your network
    TCP的拥塞控制 (Tahoe Reno NewReno SACK)
    Multipath TCP Port for Android 4.1.2
    How to enable ping response in windows 7?
    NS3
    Multipath TCP Port for Android
  • 原文地址:https://www.cnblogs.com/lgh1992314/p/5835300.html
Copyright © 2020-2023  润新知