C/C++位域知識小結

幾篇較全面的位域相關的文章:

http://www.uplook.cn/blog/9/93362/

C/C++位域(Bit-fields)之我見

C中的位域與大小端問題

內存對齊全攻略–涉及位域的內存對齊原則

本文主要對位域相關知識進行了一下梳理,參考如下:

C語言中的位域

史上最全的C位域總結2

C結構體之位域(位段)

 

C/C++中以一定區域內的位(bit)爲單位來表示的數據成爲位域,位域必須指明具體的數目。

位域的作用主要是節省內存資源,使數據結構更緊湊。

1. 一個位域必須存儲在同一個字節中,不能跨兩個字節,故位域的長度不能大於一個字節的長度。

如一個字節所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。例如:

複製代碼
   struct BitField
   {
      unsigned int a:4;  //佔用4個二進制位;
      unsigned int  :0;  //空位域,自動置0;
      unsigned int b:4;  //佔用4個二進制位,從下一個存儲單元開始存放;
      unsigned int c:4;  //佔用4個二進制位;
      unsigned int d:5;  //佔用5個二進制位,剩餘的4個bit不夠存儲4個bit的數據,從下一個存儲單元開始存放;
      unsigned int  :0;  //空位域,自動置0;
      unsigned int e:4;  //佔用4個二進制位,從這個存儲單元開始存放;
   };
複製代碼

2. 取地址操作符&不能應用在位域字段上;

3. 位域字段不能是類的靜態成員;

4. 位域字段在內存中的位置是按照從低位向高位的順序放置的;

複製代碼
struct BitField
  {
    unsigned char a:2;  //最低位;
    unsigned char b:3;
    unsigned char c:3;  //最高位;
  };
  union Union
  {
    struct BitField bf;
    unsigned int n;
  };
  union Union ubf;
  ubf.n = 0;    //初始化;
  ubf.bf.a = 0; //二進制爲: 000
  ubf.bf.b = 0; //二進制爲: 000
  ubf.bf.c = 1; //二進制爲: 001
  printf("ubf.bf.n = %u\n", ubf.n);
複製代碼

位域中的位域字段按照從低位向高位順序方式的順序來看,那麼,a、b、c這三個位域字段在內存中的放置情況是:

最高位是c:001,中間位是b:000,最低位是a:000;所以,這個位域結構中的8二進制內容就是: 00100000,總共8個位,其十進制格式就是32;

實際上打印出來的ubf.n值就是32;

ubf.n = 100; //二進制爲: 01100100

printf("ubf.bf.a = %d, ubf.bf.b = %d, ubf.bf.c = %d\n", ubf.bf.a, ubf.bf.b, ubf.bf.c);

此時,對於位域ubf.bf來說,其位於字段仍然按照從低位向高位順序方式的順序放置,則,最高位是c:011,中間位是b:001,最低位是a:00;

所以,ubf.bf.a = 0; ubf.bf.b = 1; ubf.bf.c = 3;

實際上打印出來的結果也的確如此;不夠存儲下一個位域的4位,故設爲空位域,不使用,自動置0;e從第四個字節處開始存放,佔用4位;

5. 位域的對齊

1. 如果相鄰位域字段的類型相同,且其位寬之和小於類型的sizeof大小,則後面的字段將緊鄰前一個字段存儲,直到不能容納爲止;

2. 如果相鄰位域字段的類型相同,但其位寬之和大於類型的sizeof大小,則後面的字段將從新的存儲單元開始,其偏移量爲其類型大小的整數倍;

3.如果相鄰的兩個位域字段的類型不同,則各個編譯器的具體實現有差異,VC6採取不壓縮方式,GCC和Dev-C++都採用壓縮方式;

4. 整個結構體的總大小爲最寬基本類型成員大小的整數倍。

5. 如果位域字段之間穿插着非位域字段,則不進行壓縮;(不針對所有的編譯器)

複製代碼
  struct BFA
  {
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:3;
  };
  struct BFB
  {
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:3;
    unsigned int  d:4;  //多出來這個位域字段;
  };
複製代碼

sizeof(BFA)=1, sizeof(BFB)=8;

這也說明了第三點中"相鄰兩個位於字段類型不相同時,VC6採取不壓縮的方式"

6. 當要把某個成員說明成位域時,其類型只能是int,unsigned int與signed int三者之一(說明:int類型通常代表特定機器中整數的自然長度。short類型通常爲16位,long類型通常爲32位,int類型可以爲16位或32位.各編譯器可以根據硬件特性自主選擇合適的類型長度.見The C Programming Language中文 P32)。

儘管使用位域可以節省內存空間,但卻增加了處理時間,在爲當訪問各個位域成員時需要把位域從它所在的字中分解出來或反過來把一值壓縮存到位域所在的字位中.

複製代碼
#include <iostream>
 #include <memory.h>
 using namespace std;
 struct A
 {
     int a:5;
     int b:3;
 };
 int main(void)
 {
     char str[100] = "0134324324afsadfsdlfjlsdjfl";
         struct A d;
     memcpy(&d, str, sizeof(A));
     cout << d.a << endl;
     cout << d.b << endl;
     return 0;
 }
複製代碼

在32位x86機器上輸出:

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

解析:在默認情況下,爲了方便對結構體內元素的訪問和管理,當結構體內的元素長度都小於處理器的位數的時候,便以結構體裏面最長的元素爲對其單位,即結構體的長度一定是最長的數據元素的整數倍;如果有結構體內存長度大於處理器位數的元素,那麼就以處理器的位數爲對齊單元。由於是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)

 

另一個例子,來自http://blog.chinaunix.net/uid-28697486-id-3511598.htm

複製代碼
#include "stdio.h"

void main(int argn ,char *argv)
{
    struct     test {
        unsigned a:10;
        unsigned b:10;
        unsigned c:6;
        unsigned :2;//this two bytes can't use
        unsigned d:4;
        }data,*pData;
    data.a=0x177;
    data.b=0x111;
    data.c=0x7;
    data.d=0x8;
    
    pData=&data;
    printf("data.a=%x data.b= %x data.c=%x data.d=%xn",pData->a,pData->b,pData->c,pData->d);//位域可以使用指針

    printf("sizeof(data)=%dn",sizeof(data));   //4 bytes ,最常用的情況

    struct testLen{
    char a:5;
    char b:5;
    char c:5;
    char d:5;
    char e:5;
    }len;
    
    printf("sizeof(len)=%dn",sizeof(len));     //5bytes 規則2

    struct testLen1{
        char a:5;
        char b:2;
        char d:3;
        char c:2;
        char e:7;
        }len1;
    printf("sizeof(len1) =%dn",sizeof(len1));    //3bytes 規則1

    struct testLen2{
        char a:2;
        char :3;
        char b:7;
        long d:20; //4bytes
        char e:4;
        }len2;
    printf("sizeof(len2)=%dn",sizeof(len2));  //12 規則3,4,5,總長爲4的整數倍,2+3 佔1byte,b佔1bye 由於與long對其,2+3+7 佔4字節,後面 d 與 e進行了優化 佔一個4字節


    struct testLen3{
        char a:2;
        char :3;
        char b:7;
        long d:30;
        char e:4;
        }len3;
    printf("sizeof(len3)=%dn",sizeof(len3));//12 規則3,4,5,總長爲4的整數倍,2+3 佔1byte,b佔1bye 由於與long對其,2+3+7 佔4字節,後面 d佔一個4字節,爲了保證與long對其e獨佔一個4字節
}
複製代碼

 

另:C++標準庫提供了一個bitset 類模板,它可以輔助操縱位的集合。在可能的情況下應儘可能使用它來取代位域。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章