どのように設定、クリア、トグルしますか?
27 に答える
ちょっと設定
ビットごとの OR 演算子 ( |
) を使用して、ビットを設定します。
number |= 1UL << n;
n
これにより、 のth ビットが設定されnumber
ます。st ビットn
を設定したい場合は 0 にし、 th ビットを設定したい場合は まで続けます。1
n-1
n
より広い1ULL
場合に使用します。の昇格は、未定義の動作が a の幅を超えてシフトする場所を評価した後まで発生しません。同じことが残りのすべての例に当てはまります。number
unsigned long
1UL << n
1UL << n
long
少しクリア
ビットごとの AND 演算子 ( &
) を使用して、ビットをクリアします。
number &= ~(1UL << n);
n
これにより、 のth ビットがクリアされnumber
ます。ビットごとの NOT 演算子 ( ) を使用してビット文字列を反転し、~
AND する必要があります。
少しトグル
XOR 演算子 ( ^
) を使用してビットをトグルできます。
number ^= 1UL << n;
n
これにより、 のth ビットがトグルされますnumber
。
ちょっと確認中
あなたはこれを求めていませんでしたが、追加したほうがよいでしょう。
ビットをチェックするには、数値 n を右にシフトし、ビットごとに AND します。
bit = (number >> n) & 1U;
これにより、 のn
th ビットの値がnumber
variableに入れられますbit
。
n番目のビットをxに変更する
2 の補数の C++ 実装では、次のようにn
th ビットを1
orに設定します。0
number ^= (-x ^ number) & (1UL << n);
ビットはのn
場合にセットされ、の場合にクリアされます。他の値がある場合、ゴミが発生します。 0 または 1 にブール値化します。x
1
x
0
x
x = !!x
これを 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);
一般に、移植可能なビット操作には符号なし型を使用することをお勧めします。
また
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
は th ビットをクリアし、n
th ビットを(x << n)
に設定n
しx
ます。
また、一般的にコードをコピー/貼り付けしないことも一般的に良い考えであり、非常に多くの人がプリプロセッサ マクロ (コミュニティ wiki の回答のように) や何らかのカプセル化を使用しています。
標準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バージョンでは、標準ライブラリのコンパイル時サイズのビットセットと比較して、ランタイムサイズのビットセットを使用できます。
もう 1 つのオプションは、ビット フィールドを使用することです。
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
3 ビット フィールドを定義します (実際には、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
これは、固定サイズのビット フィールドでのみ機能します。それ以外の場合は、以前の投稿で説明されているビットをいじる手法に頼る必要があります。
ヘッダーファイルで定義されたマクロを使用して、ビットセットとクリアを処理します。
/* 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
#define BITMASK_SET(x, mask) ((x) |= (mask))
#define BITMASK_CLEAR(x, mask) ((x) &= (~(mask)))
#define BITMASK_FLIP(x, mask) ((x) ^= (mask))
#define BITMASK_CHECK_ALL(x, mask) (!(~(x) & (mask)))
#define BITMASK_CHECK_ANY(x, mask) ((x) & (mask))
を使用してビットenum
に名前を付ける価値がある場合があります。
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
その名前を後で使用します。すなわち書く
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
設定、クリア、テスト。このようにして、マジック ナンバーをコードの残りの部分から隠します。
それ以外は、ジェレミーのソリューションを支持します。
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.
つまり、指定された位置に 1 を持つ 0 のフィールドを作成するだけです。唯一のトリッキーな部分は、1 のフィールドに単一の 0 ビットを設定する必要がある BitClr() マクロにあります。これは、チルダ (~) 演算子で示される同じ式の 1 の補数を使用して実現されます。
マスクが作成されると、ビットごとの and (&)、または (|)、および xor (^) 演算子を使用して、提案どおりに引数に適用されます。マスクは long 型であるため、マクロは char、short、int、または long に対しても同様に機能します。
肝心なのは、これが一連の問題全体に対する一般的な解決策であるということです。もちろん、必要に応じて明示的なマスク値を使用して、これらのマクロのいずれかと同等のものを書き換えることは可能であり、適切なことですらありますが、なぜそれを行うのでしょうか? マクロ置換はプリプロセッサで行われるため、生成されたコードは、値がコンパイラによって一定であると見なされるという事実を反映することを覚えておいてください。ビット操作を行います。
納得できない?以下にいくつかのテスト コードを示します。Watcom C を完全に最適化して _cdecl を使用せずに使用したため、結果の逆アセンブリは可能な限りクリーンになります。
--[ TEST.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;
}
----[ TEST.OUT (分解) ]----------------------------------------------------- ----------
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
----【仕上げ】------------------------------------------- ----------------------
初心者のために、例を挙げてもう少し説明したいと思います。
例:
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)
|
演算子: ビットを設定します
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
これは私のお気に入りのビット算術マクロです。これは (効率的に作業できる最大の型です) までの任意の型の符号なし整数配列に対して機能しますunsigned char
。size_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, &)) ...
等
これは「組み込み」とタグ付けされているので、マイクロコントローラーを使用していると仮定します。上記の提案はすべて有効で機能します (読み取り-変更-書き込み、共用体、構造体など)。
しかし、オシロスコープベースのデバッグを行っている間に、マイクロの PORTnSET / PORTnCLEAR レジスタに値を直接書き込む場合と比較して、これらの方法が CPU サイクルにかなりのオーバーヘッドがあることに驚きました。 -周波数 ISR のトグル ピン。
なじみのない人のために: 私の例では、マイクロには出力ピンを反映する一般的なピン状態レジスタ PORTn があるため、PORTn |= BIT_TO_SET を実行すると、そのレジスタへの読み取り-変更-書き込みが行われます。ただし、PORTnSET / PORTnCLEAR レジスタは、「このビットを 1 にしてください」(SET) または「このビットを 0 にしてください」(CLEAR) を意味する「1」と、「ピンをそのままにしておく」を意味する「0」を取ります。そのため、ビットを設定するかクリアするかに応じて 2 つのポート アドレスが作成されますが (常に便利というわけではありません)、はるかに高速な反応と小さなアセンブル コードが得られます。
ビットフィールドアプローチには、組み込み分野で他の利点があります。特定のハードウェアレジスタのビットに直接マップする構造体を定義できます。
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だと思いますが、これは実装に依存する可能性があります。また、コンパイラがバイト境界を越えるフィールドをどのように処理するかを確認します。
その後、前と同じように、個々の値の読み取り、書き込み、テストを行うことができます。
任意の型の変数の任意の場所にあるビットを確認します。
#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を使用してテストしました。ビットを設定およびクリアするための同様のマクロを作成することが可能です。このソリューションと他の多くのソリューションとの主な違いは、ほぼすべてのタイプの変数の任意の場所で機能することです。
より一般的には、任意のサイズのビットマップの場合:
#define BITS 8
#define BIT_SET( p, n) (p[(n)/BITS] |= (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] & (0x80>>((n)%BITS)))
num = 55
ビットごとの操作 (セット、取得、クリア、トグル) を実行するために最初に Integer をいくつか想定してみましょう。
n = 4
ビット演算を実行するための 0 ベースのビット位置。
少し手に入れる方法は?
nth
num 右シフトのビットを取得するにはnum
、n
回。次に、ビット単位の AND&
を 1 で実行します。
bit = (num >> n) & 1;
使い方?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
少し設定するには?
- 数値の特定のビットを設定します。左シフト1
n
回。次に、 でビットごとの OR|
演算を実行しnum
ます。
num |= (1 << n); // Equivalent to; num = (1 << n) | num;
使い方?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
少しクリアするには?
- 左シフト 1
n
回、つまり1 << n
. - 上記の結果でビット単位の補数を実行します。n 番目のビットが設定解除され、残りのビットが設定されるようになり
~ (1 << n)
ます。 &
最後に、上記の結果と でビットごとの AND 演算を実行しnum
ます。上記の 3 つの手順をまとめて次のように書くことができますnum & (~ (1 << n))
。
num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
使い方?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
少し切り替える方法は?
ビットを切り替えるには、ビットごとの XOR^
演算子を使用します。ビット単位の XOR 演算子は、両方のオペランドの対応するビットが異なる場合は 1 に評価され、それ以外の場合は 0 に評価されます。
つまり、ビットをトグルするには、トグルするビットと 1 で XOR 演算を実行する必要があります。
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
使い方?
- トグルするビットが 0 の場合、
0 ^ 1 => 1
. - トグルするビットが 1 の場合、
1 ^ 1 => 0
.
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
^ 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
推奨読書 -ビット演算の演習
このプログラムは、任意のデータ ビットを 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);
}
}
}
少しいじる作業が多い場合は、マスクを使用して全体を高速化することをお勧めします。次の関数は非常に高速で、柔軟性があります (任意のサイズのビット マップでビットをいじることができます)。
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 などがメモリ内で互いに正しくマッピングされることに注意してください (リトル エンディアン プロセッサがビッグ エンディアン プロセッサよりも「優れている」主な理由は、ああ、フレーム戦争が近づいているように感じますの上...)。
このすべての操作を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
注: ここでは、操作全体が 1 つのステップで行われます。したがって、これらはすべてSMP コンピューター上でもアトミックであることが保証されており、プロセッサー間の一貫性を維持するのに役立ちます。
これを使って:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Visual C 2010、およびおそらく他の多くのコンパイラには、ブール演算の直接サポートが組み込まれています。ビットには、ブール値と同じように 2 つの可能な値があるため、代わりにブール値を使用できます。この表現の記憶。これは機能し、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;
}
}
このコードには「特別な」ものは何もないことに注意してください。整数のように少し扱います-技術的にはそうです。2 つの値を保持できる 1 ビット整数、および 2 つの値のみ。
私は以前、このアプローチを使用して、ビット配列へのインデックスとして 6 桁のローン番号を使用して、loan_number が ISAM キーである重複したローン レコードを見つけました。猛烈な速さで、8 か月後に、データを取得していたメインフレーム システムが実際に誤動作していることが判明しました。ビット配列の単純さは、たとえば検索アプローチと比較して、それらの正確性の信頼性を非常に高くします。
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);
}
ここに私が使用するいくつかのマクロがあります:
SET_FLAG(Status, Flag) ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag) ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit) (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask) TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask) TEST_FLAGS(t,ulMask,0)