【问题标题】:Size of a bitfield member?位域成员的大小?
【发布时间】:2023-03-12 20:30:01
【问题描述】:

有谁知道如何提取位域成员的大小。下面的代码自然地给了我一个整数的大小,但是我如何找出mybits.one 中有多少位或字节?我已经尝试过sizeof(test.one),但这显然行不通。我意识到这是位的度量:

#include <iostream>

using namespace std;

int main()
{
    struct mybits {
        unsigned int one:15;
    };

    mybits test;
    test.one = 455;
    cout << test.one << endl;
    cout << "The size of test.one is:  " << sizeof(test) << endl;
}

【问题讨论】:

标签: c++ bits bit-fields


【解决方案1】:

使用constexpr的编译时解决方案:

struct S
{
    unsigned int a : 4;
    unsigned int b : 28;
};

#define GET_BIT_FIELD_WIDTH(T, f) \
    []() constexpr -> unsigned int \
    { \
        T t{}; \
        t.f = ~0; \
        unsigned int bitCount = 0; \
        while (t.f != 0) \
        { \
            t.f >>= 1; \
            ++bitCount; \
        } \
        return bitCount; \
    }()

int main()
{
    constexpr auto a = GET_BIT_FIELD_WIDTH(S, a);
    constexpr auto b = GET_BIT_FIELD_WIDTH(S, b);
    static_assert(a == 4);
    static_assert(b == 28);
}

认为它不会调用任何未定义的行为,但它确实会调用一些实现定义的行为:

  1. 位域的环绕是实现定义的。
  2. 如果签名字段的右移使用符号扩展(实现定义),上述解决方案将不适用于签名字段。在这种情况下,编译器将陷入无限循环。

【讨论】:

    【解决方案2】:

    这是一个有点棘手的通用版本:

    #include <iostream>
    #include <limits>
    #include <bitset>
    #include <cstring>
    using namespace std;
    
    template <class T>
    T umaxof()
    {
          T t;
          memset(&t, 0xFF, sizeof(T));
          return t;
    }
    
    template <class T>
    size_t bitsof(const T& umax)
    {
        return bitset<sizeof(T)*8>(umax).count();
    }
    
    int main() 
    {
        struct A
        {
            uint32_t bf1:19;
            uint32_t bf2:1;
        };
    
        cout << bitsof(umaxof<A>().bf1) << "\n";
        cout << bitsof(umaxof<A>().bf2) << "\n";
    
        return 0;
    }
    

    可以在https://ideone.com/v4BiBH试用

    注意:仅适用于无符号位字段。

    【讨论】:

      【解决方案3】:

      draft C++ standard 表示 sizeof 不应应用于 5.3.3Sizeof 段落 1 部分中的位域。如果您可以控制源代码,那么使用 enum 听起来更简单、更整洁:

      struct mybits
      {
          enum bitFieldSizes
          {
              field1 = 15,
              field2 = 2,
              field3 = 4,
              field4 = 8,
              field5 = 31
          };
      
          unsigned int one : field1 ;  
          unsigned int two : field2 ;  
          unsigned int three : field3 ;
          unsigned int four : field4 ;
          unsigned int five : field5 ;
      };
      

      如果您无法控制源,则可以使用 bit hacks 来获取您的位域的大小,std::bitset 更容易:

      #include <iostream>
      #include <bitset>
      
      struct mybits
      {
          unsigned int one : 15 ;  
          unsigned int two : 2 ;  
          unsigned int three : 4 ;
          unsigned int four : 8 ;
          unsigned int five : 31 ;
      };
      
      int main()
      {
          mybits mb1 ;
      
          mb1.one   =  ~0 ;
          mb1.two   =  ~0 ;
          mb1.three =  ~0 ;
          mb1.four  =  ~0 ;
          mb1.five  =  ~0 ;
      
          std::bitset<sizeof(unsigned int)*8> b1(mb1.one);
          std::bitset<sizeof(unsigned int)*8> b2(mb1.two);
          std::bitset<sizeof(unsigned int)*8> b3(mb1.three);
          std::bitset<sizeof(unsigned int)*8> b4(mb1.four);
          std::bitset<sizeof(unsigned int)*8> b5(mb1.five);
      
          std::cout << b1 << ":" << b1.count() << std::endl ;
          std::cout << b2 << ":" << b2.count() << std::endl ;
          std::cout << b3 << ":" << b3.count() << std::endl ;
          std::cout << b4 << ":" << b4.count() << std::endl ;
          std::cout << b5 << ":" << b5.count() << std::endl ;
      }
      

      产生以下输出:

      00000000000000000111111111111111:15
      00000000000000000000000000000011:2
      00000000000000000000000000001111:4
      00000000000000000000000011111111:8
      01111111111111111111111111111111:31
      

      【讨论】:

        【解决方案4】:

        运行时解决方案,来自本次讨论的想法:http://social.msdn.microsoft.com/Forums/en-US/7e4f01b6-2e93-4acc-ac6a-b994702e7b66/finding-size-of-bitfield

        #include <iostream>
        using namespace std;
        
        int BitCount(unsigned int value)
        {
            int result = 0;
        
            while(value)
            {
                value &= (value - 1);
                ++result;
            }
        
            return result;
        }
        
        int main()
        {
            struct mybits {
                unsigned int one:15;
            };
        
            mybits test;
            test.one = ~0;
        
            cout << BitCount(test.one) << endl;
        
            return 0;
        }
        

        打印 15。

        【讨论】:

        • 不确定,但这可能取决于编译器,因为未定义填充位的设置方式。它们可能包含垃圾。因此,请注意如何使用它。此外,它是在运行时完成的。
        • @BЈовић:只有test.one 被传递给BitCount,它不应包含填充位。可以先将整个结构归零,但我认为没有必要。
        【解决方案5】:

        没有办法获得这些信息(除了自己阅读声明)。根据标准[C++11]expr.sizeof§1,在位域上调用sizeof 是非法的:

        sizeof 运算符不得应用于...指定位域的左值。

        【讨论】:

          【解决方案6】:

          由于填充,使用 sizeof 运算符无法查看位字段中的位数。

          唯一的方法是打开定义结构的标题,然后查找。

          【讨论】:

          • 好的,谢谢。但是如果我将类型更改为 char 并且位域成员的大小超过 8 位,那么我可以使用 sizeof 函数来确定四舍五入的字节数。编译器警告我在位域中超过 char 8 位的大小。这样做还可以吗?以后会不会出问题?
          • @domonica 首先,我真的希望你不要忽视编译器警告,因为这是马虎的人所做的事情。不确定会发生什么。它可能是未定义的或实现定义的行为。无论如何,我不会这样做
          • 谢谢大家,这里有一些非常有用的点。而且,我保证我不会忽略编译器警告..
          猜你喜欢
          • 1970-01-01
          • 2021-04-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多