digirakuda ようこそ!  コンピュータの世界へ!

第18回 ビット演算子

このドキュメントは http://icrus.org/c_language_beginers_course/ 上にあります.

ビット演算子を用いたビット演算はC言語の特徴の1つです.2進法の16進法の考え方が苦手な人はこの際にプログラムで試しながら覚えてしまいましょう.

[課題18-1] int型の変数を2進数,16進数で表そう.

[プログラム例18-1]


01: #include <stdio.h>
02:
03: void main(void){
04:
05:     char *print_bin(unsigned int);
06:     int  n=1;
07:     char str[50];
08:
09:     while( gets( str )!=NULL ){
10:         if( *str==0 )
11:             break;
12:         sscanf( str, "%d", &n );
13:         printf( "(10進)%6d= (2進)%s = (16進)%04X\n", n, print_bin( n ), n );
14:     }
15: }
16: char *print_bin( unsigned int x ){
17:     static char  out[50];
18:     unsigned int n, i, j;
19:     n = 1<<15;
20:     j = 0;
31:     for( i=0; i<16; i++ ){
32:         if( x&(n>>i) )
33:             out[j]='1';
34:         else
35:             out[j]='0';
36:         j++;
37:         if( i%4==3 ){
38:             out[j]=',';
39:             j++;
40:         }
41:     }
42:     out[j-1] = '\0';
43:     return out;
45: }


09~15行目 → 関数gets()でキーボードから文字列strを受け取り処理を繰り返すループです.
10~11行目 → 文字列strの先頭が文字'\0'のとき(リターンキーのみのとき)ループから抜けプログラムを終了します.
12行目 → 文字列strからint型の変数nを取り出します.
13行目 → 関数printf_bin(n)を使って変数nから2進数表示の0,1の文字列を得ます.
13行目 → 変数nの10進数,2進数,16進数をそれぞれ表示します.
16~45行 → 関数print_bin()の本体です.
31~41行 → 変数nは,はじめ0ビット目(最上位ビット)のみ1にして,右に変数i分だけシフトして使います.forループで引数xの0ビット目から15ビット目まで,ビットが1のとき文字列outに'1'を追加し,0のときは'0'を追加します.

[結果18-1]

コンパイル・実行すると次のよう表示されます.
0[リターン印]
(10進) 0 = (2進)0000,0000,0000,0000 = (16進)0000
1[リターン印]
(10進) 1 = (2進)0000,0000,0000,0001 = (16進)0001
2[リターン印]
(10進) 2 = (2進)0000,0000,0000,0010 = (16進)0002
4[リターン印]
(10進) 4 = (2進)0000,0000,0000,0100 = (16進)0004
567[リターン印]
(10進) 567 = (2進)0000,0010,0011,0111 = (16進)0237
21354[リターン印]
(10進) 21354 = (2進)0101,0011,0110,1010 = (16進)536A
-32768[リターン印]
(10進)-32768 = (2進)1000,0000,0000,0000 = (16進)8000
32767[リターン印]
(10進) 32767 = (2進)0111,1111,1111,1111 = (16進)7FFF
-1[リターン印]
(10進) -1 = (2進)1111,1111,1111,1111 = (16進)FFFF

リターンキーを押すとプログラムは終了します.
 プログラムは整数値(-32768~32767)を入力するとその2進法表現と16進法表現を画面に出力します.入力した整数と表示された2進表現を比べていくと,2進数の仕組みが良く理解できると思います.

~ビット演算子について~

ビット演算子には ~,&,|,<<,>>,^の4つがあります.それぞれの働きを次に示します.


例 機能 説明 優先順位



a~ ビット反転 変数aのビットを反転する   1
a >> n 右シフト 変数aのビットを下位ビット側に変数n分シフトする 2
b << n 左シフト 変数aのビットを上位ビット側に変数n分シフトする 2
a & b ビットAND 変数aと変数bのビット単位のANDをとる. 3
a ^ b ビットXOR 変数aと変数bのビット単位のXORをとる. 4
a | b ビットOR 変数aと変数bのビット単位のORをとる. 5

このプログラムでは32行目で
x&(n>>i)
と使用しています.変数iは0~15まで変化します.そのとき(n>>i)は2進表現で
i== 0 のとき 1000 0000 0000 0000
i== 1 のとき 0100 0000 0000 0000
i== 2 のとき 0010 0000 0000 0000
: :
i==15 のとき 0000 0000 0000 0001
と右シフトされていきます.x&(n>>i)は引数xと(n>>i)のビット単位のANDを取ります.i==0のとき,(n>>i)は最上位ビットが1となり,引数xとのビット単位のANDをとった値を以下の2の条件に分けて処理します.
①引数xの最上位ビットが1のときx&(n>>i)は,非0(0でない)になり,そのとき文字列outに文字'1'を追加します・・・33行目.
②引数xの最上位ビットが0のとき,x&(n>>i)は0になり,文字列outに文字'0'を追加します・・・35行目.
同様にi==0からi=15まで変数nを右へビットシフトしながら,forループで処理すると,変数xの最上位ビット(0ビット目)から最下位ビット(15ビット目)までのビットの0/1が文字列outに反映されます.
 もうひとつ処理は,表示が見やすいように4桁ずつ「,」で区切るようにしています.
 ここで注意すべきする点は,引数xをunsigned int型(符号なし整数)に宣言している点です.右ビットシフトの演算の場合符号付きのint型の場合,最上位ビットが1のとき負数とみなされ右シフトしたとき,最上位ビットが0ではなく1になってしまい,ここで必要なシフト処理ができなくなります.

[課題18-2] プログラムを使って,ビット演算子の機能を理解しよう.

[プログラム例18-2]


01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <process.h>
04:
05: void main(void){
06:     char *print_bin(unsigned int);
07:     char str[50];
08:     unsigned int a, b;
09:
10:     while( gets( str )!=NULL ){
11:         if( *str==0 )
12:             break;
13:         sscanf( str, "%d%d", &a, &b );
14:
15:         puts( "ビットのNOT" );
16:         printf( "a  =(10進)%6d= (2進)%s\n", a,   print_bin(a   ) );
17:         printf( "a~ =(10進)%6d= (2進)%s\n", ~a,  print_bin( ~a  ) );
18:         puts( "ビットAND" );
19:         printf( "a  =(10進)%6d= (2進)%s\n", a,   print_bin( a   ) );
20:         printf( "b  =(10進)%6d= (2進)%s\n", b,   print_bin( b   ) );
31:         printf( "a&b=(10進)%6d= (2進)%s\n", a&b, print_bin( a&b ) );
32:         puts( "ビットXOR" );
33:         printf( "a  =(10進)%6d= (2進)%s\n", a,   print_bin( a   ) );
34:         printf( "b  =(10進)%6d= (2進)%s\n", b,   print_bin( b   ) );
35:         printf( "a^b=(10進)%6d= (2進)%s\n", a^b, print_bin( a^b ) );
36:         puts( "ビットOR");
37:         printf( "a  =(10進)%6d= (2進)%s\n", a,   print_bin( a   ) );
38:         printf( "b  =(10進)%6d= (2進)%s\n", b,   print_bin( b   ) );
39:         printf( "a|b=(10進)%6d= (2進)%s\n", a|b, print_bin( a|b ) );
40:         putchar( '\n' );
41:
42:         printf( "整数 a b を入力して下さい: " );
43:     }
44: }
45:
46: char *print_bin( unsigned int x ){
47:     この関数はプログラム例18-1と全く同じです.
48: }

[結果18-2]

コンパイル・実行すると次のよう表示されます.

整数 a b を入力して下さい: 124 408[リターン印]
ビットのNOT
a =(10進) 124 = (2進)0000,0000,0111,1100 ・・・①
a~ =(10進) -125 = (2進)1111,1111,1000,0011 ・・・②
ビットAND
a =(10進) 124 = (2進)0000,0000,0111,1100 ・・・③
b =(10進) 408 = (2進)0000,0001,1001,1000 ・・・④
a&b=(10進) 24 = (2進)0000,0000,0001,1000 ・・・⑤
ビットXOR
a =(10進) 124 = (2進)0000,0000,0111,1100 ・・・⑥
b =(10進) 408 = (2進)0000,0001,1001,1000 ・・・⑦
a^b=(10進) 484 = (2進)0000,0001,1110,0100 ・・・⑧
ビットOR
a =(10進) 124 = (2進)0000,0000,0111,1100 ・・・⑨
b =(10進) 408 = (2進)0000,0001,1001,1000 ・・・⑩
a|b=(10進) 508 = (2進)0000,0001,1111,1100 ・・・⑪

整数 a b を入力して下さい:[リターン印]

リターンキーを押すとプログラムは終了します.

変数a,bを入力して下さい.それをもとにビットごとの演算を行い結果を表示します.
(1)ビットのNOTの演算では,①から②へ0が1に変わり,1が0に変わっていることが良くわかります.
(2)ビットのANDの演算では,③,④ともに1のとき⑤が1になり,その他のときは0になっています.
(3)ビットのXORの演算では,⑥,⑦のどちらか一方だけが1のとき⑤が1になり,その他(両方が1のときを含む)のときは0になります.
(4)ビットのORの演算では,⑨,⑩のどちらか一方が1のとき⑪が1になり,その他のときは0になります.(どちらも0のとき0になり,その他のとき1になる.)
プログラムの実行例を見ながら説明を読めばすぐに理解できるでしょう.

[文法18]

1.ビット演算子


例 機能 説明 優先順位


a~ ビット反転 変数aのビットを反転する   1
a >> n 右シフト 変数aのビットを下位ビット側に変数n分シフトする 2
b << n 左シフト 変数aのビットを上位ビット側に変数n分シフトする 2
a & b ビットAND 変数aと変数bのビット単位のANDをとる. 3
a ^ b ビットXOR 変数aと変数bのビット単位のXORをとる. 4
a | b ビットOR 変数aと変数bのビット単位のORをとる. 5

(1)ビット演算は上の6つです.
(2)代入演算子として<<=,>>=,&=,^=,|=の5つも使えます.
(3)ビット演算は,基本的にはunsigned int型(符号なし整数型)に対して行われることが多いようです.もちろんその他の型にも適用できます.
(4)特に符号を持つ型(int型など)をビットシフト演算しようとするときは注意が必要です.最上位ビットに指定されている符号ビットの動きに注意して下さい.





このドキュメントは http://icrus.org/c_language_beginers_course/ 上にあります.

2017,1 ssatoh@ 足立工科大学 工学部 情報通信工学科