C プログラミング言語では、ビット演算子を使用してビットレベルで演算を実行できます。
ビット単位の演算は、ビット単位の演算子の論理的対応物である AND、OR、NOT 演算子を特徴付けるバイト レベルの演算とは対照的です。バイトレベルの演算子は、個々のビットに対して実行するのではなく、一度に 8 ビットの文字列 (バイトと呼ばれる) に対して実行します。これは、バイトが通常、アドレス指定可能なメモリ (つまり、一意のメモリ アドレスを持つデータ) の最小単位であるためです。
これはビット演算子にも当てはまります。つまり、ビット演算子は一度に 1 ビットのみを操作しますが、入力として 1 バイト未満の値を受け入れることはできません。
これらの演算子はすべて、 C++および多くのC ファミリ言語 でも使用できます。
ビット演算子
C言語ではビット操作のための演算子が6つ用意されている。[1]
ビットAND&
ビット AND 演算子は、単一のアンパサンドです: &。これは、オペランドの真理値ではなく、オペランドのビットに対して演算を行う AND の単なる表現です。ビット バイナリ AND は、バイナリ形式の数値の各位置のビットの論理積(上の表を参照) を実行します。
たとえば、バイト (char 型) を操作する場合:
11001000
& 10111000
--------
= 10001000
最初の数値の最上位ビットは1で、2番目の数値の最上位ビットも1なので、結果の最上位ビットは1になります。2番目に重要なビットでは、2番目の数値のビットは0なので、結果は0になります。 [2]
ビットOR|
ビット AND と同様に、ビット OR はビット レベルで論理和を実行します。いずれかのビットが 1 の場合、結果は 1 になり、両方のビットが 0 の場合のみ 0 になります。そのシンボルは で、|パイプと呼ぶことができます。
11001000
| 10111000
--------
= 11111000
[2]
ビット単位の排他的論理和^
ビット単位のXOR(排他的論理和)は排他的論理和を実行します。これは2つのビットを加算して桁上がりを破棄することと同じです。結果がゼロになるのは、2つのゼロまたは2つの1がある場合のみです。[3] XORはビットを1と0の間で切り替えるために使用できます。したがってi = i ^ 1、ループで使用すると、その値は1と0の間で切り替わります。[4]
11001000
^ 10111000
--------
= 01110000
シフト演算子
ビットシフト演算子は2つあります。
- 右シフト(
>>) - 左シフト(
<<)
右シフト>>
右シフト演算子のシンボルは です>>。この演算子の演算には、2 つのオペランドが必要です。左オペランドの各ビットを右にシフトします。演算子の後の数字は、ビットがシフトされる位置の数 (つまり、右オペランド) を決定します。したがって、 を実行すると、ch >> 3すべてのビットが 3 桁右にシフトされます。
ただし、シフト オペランド値が負の数であるか、この値の合計ビット数以上である場合、未定義の動作が発生することに注意してください。たとえば、32 ビットの符号なし整数をシフトする場合、シフト量が 32 以上になると未定義になります。
例:
- 変数
chにビットパターン が含まれている場合11100101、 はch >> 1結果 を生成し01110010、 はch >> 2を生成します00111001。
ここで、ビットが右にシフトされると同時に左側に空白が生成されます。符号なし型または符号付き型の負でない値に対して実行された場合、実行される演算は論理シフトであり、空白は s (ゼロ) で埋められます0。符号付き型の負の値に対して実行された場合、結果は技術的には実装定義 (コンパイラ依存) ですが、[5]ほとんどのコンパイラは算術シフトを実行し、空白は左オペランドの設定された符号ビットで埋められます。
右シフトは、次のようにビット パターンを 2 で割るのに使用できます。
i = 14 ; // ビットパターン 00001110 j = i >> 1 ; // ここではビットパターンが 1 シフトされているので、00000111 = 7 となり、これは 14/2 です。
右シフト演算子の使用
C における右シフト演算子の典型的な使用法は、次のコードからわかります。
例:
#include <stdio.h> void showbits ( unsigned int x ) { int i = 0 ; for ( i = ( sizeof ( int ) * 8 ) - 1 ; i >= 0 ; i -- ) { putchar ( x & ( 1u << i ) ? '1' : '0' ); } printf ( " \n " ); }
int main ( void ) { int j = 5225 ; printf ( "%d in binary \t\t " , j ); showbits ( j );
/* 右シフト演算のループ */
for ( int m = 0 ; m <= 5 ; m ++ ) { int n = j >> m ; printf ( "%d right shift %d gives " , j , m ); showbits ( n ); } return 0 ; }
上記のプログラムの出力は次のようになります。
5225 を 2 進数で表すと 000000000000000000001010001101001
5225 右シフト 0 は 000000000000000000001010001101001 になります
5225 右シフト 1 は 00000000000000000000101000110100 になります
5225 右シフト 2 は 000000000000000000000010100011010 になります
5225 右シフト 3 は 000000000000000000000001010001101 になります
5225 右シフト 4 は 000000000000000000000000101000110 になります
5225 右シフト 5 は 000000000000000000000000010100011 になります
左シフト<<
左シフト演算子の記号は です <<。これは、左側のオペランドの各ビットを、右側のオペランドで示される位置の数だけ左にシフトします。これは、右シフト演算子とは逆の動作をします。したがって、ch << 1上記の例 ( 11100101) で を実行すると、 が得られます11001010。生成された空白は、上記のようにゼロで埋められます。
ただし、シフト オペランド値が負の数であるか、この値の合計ビット数以上である場合、未定義の動作が発生することに注意してください。これは、ISO 9899:2011 6.5.7 ビット単位のシフト演算子の標準で定義されています。たとえば、32 ビットの符号なし整数をシフトする場合、シフト量が 32 以上になると未定義になります。
左シフトは整数を2の累乗で乗算するのに使用できます。
int i = 7 ; // 10 進数の 7 は 2 進数では (2^2) + (2^1) + (2^0) = 0000 0111 int j = 3 ; // 10 進数の 3 は 2 進数では(2^1) + (2^0) = 0000 0011 k = ( i << j ); // 左シフト演算により、10 進数で値に 2 の j 乗を掛けます// i の 2 進数表現に j 個のゼロを追加するのと同じです// 56 = 7 * 2^3 // 0011 1000 = 0000 0111 << 0000 0011
例: 簡単な加算プログラム
次のプログラムは、AND、XOR、左シフト (<<) を使用して 2 つのオペランドを追加します。
#include <stdio.h>
int main ( void ) { unsigned int x = 3 , y = 1 , sum , carry ; sum = x ^ y ; // x XOR y carry = x & y ; // x AND y while ( carry != 0 ) { carry = carry << 1 ; // キャリーを左シフトx = sum ; // x を sum として初期化y = carry ; // y を carry として初期化sum = x ^ y ; // 合計が計算されるcarry = x & y ; /* キャリーが計算され、ループ条件が評価され、 キャリーが 0 になる までプロセスが繰り返されます。 */ } printf ( "%u \n " , sum ); // プログラムは 4 を出力しますreturn 0 ; }
ビット代入演算子
C言語は、各二項算術演算とビット演算に対して複合代入演算子を提供します。各演算子は左オペランドと右オペランドを受け取り、両方に対して適切な二項演算を実行し、結果を左オペランドに格納します。[6]
ビット代入演算子は次のとおりです。
論理的等価性
ビット演算子のうち 4 つには、同等の論理演算子があります。これらは、同じ真理値表を持つという点で同等です。ただし、論理演算子は、オペランドの各ビットを独立した値として扱うのではなく、各オペランドを true または false のいずれか 1 つの値のみを持つものとして扱います。論理演算子は、ゼロを false と見なし、ゼロ以外の値を true と見なします。もう 1 つの違いは、論理演算子は短絡評価を実行することです。
以下の表は同等の演算子を対応させ、演算子のオペランドとして a と b を示しています。
!=は と同じ真理値表を持ちます^が、真の論理演算子とは異なり、それ自体は!=厳密に言えば論理演算子ではありません。これは、論理演算子がゼロ以外の値を同じように扱う必要があるためです。論理演算子として使用するには、!=まずオペランドを正規化する必要があります。両方のオペランドに論理 not を適用しても、結果の真理値表は変更されませんが、比較の前にすべてのゼロ以外の値が同じ値に変換されます。これは、!ゼロでは常に 1 になり、!ゼロ以外の値では常に 0 になるためです。
例:
/* 同等のビット演算子と論理演算子のテスト */
#include <stdio.h>
void testOperator ( char * name 、unsigned charでした、unsigned char が期待されます);
int main ( void ) { // -- ビット演算子 -- //
// ビットにパックされた真理値表
const unsigned char operand1 = 0x0A ; //0000 1010 const unsigned char operand2 = 0x0C ; //0000 1100 const unsigned char expectedAnd = 0x08 ; //0000 1000 const unsigned char expectedOr = 0x0E ; //0000 1110 const unsigned char expectedXor = 0x06 ; //0000 0110 const unsigned char operand3 = 0x01 ; //0000 0001 const unsigned char expectedNot = 0xFE ; //1111 1110
testOperator ( "ビットごとの AND" , operand1 & operand2 , ExpectAnd ); testOperator ( "ビットごとの OR" , operand1 | operand2 , ExpectedOr ); testOperator ( "ビットごとの XOR" , operand1 ^ operand2 , ExpectedXor ); testOperator ( "ビットごとの NOT" 、~ operand3 、ExpectNot ); printf ( " \n " );
// -- 論理演算子 -- //
const unsigned char F = 0x00 ; //ゼロconst unsigned char T = 0x01 ; //ゼロ以外の値
// 配列に詰め込まれた真理値表
const unsigned char operandArray1 [ 4 ] = { T , F , T , F }; const unsigned char operandArray2 [ 4 ] = { T , T , F , F }; const unsigned char expectedArrayAnd [ 4 ] = { T , F , F , F }; const unsigned char expectedArrayOr [ 4 ] = { T , T , T , F }; const unsigned char expectedArrayXor [ 4 ] = { F , T , T , F }; const unsigned char operandArray3 [ 2 ] = { F , T }; const unsigned char expectedArrayNot [ 2 ] = { T , F };
int i ; for ( i = 0 ; i < 4 ; i ++ ) { testOperator ( "論理積" , operandArray1 [ i ] && operandArray2 [ i ], expectedArrayAnd [ i ]); } printf ( " \n " );
for ( i = 0 ; i < 4 ; i ++ ) { testOperator ( "論理OR" , operandArray1 [ i ] || operandArray2 [ i ], expectedArrayOr [ i ]); } printf ( " \n " );
for ( i = 0 ; i < 4 ; i ++ ) { // ゼロ以外の値が異なる場合はオペランドに ! が必要ですtestOperator ( "Logical XOR" , ! operandArray1 [ i ] != ! operandArray2 [ i ], expectedArrayXor [ i ]); } printf ( " \n " );
for ( i = 0 ; i < 2 ; i ++ ) { testOperator ( "論理NOT" , ! operandArray3 [ i ], expectedArrayNot [ i ]); } printf ( " \n " );
0 を返す; }
void testOperator ( char * name , unsigned char was , unsigned char expected ) { char * result = ( was == expected ) ? "passed" : "failed" ; printf ( "%s %s, was: %X expected: %X \n " , name , result , was , expected ); }
上記のプログラムの出力は次のようになります。
ビット AND が成功しました。結果は 8、予想値は 8 です。
ビット OR が渡されました: E 期待値: E
ビット単位の XOR が成功しました。結果は 6、予想値は 6 です。
ビット単位で渡されませんでした: FE 期待値: FE
論理積が成功しました。結果は 1 でした。予想値は 1 でした。
論理積が成功しました。結果は 0、予想結果は 0
論理積が成功しました。結果は 0、予想結果は 0
論理積が成功しました。結果は 0、予想結果は 0
論理 OR が成功しました: 1 期待値: 1
論理 OR が成功しました: 1 期待値: 1
論理 OR が成功しました: 1 期待値: 1
論理 OR が渡されました。結果は 0、予想値は 0
論理 XOR が成功しました。結果は 0、予想結果は 0
論理 XOR が成功しました。結果は 1 でした。予想値は 1 でした。
論理 XOR が成功しました。結果は 1 でした。予想値は 1 でした。
論理 XOR が成功しました。結果は 0、予想結果は 0
論理 NOT が渡されました。: 1 期待値: 1
論理 NOT が渡されました。値: 0 期待値: 0
参照
参考文献
- ^ カーニハン、デニス・M・リッチー( 1988年3月)。『プログラミング言語C』(第2版)。ニュージャージー州エングルウッドクリフス:プレンティスホール。ISBN 0-13-110362-8. 2019年7月6日時点のオリジナルよりアーカイブ。2019年9月7日閲覧。多くの人から C に関する権威ある参考文献とみなされています。
- ^ ab 「チュートリアル - C および C++ でのビット演算子とビット操作」。cprogramming.com。
- ^ 「排他的論理和ゲートチュートリアル」。基本的な電子工学チュートリアル。
- ^ 「C++ ノート: ビット演算子」. fredosaurus.com .
- ^ 「ISO/IEC 9899:2011 - 情報技術 - プログラミング言語 - C」。www.iso.org。
- ^ 「複合代入演算子」。IBM。International Business Machines 。 2022年1月29日閲覧。
外部リンク
- ビット演算子
- ビット演算の謎を解く、やさしいCチュートリアル
