位域成员的大小是多少?

19

有没有人知道如何提取位域成员的大小?下面的代码自然地给出了一个整数的大小,但是我该如何找出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;
}

1
运行时解决方案:将整个结构的值设置为零,然后使用“~”反转字段,最后计算设置为“1”的位数。http://social.msdn.microsoft.com/Forums/en-US/7e4f01b6-2e93-4acc-ac6a-b994702e7b66/finding-size-of-bitfield - Alex F
6个回答

7

《C++标准草案》在第5.3.3节的Sizeof段落1中规定,sizeof不能应用于位域。如果您控制源代码,则使用枚举会更简单、更整洁。

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 ;
};

如果您无法控制源代码,可以使用位操作技巧来获取位域的大小,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

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

4

使用 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
非常好的解决方案,谢谢。使用gcc编译时,由于尝试将~0分配给较窄的位域,会出现溢出警告。warning: conversion from 'unsigned int' to 'unsigned char:4' changes value from '4294967295' to '15' [-Woverflow]使用clang似乎没问题。相反,设置t.f = 1,然后左移直到位从左侧掉落似乎效果很好。t.f = 1; while (t.f != 0) { t.f <<= 1; ...for (t.f = 1; t.f !=0; t.f <<= 1, bitcount ++); - Jack Culhane
这很酷,Jack的评论解决了有符号字段的计算问题,而无需确定T的无符号版本(在位域中可能不可能)。 - Alexis Wilke

2

由于填充的存在,使用sizeof运算符无法看到位域中的位数。

唯一的方法是打开定义结构的头文件并查找它。


好的,谢谢。但是如果我将类型更改为char并超过位域成员的8位大小,则可以使用sizeof函数确定向上舍入的字节数。编译器会警告我超出了char 8位的位域大小。这样做还可以吗?以后会有问题吗? - domonica
@domonica 首先,我真的希望你不要忽略编译器警告,因为这是懒惰的人做的事情。不确定会发生什么。它可能是未定义或实现定义的行为。无论如何,我不会这样做。 - BЈовић
谢谢大家,这里有一些非常有用的建议。我保证不会忽略编译器警告。 - domonica

2

除了自己阅读声明外,没有其他方法可以获取此信息。根据标准 [C++11]expr.sizeof§1,在位域上调用sizeof是非法的:

不得对指定位域的lvalue应用sizeof运算符。


-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上尝试。

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


您正在对位域使用sizeof,根据标准是不允许的。 - ABaumstumpf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接