在回答与位标志相关的问题Bit Manipulation and Flags 时,我提供了一个使用unsigned char 数组的示例,这是一种用于处理非常大的位标志集的方法,我将在此发布。
此源示例提供以下内容:
- 为 bitflag 值定义的一组预处理器
- 一组用于操作位的预处理器宏
- 在数组上实现按位运算的几个函数
对此的一般方法如下:
- 为指定数组偏移量和位模式的标志创建一组定义
- 为适当大小的
unsigned char 数组创建 typedef
- 创建一组实现按位逻辑运算的函数
答案中的细节以及一些改进和更多说明
使用一组 C 预处理器定义来创建一组与数组一起使用的位标志。这些位标志定义了指定unsigned char 数组中的偏移量以及要操作的位。
此示例中的定义是 16 位值,其中高字节包含数组偏移量,低字节包含unsigned char 数组字节的位标志,其偏移量在高字节中。使用这种技术,您可以拥有最多 256 个元素、256 * 8 或 2,048 个位标志的数组,或者通过从 16 位定义到 32 位 long 您可以拥有更多。 (在 cmets 中,第 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
接下来,您将使用一组宏来设置和取消设置位以及 typedef,以使其更易于使用。不幸的是,在 C 中使用 typedef 并不能为您提供更好的编译器类型检查,但它确实使它更易于使用。这些宏不检查它们的参数,因此您可能会觉得使用常规函数更安全。
#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 数组。此更改将需要修改实用函数的接口,将BitSet 替换为unsigned char 指针和unsigned char 数组,其中定义了位标志变量。除了unsigned char 指针,您还需要指定数组的长度。
您也可以考虑采用类似于 Is concatenating arbitrary number of strings with nested function calls in C undefined behavior? 中对文本字符串所做的方法。