単一ビットをどのように設定、クリア、切り替えますか?
-
09-06-2019 - |
質問
C/C++ でビットを設定、クリア、切り替えするにはどうすればよいでしょうか?
解決
ちょっとした設定
ビットごとの OR 演算子 (|
)ビットを設定します。
number |= 1UL << n;
これにより、 n
のビット目 number
. n
を設定する場合は、ゼロにする必要があります。 1
st bit などまで n-1
, を設定したい場合は、 n
ビット目。
使用 1ULL
もし number
より広いです unsigned long
;の推進 1UL << n
評価するまでは起こりません 1UL << n
ここで、幅を超えてシフトするのは未定義の動作です。 long
. 。同じことが残りのすべての例に当てはまります。
少しクリア
ビットごとの AND 演算子 (&
)少しクリアします。
number &= ~(1UL << n);
それはクリアします n
のビット目 number
. 。ビット単位の NOT 演算子 (~
)、その後 AND をとります。
少し切り替えます
XOR 演算子 (^
) を使用して少し切り替えることができます。
number ^= 1UL << n;
これにより、 n
のビット目 number
.
ちょっと確認中
あなたはこれを求めていませんでしたが、追加したほうがよいでしょう。
ビットを確認するには、数値 n を右にシフトし、ビット単位で AND 演算します。
bit = (number >> n) & 1U;
それはの値を置きます n
のビット目 number
変数に入れる bit
.
変更する nビット目から バツ
の設定 n
番目のビットをどちらかに 1
または 0
これは、2 の補数 C++ 実装で次のように実現できます。
number ^= (-x ^ number) & (1UL << n);
少し n
の場合に設定されます x
は 1
, 、次の場合はクリアされます x
は 0
. 。もし 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);
一般に、移植可能なビット操作には符号なし型を使用することをお勧めします。
または
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
をクリアします n
番目のビットと (x << n)
を設定します n
ビット目から x
.
また、一般にコードをコピー/ペーストしないことも一般的に良い考えであり、非常に多くの人がプリプロセッサ マクロを使用しています ( コミュニティ Wiki の回答はさらに下にあります) またはある種のカプセル化。
他のヒント
標準 C++ ライブラリの使用: std::bitset<N>
.
または ブースト バージョン: 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
/* 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) {...}
設定、クリア、テストします。こうすることで、コードの残りの部分からマジックナンバーを隠すことができます。
それ以外では、私はジェレミーの解決策を支持します。
から 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)))
OK、物事を分析しましょう...
これらすべてで問題があると思われる一般的な表現は、「(1L << (posn))」です。これはすべて、1つのビットをオンにしたマスクを作成し、整数タイプで動作することです。「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のフィールドを作成するだけです。唯一のトリッキーな部分は、BitClr()マクロにあり、1のフィールドにシングル0ビットを設定する必要があります。これは、Tilde(〜)演算子によって示されるのと同じ式の1の補数を使用することによって達成されます。
マスクが作成されると、BitWiseと(&)、または(|)、およびXOR(^)演算子を使用して、提案したように引数に適用されます。マスクはタイプが長いため、マクロはChar、Short's、Int、またはLongでも同様に機能します。
一番下の行は、これがクラスの問題の一般的な解決策であるということです。もちろん、これらのマクロのいずれかに相当するものを明示的なマスク値を必要とするたびに書き換えることは可能であり、さらには適切ですが、なぜそれを行うのですか?マクロ置換はプリプロセッサで発生するため、生成されたコードは、コンパイラによって値が一定と見なされるという事実を反映していることを忘れないでください。ビット操作を行う必要があるたびに「ホイールを再発明する」のと同じように、一般化されたマクロを使用することも効率的です。
納得いかない?ここにいくつかのテストコードがあります - 私は完全な最適化を備えた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;
}
----[ 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
----[終了]------------------------------------------- ------------------------
ビット単位の演算子を使用します。 &
|
最後のビットを設定するには 000b
:
foo = foo | 001b
最後のビットをチェックインするには foo
:
if ( foo & 001b ) ....
最後のビットをクリアするには foo
:
foo = foo & 110b
私が使用した XXXb
明確にするために。ビットをパックするデータ構造に応じて、おそらく 16 進数表現を使用することになります。
初心者のために、例を挙げてもう少し説明したいと思います。
例:
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, &)) ...
等
ビットフィールドのアプローチには、組み込み分野では他の利点もあります。特定のハードウェア レジスタのビットに直接マップする構造体を定義できます。
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 ファーストだと思いますが、これは実装に依存する可能性があります。また、コンパイラーがバイト境界を越えるフィールドをどのように処理するかを確認してください。
その後、以前と同様に、個々の値の読み取り、書き込み、テストを行うことができます。
これには「組み込み」というタグが付いているので、マイクロコントローラーを使用していると仮定します。上記の提案はすべて有効であり、機能します (読み取り、変更、書き込み、共用体、構造体など)。
しかし、オシロスコープベースのデバッグ中に、マイクロの PORTnSET / PORTnCLEAR レジスタに値を直接書き込む場合と比較して、これらの方法では CPU サイクルにかなりのオーバーヘッドがあることに気づき、驚きました。これは、タイトなループや高負荷の場合に大きな違いを生みます。 -周波数ISRのトグルピン。
馴染みのない人のために:この例では、マイクロには出力ピンを反映する汎用ピン状態レジスタ PORTn があるため、PORTn |= BIT_TO_SET を実行すると、そのレジスタへの読み取り、変更、書き込みが行われます。ただし、PORTnSET / PORTnCLEAR レジスタは、「このビットを 1 にしてください」(SET) または「このビットを 0 にしてください」(CLEAR) を意味する「1」と、「ピンをそのままにしておく」ことを意味する「0」を受け取ります。したがって、ビットをセットするかクリアするかに応じて、最終的に 2 つのポート アドレスが必要になります (必ずしも便利とは限りません)。 多くの 反応が速くなり、アセンブルされたコードが小さくなります。
より一般的な、任意のサイズのビットマップの場合:
#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)))
任意の型の変数内の任意の位置にあるビットをチェックします。
#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);
}
}
}
細かい操作をたくさん行う場合は、全体を迅速化するマスクを使用するとよいでしょう。以下の関数は非常に高速でありながら柔軟性があります (任意のサイズのビット マップでビット操作が可能です)。
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;
}
を拡張すると、 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;
}
このすべての操作を C プログラミングで実行したい場合は、 Linuxカーネル その場合は、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コンピューターでも、プロセッサ全体で一貫性を保つのに役立ちます。
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;
}
}
このコードには「特別な」点は何もないことに注意してください。これは整数と少し似ていますが、技術的にはそうなのです。2 つの値、または 2 つの値のみを保持できる 1 ビット整数。
私はかつてこのアプローチを使用して、重複するローン レコードを検索しました。ここで、loan_number は ISAM キーであり、6 桁のローン番号をビット配列へのインデックスとして使用しました。驚くほど速く、8 か月後には、データを取得していたメインフレーム システムが実際に故障していることが証明されました。ビット配列は単純であるため、たとえば検索アプローチと比較して、その正確さに対する信頼性が非常に高くなります。
定義されている演算子のいずれかを使用します ここ.
ビットを設定するには、使用します int x = x | 0x?;
どこ ?
はバイナリ形式のビット位置です。
私が使用するいくつかのマクロを次に示します。
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)
使用される変数
int value, pos;
値 - データ
pos - 設定、クリア、または切り替えの対象となるビットの位置。
ビットを設定します:
value = value | 1 << pos;
少し明確にしてください:
value = value & ~(1 << pos);
少し切り替えます:
value = value ^ 1 << pos;
単一ビットをどのように設定、クリア、切り替えますか?
マスクを形成しようとするときによくあるコーディング上の落とし穴に対処するには、次のようにします。
1
常に十分な幅があるわけではない
いつどんな問題が起きるのか number
より幅広のタイプです 1
?
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;
ほとんどのビット操作と同様に、使用するのが最適です。 署名されていない ではなくタイプ 署名済み もの
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);
}
C++11 テンプレート バージョン (ヘッダーに配置):
namespace bit {
template <typename T1, typename T2> inline void set (T1 &variable, T2 bit) {variable |= ((T1)1 << bit);}
template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^= ((T1)1 << bit);}
template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}
namespace bitmask {
template <typename T1, typename T2> inline void set (T1 &variable, T2 bits) {variable |= bits;}
template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}
n ビットを変更するには、C 言語で次の関数のいずれかを試してください。
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;
}