验证CprimerPlus中关于位字段的性质

在CprimerPlus中有这种表述

如果声明的总位数超过了一个unsigned int类型的大小会怎么?用到下一个unsigned int 类型的储存未知。一个字段不允许跨越两个unsigned int之间的边界。编译器会移动自动跨界的字段,保持unsigned int的边界对齐。一旦发生这种情况,第一个unsigned int中会留下一个未命名的“洞”<\br>可以用未命名的字段宽度填充未命名的“洞”。使用一个宽度未0的未命名字段迫使下一个字段与下一个整数对齐<\q>

位字段肯定是小于一个unsigned int的,所以这里指的跨越边界应该是多个位字段排列下去导致某个位字段跨越了unsigned int的边界,假设在一个系统中unsigned int为32位,则会出现这种情况验证CprimerPlus中关于位字段的性质

 

编译器处理之后

 验证CprimerPlus中关于位字段的性质

 

然后可以大小为0的位字段使得下一个字段对齐。

看完这些之后,我就写了一些代码想要验证它说的具体是怎样的,需要计算位字段field的大小和整个位字段结构体的大小。

第一版的代码是这样的:

#include<stdio.h>
#include<limits.h>//包含CHAR_BIT

int main(void)
{
        int cb = CHAR_BIT;//使cb等于一个字节的位数
        //定义位字段结构体
        struct{
                unsigned int field1 : 10;
                unsigned int field2 : 10;
                unsigned int field3 : 10;
                unsigned int field4 : 10;
        } try;
        printf("Size: field1 = %d, field2 = %d, field3 = %d, field4 = %d\n",                sizeof(try.field1)*cb, sizeof(try.field2)*cb,                 sizeof(try.field3)*cb, sizeof(try.field4)*cb);
        printf("Size: unsigned int = %d, try = %d",                sizeof(unsigned int)*cb, sizeof(try)*cb);

        return 0;
}

但是我编译之后发现编译器报错

try2.c: In function ‘main’:
try2.c:14: error: ‘sizeof’ applied to a bit-field
try2.c:14: error: ‘sizeof’ applied to a bit-field
try2.c:15: error: ‘sizeof’ applied to a bit-field
try2.c:15: error: ‘sizeof’ applied to a bit-field

简单的查了一下,发现sizeof不能用于单个位字段的大小,想想也合理毕竟sizeof给出的返回值是整数的字节数,单个位字段可能小于1字节。

那么位字段怎么计算比特位大小呢,百度之后并没有找到C相关的函数,所以我只能自己写了。了解了c中整数的存储原理,给某个整数或者类似的和整数存储方式相同的变量赋值1,就可以使这个变量的每个比特位都为1。 

根据这个使用位逻辑运算符我想出两种方案,方案1较为直观,方案2则要巧妙一些。

方案1:

        int i=-1, count=0, mask=1;
//使用mask盖住前面的位,因为移位运算符会用0填充,所以最后一位被移出时循环停止
        while(i & mask){
                i >>= i;
                count++;
        }

方案2:

        int i=-1, count=0;
//每次将一个位归零
        while(i){
                i &= i-1;
                count++;
        } 

最后采用方案二,代码如下:

#include<stdio.h>
#include<limits.h>
int main(void)
{
        struct{
                unsigned int field1 : 10;
                unsigned int field2 : 10;
                unsigned int field3 : 10;
                unsigned int field4 : 10;
        }try1;
        struct{
                unsigned int field1 : 10;
                unsigned int : 0;
                unsigned int field2 : 10;
        }try2;
        int size1=0, size2=0, size3=0, size4=0;
//初始化field
        try1.field1=-1, try1.field2=-1,try1.field3=-1, try1.field4=-1;
        while(try1.field1){
                try1.field1 &= try1.field1-1;
                size1++;
        }
        while(try1.field2){
                try1.field2 &= try1.field2-1;
                size2++;
        }
        while(try1.field3){
                try1.field3 &= try1.field3-1;
                size3++;
        }
        while(try1.field4){
                try1.field4 &= try1.field4-1;
                size4++;
        }
        printf("size: try1 = %d\n", sizeof(try1)*CHAR_BIT);
        printf("try1: size:\n field1 = %d, field2 = %d, field3 = %d, \
field4 = %d\n", \
        size1, size2, size3, size4);
//初始化field和size
        size1=0, size2=0;
        try2.field1=-1, try2.field2 = -1;
        while(try2.field1){
                try2.field1 &= try2.field1-1;
                size1++;
        }
        while(try2.field2){
                try2.field2 &= try2.field2-1;
                size2++;
        }
        printf("size: try2 = %d\n", sizeof(try2)*CHAR_BIT);
        printf("try2: size:\n field1 = %d, field2 = %d\n", size1, size2);

        return 0;
}
                          

最后得到的结果符合书上描述:

[lyb@localhost not_myc]$ gcc try.c -o test
[lyb@localhost not_myc]$ ./test
size: try1 = 64
try1: size:
 field1 = 10, field2 = 10, field3 = 10, field4 = 10
size: try2 = 64
try2: size:
 field1 = 10, field2 = 10

 

验证CprimerPlus中关于位字段的性质

上一篇:ruoyi-vue前后端分类nginx配置


下一篇:Kong03-Nginx、OpenResty、Kong 的基本概念和区别联系