如何设置、清除和切换一点?
当前回答
Visual C 2010,也许还有许多其他编译器,都直接支持内置的布尔运算。一个位有两个可能的值,就像一个布尔值一样,所以我们可以使用布尔值,即使在这种表示中,布尔值占用的内存空间比一个位多。这是有效的,即使sizeof()运算符也能正常工作。
bool IsGph[256], IsNotGph[256];
// Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
因此,对于您的问题,IsGph[i]=1或IsGph[i]=0使得设置和清除布尔值变得容易。
要查找不可打印的字符:
// Initialize boolean array to detect UN-printable characters,
// then call function to toggle required bits true, while initializing a 2nd
// boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++) {
if(IsGph[i]) {
IsNotGph[i] = 0;
} else {
IsNotGph[i] = 1;
}
}
注意,这段代码没有什么“特殊”之处。它有点像一个整数-从技术上讲,它是一个1位整数,可以容纳2个值,并且只能容纳2个。
我曾经使用这种方法查找重复的贷款记录,其中loan_number是ISAM密钥,使用6位贷款编号作为位数组的索引。8个月后,我们以惊人的速度证明了我们从中获取数据的主机系统实际上发生了故障。比特阵列的简单性使人们对其正确性的信心非常高——例如,与搜索方法相比。
其他回答
使用标准C++库:std::bitset<N>。
或者Boost版本:Boost::dynamic_bitset。
没有必要自己动手:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<5> x;
x[1] = 1;
x[2] = 0;
// Note x[0-4] valid
std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010
与标准库编译时大小的位集相比,Boost版本允许运行时大小的比特集。
有时值得使用枚举来命名位:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
然后稍后使用这些名称,即写
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
设置、清除和测试。通过这种方式,您可以对代码的其余部分隐藏神奇的数字。
除此之外,我支持佩奇·鲁滕的解决方案。
如果你在做很多无聊的事情,你可能会想使用口罩,这会让整个事情变得更快。以下函数速度非常快,而且仍然灵活(它们允许在任何大小的位图中进行位旋转)。
const unsigned char TQuickByteMask[8] =
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80,
};
/** Set bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TSetBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] |= TQuickByteMask[n]; // Set bit.
}
/** Reset bit in any sized mask.
*
* @return None
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TResetBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] &= (~TQuickByteMask[n]); // Reset bit.
}
/** Toggle bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TToggleBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] ^= TQuickByteMask[n]; // Toggle bit.
}
/** Checks specified bit.
*
* @return 1 if bit set else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
short TIsBitSet( short bit, const unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
// Test bit (logigal AND).
if (bitmap[x] & TQuickByteMask[n])
return 1;
return 0;
}
/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
short TIsBitReset( short bit, const unsigned char *bitmap)
{
return TIsBitSet(bit, bitmap) ^ 1;
}
/** Count number of bits set in a bitmap.
*
* @return Number of bits set.
*
* @param bitmap - Pointer to bitmap.
* @param size - Bitmap size (in bits).
*
* @note Not very efficient in terms of execution speed. If you are doing
* some computationally intense stuff you may need a more complex
* implementation which would be faster (especially for big bitmaps).
* See (http://graphics.stanford.edu/~seander/bithacks.html).
*/
int TCountBits( const unsigned char *bitmap, int size)
{
int i, count = 0;
for (i=0; i<size; i++)
if (TIsBitSet(i, bitmap))
count++;
return count;
}
注意,要在16位整数中设置位“n”,请执行以下操作:
TSetBit( n, &my_int);
这取决于你确保比特数在你通过的位图范围内。请注意,对于字节、字、dword、qword等的小端处理器,它们在内存中正确地相互映射(小端处理器比大端处理器“更好”的主要原因,啊,我感觉到一场火焰大战即将到来…)。
检查任意类型变量中任意位置的位:
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
示例用法:
int main(void)
{
unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
for (int ix = 0; ix < 64; ++ix)
printf("bit %d is %d\n", ix, bit_test(arr, ix));
return 0;
}
笔记:这是设计为快速(考虑到其灵活性)和非分支。当编译Sun Studio 8时,它会产生高效的SPARC机器代码;我还在amd64上使用MSVC++2008测试了它。可以制作类似的宏来设置和清除位。与其他解决方案相比,此解决方案的关键区别在于它适用于几乎任何类型的变量中的任何位置。
如果您想在Linux内核中使用C编程执行所有操作,那么我建议使用Linux内核的标准API。
看见https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html
set_bit Atomically set a bit in memory
clear_bit Clears a bit in memory
change_bit Toggle a bit in memory
test_and_set_bit Set a bit and return its old value
test_and_clear_bit Clear a bit and return its old value
test_and_change_bit Change a bit and return its old value
test_bit Determine whether a bit is set
注意:这里整个操作在一个步骤中完成。因此,即使在SMP计算机上,这些都保证是原子的,并且是有用的以保持处理器之间的一致性。