如何设置、清除和切换一点?


当前回答

如果你在做很多无聊的事情,你可能会想使用口罩,这会让整个事情变得更快。以下函数速度非常快,而且仍然灵活(它们允许在任何大小的位图中进行位旋转)。

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等的小端处理器,它们在内存中正确地相互映射(小端处理器比大端处理器“更好”的主要原因,啊,我感觉到一场火焰大战即将到来…)。

其他回答

使用此项:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

检查任意类型变量中任意位置的位:

#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测试了它。可以制作类似的宏来设置和清除位。与其他解决方案相比,此解决方案的关键区别在于它适用于几乎任何类型的变量中的任何位置。

此程序用于将任何数据位从0更改为1或从1更改为0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

如果您想在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计算机上,这些都保证是原子的,并且是有用的以保持处理器之间的一致性。

从snip-c.zip的bitops.h:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

好的,让我们分析一下。。。

在所有这些问题中,您似乎遇到的常见表达是“(1L<<(posn))”。所有这些都是创建一个带有单个位的掩码并且可以与任何整数类型一起工作。“posn”参数指定把钻头放在你想要的位置。如果posn==0,则此表达式将评估为:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

如果posn==8,它将评估为:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

换句话说,它只需在指定的位置唯一棘手的部分是我们需要设置的BitClr()宏1字段中的单个0位。这是通过使用1与颚化符(~)运算符表示的表达式相同的补码。

一旦创建了掩码,它将按照您的建议应用于参数,使用按位和(&)、或(|)和xor(^)运算符。自从面具类型为long,宏将在char、short、int、,或长的。

底线是,这是一个通用的解决方案问题。当然,重写相当于每次使用显式掩码值的任何宏需要一个,但为什么要这样做?请记住,宏替换发生在因此生成的代码将反映以下事实:被编译器认为是常量,即使用起来同样有效每当你需要做的时候,通用宏都会“重新发明轮子”比特操作。

不服气?下面是一些测试代码-我使用了Watcom C,并进行了全面优化并且不使用_cdecl,因此生成的反汇编将与可能:

----[测试.C]----------------------------------------------------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

----[测试输出(拆卸)]-----------------------------------------------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

----[完成]-----------------------------------------------------------------