4

假设我有一个枚举,其位标志选项大于标准数据类型中的位数:

enum flag_t {
FLAG_1 = 0x1,
FLAG_2 = 0x2,
...
FLAG_130 = 0x400000000000000000000000000000000,
};

由于几个原因,这是不可能的。枚举的最大大小为 128 位(在我的实验系统上的 C/gcc 中),单个变量的最大大小也为 128 位等。

在 C 中,您不能对数组执行按位运算,但在 C++ 中,我想您可以重载按位运算符来使用循环来完成这项工作。

除了手动记住哪些标志在哪里可以为大数工作之外,C 中还有其他方法吗?

4

3 回答 3

5

这正是位域的用途。

在 C 中,可以定义以下数据布局:

struct flag_t 
{
     unsigned int flag1 : 1;
     unsigned int flag2 : 1;
     unsigned int flag3 : 1;
(...)
     unsigned int flag130 : 1;
(...)
     unsigned int flag1204 : 1;   // for fun
};

在此示例中,所有标志仅占用一位。一个明显的优势是无限数量的标志。另一个很大的优势是您不再局限于单位标志,您可以在中间合并一些多值标志。

但最重要的是,就单元操作而言,测试和归因会有所不同,并且可能会简化:您不再需要进行任何屏蔽,只需通过命名直接访问标志即可。顺便说一句,利用这个机会给这些标志更全面的名字:)

于 2013-05-07T18:00:45.300 回答
2

在这个与 bitflags、 Bit Manipulation 和 Flags相关的问题的回答中,我提供了一个使用数组的示例,该unsigned char数组是一种处理非常大的 bitflag 集的方法,我将在此发布。

此源示例提供以下内容:

  • 一组预处理器为 bitflag 值定义
  • 一组预处理器宏来操作位
  • 几个函数来实现对数组的按位运算

对此的一般方法如下:

  • 为指定数组偏移量和位模式的标志创建一组定义
  • unsigned char为适当大小的数组创建 typedef
  • 创建一组实现按位逻辑运算的函数

答案中的细节有一些改进和更多的说明

使用一组 C 预处理器定义来创建一组要与数组一起使用的位标志。这些位标志定义了指定unsigned char数组内的偏移量以及要操作的位。

此示例中的定义是 16 位值,其中高字节包含数组偏移量,低字节包含偏移量在高字节中的数组字节的位标志unsigned char。使用这种技术,您可以拥有多达 256 个元素、256 * 8 或 2,048 个位标志的数组,或者通过从 16 位定义到 32 位long,您可以拥有更多。(在下面的注释中,位 0 表示字节的最低有效位,位 7 表示字节的最高有效位)。

#define ITEM_FLG_01  0x0001     // array offset 0, bit 0
#define ITEM_FLG_02  0x0002     // array offset 0, bit 1
#define ITEM_FLG_03  0x0101     // array offset 1, bit 0
#define ITEM_FLG_04  0x0102     // array offset 1, bit 1
#define ITEM_FLG_05  0x0201     // array offset 2, bit 0
#define ITEM_FLG_06  0x0202     // array offset 2, bit 1
#define ITEM_FLG_07  0x0301     // array offset 3, bit 0
#define ITEM_FLG_08  0x0302     // array offset 3, bit 1
#define ITEM_FLG_10  0x0908     // array offset 9, bit 7

接下来,您有一组宏来设置和取消设置位以及 atypedef以使其更易于使用。不幸的是typedef,在 C 中使用 a 并不能为您提供更好的编译器类型检查,但它确实使它更易于使用。这些宏不检查它们的参数,因此您可能会觉得使用常规函数更安全。

#define SET_BIT(p,b) (*((p) + (((b) >> 8) & 0xf)) |= (b) & 0xf)
#define TOG_BIT(p,b) (*((p) + (((b) >> 8) & 0xf)) ^= (b) & 0xf)
#define CLR_BIT(p,b) (*((p) + (((b) >> 8) & 0xf)) &= ~ ((b) & 0xf))
#define TST_BIT(p,b) (*((p) + (((b) >> 8) & 0xf)) & ((b) & 0xf))

typedef unsigned char BitSet[10];

使用这个基本框架的一个例子如下。

BitSet uchR = { 0 };
int  bValue;

SET_BIT(uchR, ITEM_FLG_01);
bValue = TST_BIT(uchR, ITEM_FLG_01);
SET_BIT(uchR, ITEM_FLG_03);
TOG_BIT(uchR, ITEM_FLG_03);
TOG_BIT(uchR, ITEM_FLG_04);
CLR_BIT(uchR, ITEM_FLG_05);
CLR_BIT(uchR, ITEM_FLG_01);

接下来,您可以引入一组实用函数来执行我们想要支持的一些按位运算。这些按位运算类似于内置的 C 运算符,例如按位或 ( |) 或按位与 ( &)。这些函数使用内置的 C 运算符对所有数组元素执行指定的运算符。

实用功能的这些特定示例修改了所提供的一组位标志。但是,如果这是一个问题,您可以修改函数以接受三个参数,一个用于操作的结果,另外两个用于在操作中使用的两组位标志。

void AndBits(BitSet s1, const BitSet s2)
{
    size_t nLen = sizeof(BitSet);

    for (; nLen > 0; nLen--) {
        *s1++ &= *s2++;
    }
}

void OrBits(BitSet s1, const BitSet s2)
{
    size_t nLen = sizeof(BitSet);

    for (; nLen > 0; nLen--) {
        *s1++ |= *s2++;
    }
}

void XorBits(BitSet s1, const BitSet s2)
{
    size_t nLen = sizeof(BitSet);

    for (; nLen > 0; nLen--) {
        *s1++ ^= *s2++;
    }
}

如果您使用这种方法需要多个位标志类型的大小,那么最灵活的方法可以消除typedef并只使用unsigned char各种大小的直接数组。此更改将需要修改实用程序函数的接口,替换为定义位标志变量BitSetunsigned char指针和unsigned char数组。除了unsigned char指针,您还需要指定数组的长度。

您还可以考虑一种类似于在 C 未定义行为中将任意数量的字符串与嵌套函数调用连接起来的文本字符串所做的方法?.

于 2018-03-21T12:25:25.007 回答
1

与其尝试将大得离谱的数字分配给枚举以便您可以拥有数百位宽的位域,不如让编译器为您的标志名称分配一个正常的从零开始的数字序列,并使用数组模拟一个宽位域无符号字符。unsigned char bits[128]您可以使用、 和 writeget_flag()和accessor 函数拥有一个 1024 位位域,set_flag()以屏蔽涉及的少量额外工作。

但是,更好的建议是再次查看您的设计,并问自己“为什么我需要超过一百个不同的标志?”。在我看来,您真正需要的是重新设计。

于 2013-05-07T16:44:00.870 回答