入力した数値が暗号化されて、再度暗号化された数値を入力すると元の数に戻るという不思議なプログラムをC言語で紹介します。
今回は、コメントを入れて10行です。
xAngou.c
/* xAngou.c: 簡易暗号化プログラム */ #include <stdio.h> int main(void) { int x; printf("number? "); scanf("%d", &x); printf("result: %d\n", x ^ 0x1234); return 0; }
実行結果
最初に適当な数値2525を入力
number? 2525
result: 7145
再度実行し先ほど表示された7145を入力すると…
number? 7145
result: 2525
元の数2525に戻ります!
ぜひ、他の数値でも試してみてください。
不思議ですね!
でもC言語は、ビット演算が可能な言語ですのでちっとも不思議ではありません。
以下解説です。
C言語のビット演算子
ビット演算に使用できるC言語の演算子には次のものがあります。
ビット演算子 | 意味 | 使用例 | 意味 |
---|---|---|---|
& | ビット毎の論理積 | a & 10 | 変数aと00001010の論理積 |
| | ビット毎の論理和 | a | 10 | 変数aと00001010の論理和 |
^ | ビット毎の排他的論理和 | a ^ 10 | 変数aと00001010の排他的論理和 |
<< | ビット左シフト | a << 2 | 変数aの値を2ビット左にシフトする |
>> | ビット右シフト | a >> 2 | 変数aの値を2ビット右にシフトする |
~ | ビット毎の反転 | ~a | 変数aをビット反転する |
ビット演算は、2つのビット同士の計算に使います。
単純に1ビットで考えます。
aが1、bが0とします。
a & b の結果は 0です。掛け算として考えると分かりやすいです。
a | b の結果は、1です。どちらか一方が1なら1になるという考え方です。
全てのパターンを表にするとこうなります。
a & b(どちらかが0なら結果は0)
a | b | 結果 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 0 |
0 | 1 | 0 |
1 | 1 | 1 |
a | b(どちらか一方が1なら結果は1)
a | b | 結果 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 1 |
今回使った排他的論理和a ^ bはどうでしょうか?
これも表で示します。
a ^ b
a | b | 結果 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
不思議な結果です。
双方が同じ値(0と0又は1と1)の場合、0。
双方が違う値(1と0又は0と1)の場合、1となります。
これを使うと先ほどの元に戻る数が実現できます。
2進数(ビット)に直して考える
プログラムで解説します。
int型だとビット数が大きいためchar型(1バイト=8ビット)の変数を使ったプログラムで説明します。
次のプログラムを見てください。
/* 排他的論理和の確認 */ #include <stdio.h> int main(void) { /* 元の数 */ char x = 10; printf("%d\n", x); /* 排他的論理和の演算を実行 */ x = x ^ 15; printf("%d\n", x); /* もう一度、排他的論理和の演算を実行 */ x = x ^ 15; printf("%d\n", x); return 0; }
実行結果
10
5
10
数値を全て2進数に置き換えると理解できます。
まずは最初の状態
次に最初に排他的論理和のビット演算をした状態
もう一度排他的論理和のビット演算をした状態
見事元に戻りました!
最初に紹介した10行プログラムでは、以下の部分が排他的論理和の演算に当たります。
printf(“result: %d\n”, x ^ 0x1234);
16進数表記になっていますが、やっていることは一緒です。x ^ 0x1234の0x1234の箇所は適当な数値です。
他の数値にするとresultで表示される数値は変わりますが、元に戻ります。
ビット演算面白いですね!
コメント