在C语言中通常操作只有真假两种状态的的数据时使用布尔bool变量比较多,如果需要同时观察多个状态,这时候选择位操作效率会更高,用一个字节的8位分别表示8种状态。比较节省内存,处理起来效率更高。但是这种方法有一个缺点,就是看起来不直观,比如0x5C要想知道每一位的状态还得换算一下,同时要操作某一个单独位时,还必须使用位操作运算,比如位与、位或、异或。
如果熟悉单片机的就会想到,能不能在C语言中像操作寄存器那样直接操作一个字节的单独一位呢?
比如这是一个单片机的端口方向配置寄存器,每一个端口有8个口,每个口可以单独配置为输出模式或者输入模式。比如现在设置 PA_DDR.DDR3 = 1;PA_DDR.DDR6 = 0; 那么就代表此时将PA口的端口3设置为输出模式,端口6设置为输入模式。用这种方式操作时,比直接使用位操作看起来更加的直观明了。
在C语言中也有这也得一种操作方式,它叫做 位字段 ,位字段是一个 signed int 或 unsigned int 类型变量中的一组相邻位。为字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。
struct
{
unsigned int Sun: 1;
unsigned int Mon: 1;
unsigned int Tue: 1;
unsigned int Wed: 1;
unsigned int Thur: 1;
unsigned int Fri: 1;
unsigned int Sat: 1;
} week;
在上面的这个 星期的结构中,提供了7个1位的字段,每一个字段就代表其中一天。这个就可以通过结构成员运算符点( . )来对每一个字符赋值。
week.Mon = 1;
week.Tue = 1;
week.Wed = 1;
week.Thur = 1;
week.Fri = 1;
week.Sat = 0;
week.Sun = 0;
通过结构运算符设置周一到周五的值为1,周末值为0。通过这样的方式可以单独操作字节中的某一位,和单片机中对于寄存器的操作就很类似了。由于每个字段只占了一位,所以字符的值只能是0或者1。还可以将每个字段设置为多个位。
struct {
unsigned int code1 : 2;
unsigned int code2 : 3;
unsigned int code3 : 8;
} prcode;
创建了一个2位字段,一个3位字段,一个8位字段。可以这样赋值:
prcode.code1 = 3;
prcode.code2 = 7;
prcode.code3 = 255;
赋值的大小不能超过字段所能表示的最大范围。
在第一个例子中,字段的所有位数加起来只有7位,但是在第二个例子中,所有的字段位数加起来有13位,那么在内存中这两种情况是如何存储的呢?
在C语言中规定,如果声明的总位数超过了一个unsigned int 类型的大小,就会用到下一个unsigned int 类型的存储位置,一个字段不允许跨越两个unsigned int之间的边界,编译器会自动移动跨界的字段。保持unsigned int的边界对齐。
为了方便观察内存中的数据情况,将代码移植到单片机中。来观察在内存中的存储情况。
struct
{
unsigned int Sun: 1;
unsigned int Mon: 1;
unsigned int Tue: 1;
unsigned int Wed: 1;
unsigned int Thur: 1;
unsigned int Fri: 1;
unsigned int Sat: 1;
} week;
struct
{
unsigned int code1 : 5;
unsigned int code2 : 5;
unsigned int code3 : 7;
} prcode;
week.Sun = 1;
week.Mon = 0;
week.Tue = 1;
week.Wed = 0;
week.Thur = 1;
week.Fri = 0;
week.Sat = 1;
prcode.code1 = 0x03;
prcode.code2 = 0x11;
prcode.code3 = 0x22;
cnt1 = sizeof(week);
cnt2 = sizeof(prcode);
给两个位字段的每一段分别赋值,最后通过sizeof()计算着两个位字段占用内存的大小。
week每个字段赋值完成后,内存中的数据变成了55。
字段Sun为最低位,字段Sat为最高位。
接下来存储prcode字段的数据。
存储完 0x03和0x11时,内存中的数据变成了02 23.
code1字段和code1字段各占了5位,所以最低5位是code1的值,紧接着5位是code2的值。其余位默认为0。
最后给code3字段赋值,code3占7位,值为0x22。
最后计算出week占用2个字节,prcode占用4个字节。
当在内存中编译器自动边界对齐后,就会留下一个未命名的“洞”,可以使用未命名的字段宽度填充未命名的“洞”。如下代码:
struct
{
unsigned int code1 : 1;
unsigned int : 4;
unsigned int code2 : 1;
unsigned int : 1;
unsigned int code3 : 1;
} prcode;
在code1和code2之间有4个空洞,在code2和code3之间有1个空洞。
内存中的数据是 a1 。
可以看到“空洞”的位置被自动填充为0。在C语言中规定,位字段是以unsigned int作为基本的布局单元,即使一个结构的成员是1位字段,在内存中该结构的大小也是一个unsigned int类型的大小。另外在位字段中布尔类型 (bool)也是可以使用的。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_20222919/article/details/121241095
内容来源于网络,如有侵权,请联系作者删除!