not - c++ operator




你如何設置,清除和切換一個位? (18)

如何在C / C ++中設置,清除和切換?


你如何設置,清除和切換一個位?

為了解決在嘗試形成掩模時常見的編碼缺陷:
1並不總是足夠寬

什麼問題number1什麼更廣泛?
x對於1 << x導致未定義行為(UB)的轉變可能太大了。即使x不是太大,~也可能無法翻轉足夠多的重要位。

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

確保1足夠寬:

代碼可以使用1ull或迂腐(uintmax_t)1,讓編譯器進行優化。

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

或演員 - 這使得編碼/審查/維護問題使演員保持正確和最新。

number |= (type_of_number)1 << x;

或者1通過強制進行至少與類型一樣寬的數學運算來輕輕地促進number

number |= (number*0 + 1) << x;

與大多數位操作,最好與工作無符號的類型,而不是簽署


設置一下

使用按位OR運算符( | )設置一個位。

number |= 1UL << n;

這將設置第n numbern應該為零,如果要設置第1位,依此類推,最多為n-1 ,如果要設置第n位。

如果number寬於unsigned long ,則使用1ULL ; 促進1UL << n直到評估1UL << n之後才會發生,其中未定義的行為偏移超過長度的寬度。 這同樣適用於所有其他示例。

清理一下

使用按位AND運算符( & )清除一位。

number &= ~(1UL << n);

這將清除第nnumber 。 必須使用按位NOT運算符( ~ )反轉位串,然後運行AND。

切換了一下

XOR運算符( ^ )可用於切換位。

number ^= 1UL << n;

這將切換數字的第n位。

檢查一下

你沒有要求這個,但我不妨補充一下。

要檢查一下,將數字n向右移動,然後按位移動它:

bit = (number >> n) & 1U;

這會將第n位數的number放入變量bit

將第n位更改為x

將第n位設置為10可以通過以下2的補碼C ++實現來實現:

number ^= (-x ^ number) & (1UL << n);

如果x1 ,則設置位n ,如果x0 ,則清除位n 。 如果x有其他值,你會得到垃圾。 x = !!x將其布爾化為0或1。

為了使其獨立於2的補碼否定行為(其中-1設置了所有位,與1的補碼或符號/幅度C ++實現不同),使用無符號否定。

number ^= (-(unsigned long)x ^ number) & (1UL << n);

要么

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

使用無符號類型進行便攜式位操作通常是個好主意。

一般來說,通常不要復制/粘貼代碼也是一個好主意,因此很多人使用預處理器宏(如社區維基回答更進一步 )或某種封裝。


檢查任意類型變量中任意位置的位:

#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測試了它。 可以為設置和清除位創建類似的宏。 與其他許多解決方案相比,此解決方案的主要區別在於它適用於幾乎任何類型的變量中的任何位置。


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使設置和清除bool變得容易。

要找到不可打印的字符......

//  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個月後證明我們從中獲取數據的主機系統實際上是故障。位陣列的簡單性使得它們的正確性非常高 - 例如,與搜索方法相比。


位域方法在嵌入式領域具有其他優勢。 您可以定義一個直接映射到特定硬件寄存器中的位的結構。

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

您需要了解位打包順序 - 我認為它首先是MSB,但這可能與實現有關。 另外,驗證編譯器處理程序如何跨越字節邊界。

然後,您可以像以前一樣讀取,寫入,測試各個值。


使用按位運算符: & |

要設置000b最後一位:

foo = foo | 001b

檢查foo最後一位:

if ( foo & 001b ) ....

要清除foo最後一位:

foo = foo & 110b

為清楚起見,我使用了XXXb 。 您可能正在使用HEX表示,具體取決於您打包位的數據結構。


另一種選擇是使用位字段:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

定義一個3位字段(實際上,它是三個1位字符)。 位操作現在變得有點(哈哈)更簡單:

設置或清除一下:

mybits.b = 1;
mybits.c = 0;

要切換一下:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

檢查一下:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

這僅適用於固定大小的位字段。 否則你必須採用之前帖子中描述的比特技巧。


在C語言中嘗試使用其中一個函數來改變n位:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

要么

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

要么

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

對於初學者,我想用一個例子來解釋一下:

例:

value is 0x55;
bitnum : 3rd.

使用&運算符檢查位:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

切換或翻轉:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| operator:設置位

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

我使用頭文件中定義的宏來處理位集和清除:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

有時候使用enum命名這些位:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

然後使用名稱 。 即寫

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

設置,清除和測試。 這樣,您可以隱藏其餘代碼中的幻數。

除此之外,我贊同傑里米的解決方案。


用這個:

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

    return num;
}

該程序將任何數據位從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);
        }
    }
}

變量使用

int value, pos;

value - 數據
位置 - 我們有興趣設置,清除或切換
的位的位置設置一個位

value = value | 1 << pos;

清楚一點

value = value & ~(1 << pos); 

切換一下

value = value ^ 1 << pos;

這是我最喜歡的位算術宏,適用於從unsigned charsize_t任何類型的無符號整數數組(這是應該高效處理的最大類型):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

設置一下:

BITOP(array, bit, |=);

要清楚一點:

BITOP(array, bit, &=~);

要切換一下:

BITOP(array, bit, ^=);

測試一下:

if (BITOP(array, bit, &)) ...

等等


使用here定義的運算符之一。

要設置一個位,用來int x = x | 0x?;在那裡?是二進制形式的比特位置。


擴大bitset答案:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

int set_nth_bit(int num, int n){

    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){

    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){

    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){

    return num & (1 << n);
}




bitwise-operators