首页 > 编程知识 正文

c语言位域,人工智能使用的计算机语言

时间:2023-05-04 23:18:46 阅读:225725 作者:4978

目录 什么是位域位域的定义位域的使用使用位域的注意点(重要)实际应用

什么是位域

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个bit。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 bit即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。

所谓"位域"是把一个字节中的二进制位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

典型的应用场景:

用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。结构体中部分成员可能的赋值较小只需要用到1位或几位,可以把这类成员凑到一起减少整个结构体占用的空间。可巧妙用于位操作 位域的定义

位域定义与结构定义相仿,其形式为:

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

其中位域列表的形式为:

type [member_name] : width ;

其中 type :类型说明符; member_name: 位域名; width :位域长度;

例如:

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

char 类型变量大小为一个字节(八位), 取值范围为(-128, 127)。在上述位域的定义中,
a 只取三位,取值范围为(-4, 3)。如果 a 为无符号型( unsigned char), 则取值范围为(0, 7)。

位域的使用

位域的使用和结构成员的使用相同,其一般形式为:
位域变量名.位域名

位域变量指针名->位域名
位域允许用各种格式输出。

示例:

int 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,%dn",bit.a,bit.b,bit.c); /* 以整型量格式输出三个域的内容 */ pbit=&bit; /* 把位域变量 bit 的地址送给指针变量 pbit */ pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */ pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */ pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */ printf("%d,%d,%dn",pbit->a,pbit->b,pbit->c); /* 用指针方式输出了这三个域的值 */}

结果:

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

1、位域成员必须声明为整型int、unsigned int或signed int类型,或是char,unsigned char,但不能是浮点型包括float,double
在ANSI C 中,这几种数据类型是signed int和unsigned int;到了C99、C11新增了_Bool 的位字段。支持char一般是由于编译器扩充的。

2、位域的长度不能超过它所依附的数据类型的长度,成员变量都是有类型的,这个类型限制了成员变量的最大长度,: 后面的数字不能超过这个长度。
例如:

struct k{int a : 33;}

这里就会报错。

3、位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的(没有名称当然没法访问)。例如:

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

4、当无名位域且长度度为0时,表示下一个变量从下一段地址开始存储。(下一段地址的具体位置还得根据结构体的内存对齐原则)
示例:

#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)=%dn",sizeof(test)); printf("*ptr=%x ptr=%pn", *ptr,ptr); printf("*ptr=%x ptr=%pn", *(ptr+1), ptr + 1);}

结果:

上述例程中,位域a独占一个字节,b和c共占一个字节。
b直接从下一个字节地址开始存储了。

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

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

6、当结构体位域成员超出了限定的位数,将发生上溢(溢出中的一种)。

#include <stdio.h>struct{unsigned int age : 3;} Age;int main(){unsigned int* ptr = &Age;Age.age = 4;printf("Sizeof( Age ) : %dn", sizeof(Age));printf("Age.age : %dn", Age.age);Age.age = 7;printf("Age.age : %dn", Age.age);Age.age = 8; // 二进制表示为 1000 有四位,超出了定义的3位长度printf("Age.age : %dn", Age.age);printf("*p=%xn", *ptr);return 0;}

结果:

注意,从最后的*p=0可以看出,溢出没有导致其他位发现改变,否则这里就会等于8了。

7、位域可以和正常的结构体成员写到一起。如:

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

8、一个位域存储在N个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。即不能跨字节存储位域。
举例:

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

sizeof(struct pack) = 8

一个unsigned int是4字节,a占了12位,还剩20字节,b需要24位,已经不够了,又不能跨字节,因此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=%08Xn", *ptr);printf("*(ptr+1)=%08Xn", *(ptr+1));}

结果:

由上面的测试可以验证,a和b之间的确有空的区域,即结果中的00000部分;

9、整个结构体空间占用大小
含位域的结构体占用存储大小规则大致如下:

如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++,GCC采取压缩方式;如果位域字段之间穿插着非位域字段,则不进行压缩(非位域字段保持其类型的对应大小);整个结构体的总大小为最宽基本类型成员大小的整数倍。

第1,2点规则实际就是上面第8点所说的注意项问题,参考上文的示例即可。
第3点相邻的位域字段的类型不同,则各编译器的具体实现有差异举例:

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

在Visual Studio 2019上运行结果:

在Linux上GCC编译运行结果:
4
(gcc version:4.8.2)

第四点如果位域字段之间穿插着非位域字段,则不进行压缩,指的是非位域字段不压缩

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

如上述示例程序struct test的d就是插在位域中的非位域字段,它的类型是int,所以d占4个字节,a和b共占4个字节,c单独占4个字节,因此sizeof(struct test) = 12

对于有位域的结构体占用存储大小的问题,做一个精简的方法总结:
带有’位域’的结构体并不是按照每个域对齐的,而是将一些位域 成员’捆绑’在一起做对齐的。"捆绑"还需注意跨字节问题,不能跨字节存储位域。位域捆绑后,按照普通结构体占用大小的规则计算即可。

实际应用

示例:

#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=%02Xn",sta->value);printf("sizeof=%dn", sizeof(sta->value));sta->BIT.D0 = 0;//给第一个位赋值printf("sta.value=%Xn", sta->value);printf("a=%Xn", a);return 0;}

上述例程,使用union和位域实现对变量的某一bit位进行操作;该方式可用于计算机操作底层硬件寄存器。
结果:

参考:
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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。