日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

C语言 位域的使用

發(fā)布時間:2024/1/23 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言 位域的使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 什么是位域
  • 位域的定義
  • 位域的使用
  • 使用位域的注意點(重要)
  • 實際應(yīng)用

什么是位域

有些信息在存儲時,并不需要占用一個完整的字節(jié),而只需占幾個或一個bit。例如在存放一個開關(guān)量時,只有 0 和 1 兩種狀態(tài),用 1 bit即可。為了節(jié)省存儲空間,并使處理簡便,C 語言又提供了一種數(shù)據(jù)結(jié)構(gòu),稱為"位域"或"位段"。

所謂"位域"是把一個字節(jié)中的二進(jìn)制位劃分為幾個不同的區(qū)域,并說明每個區(qū)域的位數(shù)。每個域有一個域名,允許在程序中按域名進(jìn)行操作。這樣就可以把幾個不同的對象用一個字節(jié)的二進(jìn)制位域來表示。

典型的應(yīng)用場景:

  • 用 1 位二進(jìn)位存放一個開關(guān)量時,只有 0 和 1 兩種狀態(tài)。
  • 讀取外部文件格式——可以讀取非標(biāo)準(zhǔn)的文件格式。例如:9 位的整數(shù)。
  • 結(jié)構(gòu)體中部分成員可能的賦值較小只需要用到1位或幾位,可以把這類成員湊到一起減少整個結(jié)構(gòu)體占用的空間。
  • 可巧妙用于位操作

位域的定義

位域定義與結(jié)構(gòu)定義相仿,其形式為:

struct 位域結(jié)構(gòu)名 {位域列表 };

其中位域列表的形式為:

type [member_name] : width ;

其中 type :類型說明符; member_name: 位域名; width :位域長度;

例如:

struct TESTA {char a:3;char b:5;char c:4;char d:4; };

char 類型變量大小為一個字節(jié)(八位), 取值范圍為(-128, 127)。在上述位域的定義中,
a 只取三位,取值范圍為(-4, 3)。如果 a 為無符號型( unsigned char), 則取值范圍為(0, 7)。

位域的使用

位域的使用和結(jié)構(gòu)成員的使用相同,其一般形式為:
位域變量名.位域名

位域變量指針名->位域名
位域允許用各種格式輸出。

示例:

int main() {struct bs{unsigned a:1;unsigned b:3;unsigned c:4;} bit,*pbit;bit.a=1; /* 給位域賦值(應(yīng)注意賦值不能超過該位域的允許范圍) */bit.b=7; /* 給位域賦值(應(yīng)注意賦值不能超過該位域的允許范圍) */bit.c=15; /* 給位域賦值(應(yīng)注意賦值不能超過該位域的允許范圍) */printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式輸出三個域的內(nèi)容 */pbit=&bit; /* 把位域變量 bit 的地址送給指針變量 pbit */pbit->a=0; /* 用指針方式給位域 a 重新賦值,賦為 0 */pbit->b&=3; /* 使用了復(fù)合的位運算符 "&=",相當(dāng)于:pbit->b=pbit->b&3,位域 b 中原有值為 7,與 3 作按位與運算的結(jié)果為 3(111&011=011,十進(jìn)制值為 3) */pbit->c|=1; /* 使用了復(fù)合位運算符"|=",相當(dāng)于:pbit->c=pbit->c|1,其結(jié)果為 15 */printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指針方式輸出了這三個域的值 */ }

結(jié)果:

使用位域的注意點(重要)

1、位域成員必須聲明為整型int、unsigned int或signed int類型,或是char,unsigned char,但不能是浮點型包括float,double
在ANSI C 中,這幾種數(shù)據(jù)類型是signed int和unsigned int;到了C99、C11新增了_Bool 的位字段。支持char一般是由于編譯器擴充的。

2、位域的長度不能超過它所依附的數(shù)據(jù)類型的長度,成員變量都是有類型的,這個類型限制了成員變量的最大長度,: 后面的數(shù)字不能超過這個長度。
例如:

struct k{int a : 33; }

這里就會報錯。

3、位域可以是無名位域,這時它只用來作填充或調(diào)整位置。無名的位域是不能使用的(沒有名稱當(dāng)然沒法訪問)。例如:

struct k{int a:1;int :2; /* 該 2 位不能使用 */int b:3;int c:2; };

4、當(dāng)無名位域且長度度為0時,表示下一個變量從下一段地址開始存儲。(下一段地址的具體位置還得根據(jù)結(jié)構(gòu)體的內(nèi)存對齊原則)
示例:

#include <stdio.h>struct k {char a : 2;char : 0;char b : 2;char c : 2; }; struct k test; int main() {char* ptr = (char *)&test;test.a = 1;test.b = 1;test.c = 1;printf("siezof(test)=%d\n",sizeof(test));printf("*ptr=%x ptr=%p\n", *ptr,ptr);printf("*ptr=%x ptr=%p\n", *(ptr+1), ptr + 1); }

結(jié)果:

上述例程中,位域a獨占一個字節(jié),b和c共占一個字節(jié)。
b直接從下一個字節(jié)地址開始存儲了。

5、不能使用位域的地址,如:

struct k {char a : 2;char : 0;char b : 2;char c : 2; }; struct k test; printf("&test.a=%p\n",&test.a);//報錯 不能使用位域的地址

6、當(dāng)結(jié)構(gòu)體位域成員超出了限定的位數(shù),將發(fā)生上溢(溢出中的一種)。

#include <stdio.h>struct {unsigned int age : 3; } Age;int main() {unsigned int* ptr = &Age;Age.age = 4;printf("Sizeof( Age ) : %d\n", sizeof(Age));printf("Age.age : %d\n", Age.age);Age.age = 7;printf("Age.age : %d\n", Age.age);Age.age = 8; // 二進(jìn)制表示為 1000 有四位,超出了定義的3位長度printf("Age.age : %d\n", Age.age);printf("*p=%x\n", *ptr);return 0; }

結(jié)果:

注意,從最后的*p=0可以看出,溢出沒有導(dǎo)致其他位發(fā)現(xiàn)改變,否則這里就會等于8了。

7、位域可以和正常的結(jié)構(gòu)體成員寫到一起。如:

struct {unsigned int age1 : 3;unsigned int age2 : 3;unsigned int age3;unsigned int age4 : 3; } Age;

8、一個位域存儲在N個字節(jié)中,如一個字節(jié)所剩空間不夠存放另一位域時,則會從下一單元起存放該位域。即不能跨字節(jié)存儲位域。
舉例:

struct pack {unsigned int a:12; unsigned int b:24; unsigned int c:6; };

sizeof(struct pack) = 8

一個unsigned int是4字節(jié),a占了12位,還剩20字節(jié),b需要24位,已經(jīng)不夠了,又不能跨字節(jié),因此b只能存在下一個unsigned int。

程序驗證:

struct pack {unsigned int a : 12;unsigned int b : 24;unsigned int c : 6; }; struct pack test; int main() {unsigned int* ptr = (unsigned int *)&test;test.a = 0xFFF;test.b = 0xFFFFFF;test.c = 0x3F;printf("*ptr=%08X\n", *ptr);printf("*(ptr+1)=%08X\n", *(ptr+1)); }

結(jié)果:

由上面的測試可以驗證,a和b之間的確有空的區(qū)域,即結(jié)果中的00000部分;

9、整個結(jié)構(gòu)體空間占用大小
含位域的結(jié)構(gòu)體占用存儲大小規(guī)則大致如下:

  • 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個字段存儲,直到不能容納為止
  • 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍;
  • 如果相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++,GCC采取壓縮方式;
  • 如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮(非位域字段保持其類型的對應(yīng)大小);
  • 整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。
  • 第1,2點規(guī)則實際就是上面第8點所說的注意項問題,參考上文的示例即可。
    第3點相鄰的位域字段的類型不同,則各編譯器的具體實現(xiàn)有差異舉例:

    #include <stdio.h>struct test {char a : 2;char b : 3;int c : 1; };int main(void) {printf("%d\n", sizeof(struct test));return 0; }

    在Visual Studio 2019上運行結(jié)果:

    在Linux上GCC編譯運行結(jié)果:
    4
    (gcc version:4.8.2)

    第四點如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮,指的是非位域字段不壓縮

    #include <stdio.h>struct test {char a : 2;char b : 3;int d ;int c : 1; };int main(void) {printf("%d\n", sizeof(struct test));return 0; }

    如上述示例程序struct test的d就是插在位域中的非位域字段,它的類型是int,所以d占4個字節(jié),a和b共占4個字節(jié),c單獨占4個字節(jié),因此sizeof(struct test) = 12

    對于有位域的結(jié)構(gòu)體占用存儲大小的問題,做一個精簡的方法總結(jié):
    帶有’位域’的結(jié)構(gòu)體并不是按照每個域?qū)R的,而是將一些位域 成員’捆綁’在一起做對齊的。"捆綁"還需注意跨字節(jié)問題,不能跨字節(jié)存儲位域。位域捆綁后,按照普通結(jié)構(gòu)體占用大小的規(guī)則計算即可。

    實際應(yīng)用

    示例:

    #include <stdio.h>union STATE {struct BITDATA{int D0 : 1;int D1 : 1;int D2 : 1;int D3 : 1;int D4 : 1;int D5 : 1;int D6 : 1;int D7 : 1;}BIT;int value; };int main(void) {int a = 0xFF;union STATE* sta;sta = &a;printf("sta.value=%02X\n",sta->value);printf("sizeof=%d\n", sizeof(sta->value));sta->BIT.D0 = 0;//給第一個位賦值printf("sta.value=%X\n", sta->value);printf("a=%X\n", a);return 0; }

    上述例程,使用union和位域?qū)崿F(xiàn)對變量的某一bit位進(jìn)行操作;該方式可用于計算機操作底層硬件寄存器。
    結(jié)果:

    參考:
    https://blog.51cto.com/u_15244533/2845234
    https://blog.51cto.com/u_14207158/2352294
    https://blog.51cto.com/u_9233403/2121352
    https://www.runoob.com/cprogramming/c-bit-fields.html
    https://blog.csdn.net/sty124578/article/details/79456405

    總結(jié)

    以上是生活随笔為你收集整理的C语言 位域的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。