(1)確認用プログラムの準備
ビット演算とは,
データを1ビットずつ処理する演算ですが,
これまで出てきた加算・減算などの演算にくらべ,
なじみのない方が多いかもしれません。
しかし,
ひとつの命令をフィールドに分けたり,
特定のビットを判定するには,
必要不可欠な演算です。
当然,システム記述用言語としては,
これらの操作ができなくてはいけません。
しかし,なじみがない方のほうが多いと思います。
そのような方は,
演算結果をビットごとに確認するプログラムを
先につくってしまいましょう。
このプログラムは
ビット構造体や共用体の例でもありますので,
ぜひ,例題として保存しておくことを
お勧めします。
|
[ビット演算確認用プログラム] |
|
#include "stdafx.h"
#include "string.h"
/*(1)ビットフィールド構造体*/
struct bitdata
{
unsigned b7:1;
unsigned b6:1;
unsigned b5:1;
unsigned b4:1;
unsigned b3:1;
unsigned b2:1;
unsigned b1:1;
unsigned b0:1;
};
/*(2)演算子テーブル構造体*/
struct opetab
{
char name[4];
int num;
};
/*(3)演算子テーブル定義*/
static struct opetab tab[7]
={{"" ,0},{">>",2},{"<<",2},
{"&",2}, {"|",2},{"^",2},
{"~",1}};
/*(4)文字とビットフィールドの領域の共有*/
union uxd
{
char ch;
struct bitdata bt;
};
/* 1ビット表示 */
void print_bit(char ch)
{
if(ch==0) printf("0");
else printf("1");
}
/* ビット列表示 */
void print_bitlist(struct bitdata ch)
{
print_bit(ch.b0);print_bit(ch.b1);
print_bit(ch.b2);print_bit(ch.b3);
print_bit(ch.b4);print_bit(ch.b5);
print_bit(ch.b6);print_bit(ch.b7);
}
/* 演算子テーブル探索*/
int table_search(int num,struct opetab tab[])
{
int i; i=num-1;
while(strcmp(tab[i].name,tab[0].name)!=0)
i--;
return i;
}
/* 演算用データ入力*/
void data_in(int num,int dt[])
{
int i;union uxd r;
for(i=0;i<num;i++)
{
printf("\nデータを16進2桁で入力(%d)=",i+1);
scanf("%x",&dt[i]);
printf(" 演算用データ:");
r.ch=dt[i];print_bitlist(r.bt);
}
}
/* 演算子入力*/
void ope_in(char ch[])
{
printf("\n演算子入力:");
scanf("%s",ch);
}
/* 結果表示*/
void print_result(struct bitdata r)
{
printf("\n\n 結 果=");
print_bitlist(r);
printf("\n");
}
/* 演算実行*/
int exec_op(int id,int dt[])
{
switch(id)
{
case 1 : return(dt[0] >> dt[1]);
case 2 : return(dt[0] <<
dt[1]);
case 3 : return(dt[0] & dt[1]);
case 4 : return(dt[0] | dt[1]);
case 5 : return(dt[0] ^ dt[1]);
case 6 : return( ~ dt[0]);
default: return 0;
}
}
/* メイン*/
int main(int argc, char* argv[])
{
int id; int dt[2];union uxd
r;
ope_in(tab[0].name);
while(strcmp(tab[0].name,"end")
!=0)
{
id=table_search(7,tab);
if(id != 0)
{
data_in(tab[id].num,dt);
r.ch=exec_op(id,dt);
print_result(r.bt);
}
else printf("**演算子の誤り\n");
ope_in(tab[0].name);
}
}
|
(2)基本的なビット演算
1ビットのデータA,Bに対する基本的な演算を
下表に示します。
A |
B |
A & B |
A | B |
A ^ B |
~ A |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
Ope 1 |
Ope 2 |
(and) |
(or) |
(exor) |
(not) |
これらの演算を使って,
あるビットがオンかオフかの判定,
ビット反転,ビットごとの組合せなどの
処理を行います。
■小演習(1)
基本的なビット演算を準備したプログラムで
確認してみましょう。
(3)ビット単位の左シフト
ビットを左にシフトします。
0000 0011 ⇒(左に1ビットシフト)⇒ 0000
0110
(10進数 3) (10進数
6)
[記述法]
A << 1 (データ A を左に1ビットシフト)
左シフトは,2n倍の乗算の替わりにも使うことができます。
シフト |
等価な乗算 |
A << 1 |
A * 2 |
A << 2 |
A * 4 |
A << 3 |
A * 8 |
A << 4 |
A * 16 |
A << 5 |
A * 32 |
■小演習(2)
左シフトを準備したプログラムで確認してみましょう。
(4)ビット単位の右シフト
ビットを右にシフトします。
0000 00110 ⇒(左に1ビットシフト)⇒ 0000
011
(10進数 6) (10進数
3)
[記述法]
A >> 1 (データ A を右に1ビットシフト)
右は,2n整数による除算の替わりにも使うことができます。
シフト |
等価な除算 |
A >> 1 |
A / 2 |
A >> 2 |
A / 4 |
A >> 3 |
A / 8 |
A >> 4 |
A / 16 |
A >> 5 |
A / 32 |
なお,符号ビットの取り扱いが,処理系によっては,
次のように異なる場合があります。
●unsignedの場合,
左端の空いた部分に0が詰められる。
(論理シフト)
●signedの場合,
最も左端のビットがコピーされる。 (算術シフト)
[算術シフト]
0000 0110 ⇒(右に1ビットシフト)⇒ 0000
0011
1100 0110 ⇒(右に1ビットシフト)⇒ 1110
0011
[論理シフト]
0000 0110 ⇒(右に1ビットシフト)⇒ 0000
0011
1100 0110 ⇒(右に1ビットシフト)⇒ 0110
0011
しかし,逆にいうと
上記のように処理されない処理系もありますので,
算術シフトを使いたい場合は,
以下のように記述することをお勧めします。
( A >> 1) | (A & 0x80)
■小演習(3)
右シフトを準備したプログラムで確認してみましょう。
(5)ビット列表示の改良
準備したプログラムのビット列表示の部分は,
ビットフィールド構造体を使わなくても
シフトを使えば,以下のように短く表現できます。
|
#/* もうひとつのビット列表示*/ |
|
void print_bitlist_dash(char ch_in)
{
char ch;int i; ch = ch_in;
for(i = 0;i < 8; i++, ch <<= 1)
printf((ch & 0x80)? "1":"0");
}
|
(6)演習
上記(5)で示したプログラムを用いて,
確認用のプログラムを短く書き直して下さい。
[ヒント]ビットフィールド構造体の宣言および
charとbitの共用体の宣言は削除します。
|
1.C言語の誕生
2.C言語の仲間たち
3.新しい言語をすばやく覚えるには
4.Cでプログラミング
5.データの入れ物 変数の考え方
6.注釈・定数
7.プログラムの実行順序
8.配列
9.C言語特有の代入文・制御構造
10.関数の話
11.構造体と共用体
12.ビット演算
13.プログラムの場所をポインタで
14.ファイルの入出力
15.色々な便利な方法
|