フラグとビット演算
フラグ
フラグとは、英語でflag、つまり旗を意味する単語です。この言葉はプログラミングによく用いられ、あるものが有効か無効か、ONかOFFか、といったような状態を表すものとしてしばしば利用されます。このような状態を表す型は、一般的にはbooleanなどと呼ばれ、多くの言語では、専用のデータ型が存在しますが、C言語においては、存在しません。
そこで、C言語でこういったフラグを用いる際には、様々な方法を用います。もっとも簡単な方法は、#defineでマクロを用いて定義する方法です。まずは、その方法から紹介してくことにします。
サンプルプログラム
以下のサンプルは、C言語でフラグを表現するための最も単純なサンプルです。入力して実行してみてください。
listex-flag1-1:main.c#include <stdio.h> #include <stdlib.h> #include <time.h> void main(){ int a,b; // 乱数の初期化 srand((unsigned) time(NULL)); // 1から10までの乱数を発生させる a = rand() % 10 + 1; b = rand() % 10 + 1; // 計算結果を出力 printf("%d + %d = %d¥n",a,b,a+b); }
乱数関連の関数
このプログラムの特徴は、実行するたびに結果が変わることです。それは、10行目、11行目で変数a,bに値を代入する際に使用するrand()関数に理由があります。 まずは、このプログラムで用いられている関数を紹介しましょう。(表1-1)
関数 | 書式 | 意味 | 使用例 |
---|---|---|---|
srand() | srand(unsigned seed); | randで発生させる乱数の系列を変更。 | srand((unsigned) time(NULL)); |
rand() | rand() | 0~RAND_MAX の間の疑似乱数を返す。 (RAND_MAXは処理系依存) | rand() % 10 + 1; |
time関数
これだけを見ると、srand()を使用する意味がよくわからないと思います。実は、C言語で発生させる乱数は、擬似的なもので、この処理を行わないと、 なんども同じパターンの乱数が発生してしまいます。
そこで、srand()関数を用いて変数を初期化するのですが、この時、引数が同じでは、やはり同じパターンの数値しか出ません。そこで、 引数として、秒単位で現在時刻を返すtime()関数を用い、実行するごとに違う系列の乱数が出現するようにしているわけです。
関数 | 書式 | 意味 | 使用例 |
---|---|---|---|
time() | time_t time(time_t *timer); | 1970年 1月 1日の00:00:00 から現在までの 経過時間を秒単位で取得する。 | time(NULL); |
なお、rand(),srand()関数を利用するには、stdlib.h、time()関数を利用するには、time.hをインクルードする必要があります。
乱数の範囲を指定する
では、プログラムの中身を紹介していきましょう。まず、8行目で乱数を初期化しています。次に、10,11行目で乱数を発生させていますが、 通常、rand()関数は、0からRAND_MAXという非常に大きな値を発生させます。そのため、1から10の乱数を発生させるには、まず%10をします。 これは10で割った余りという意味なので、乱数は0から9の範囲に限定されますから、それに1を足せば、1から10の値を得ることが出来るのです。(図1-1)
![]() |
考え方としては難しいですが、以下の方法を覚えておくと、非常に便利でしょう。
1からnまでの乱数をえる方法rand() % n + 1
rand() % (n + 1)
数学関数
サンプルプログラム
C言語には、沢山の数値計算用の関数が用意されています。非常にたくさんあり、全てを紹介することはできませんが、比較的使用頻度の高いものをここでいくつか紹介していきましょう。
まずは、三角関数を使ったサンプルを見てみましょう。
listex1-2:main.c#include <stdio.h> #include <math.h> #define PI 3.14 void main(){ int angle; double rad; printf("角度を入力してください(0~360):"); scanf("%d",&angle); // 角度をラジアンに変換 rad = PI * (double)angle / 180.0; // 三角関数での計算 printf("sin(%d)=%f¥n",angle,sin(rad)); printf("cos(%d)=%f¥n",angle,cos(rad)); printf("tan(%d)=%f¥n",angle,tan(rad)); }
sin(45)=0.706825
cos(45)=0.707388
tan(45)=0.999204
三角関数
sin、cos、tanは、その綴りのとおり、ずばりサイン・コサイン・タンジェントを意味します。つまり、数学でいうところの三角関数です(表1-3.参照)。
関数 | 書式 | 意味 | 使用例 |
---|---|---|---|
tan() | tan(double rad); | ()に指定したラジアンを入れるとタンジェントが得られる。 | doube a = tan(2*3.14); |
sin() | sin(double rad); | ()に指定したラジアンを入れるとサインが得られる。 | doube a = sin(2*3.14); |
cos() | cos(double rad); | ()に指定したラジアンを入れるとコサインが得られる。 | doube a = cos(2*3.14); |
なお、これらはすべて、math.hに含まれています。
ラジアン
ただ、引数として与えるのは、角度ではなく、ラジアンだという点に注意してください。通常、角度を0度から360度までで測るやり方を、度数法(どすうほう)と言いますが、これに対し、ラジアンで角度を指定する方法を弧度法(こどほう)と言います。
ラジアンという考え方は、180度をπ(パイ)ラジアンと考えるやり方で、一般の角度をラジアンに変換するには、180で割り、そこに円周率をかけてやる必要があります。
定数の設定
この計算は、12行目で行っていますが、円周率を表すPIが定義されているのは4行目です。#defineマクロはすでにファイル分割で扱いましたが、このように、定数を定義するのにも用いることが出来ます。
#defineマクロによる定数の定義このようにすれば、PIという文字列が出てくれば、このソースの中では、3.14として扱うことが出来ます。
マクロには、定数を表現する方法のほかに、引数を与えて関数のような処理を行う引数付きマクロと呼ばれるものがあります。詳しくは、こちらをご覧ください。
その他の数学関数
続いては、その他よく使う数学関数を見てみましょう。
listex1-3:main.c#include <stdio.h> #include <math.h> void main(){ int n = -2; double d1 = -2.5,d2 = 4.0; printf("%dの絶対値は%d¥n",n,abs(n)); printf("%fの絶対値は%f¥n",d1,fabs(d1)); printf("%fの2乗は%fです。¥n",d2,pow(d2,2)); printf("%fの平方根は%fです。¥n",d2,sqrt(d2)); }
-2.500000の絶対値は2.500000
4.000000の2乗は16.000000です。
4.000000の平方根は2.000000です。
使用した関数の説明をしましょう(表1-4)。
関数 | 書式 | 意味 | 使用例 |
---|---|---|---|
abs() | abs(int n); | 与えられた整数の絶対値を求める。 | int n = abs(-10); |
fabs() | fabs(double d); | 与えられた実数の絶対値を求める。 | double d = fabs(-3.1); |
pow() | pow(double x,double y); | xのy乗を求める。 | double d = pow(3.0,2.0); |
sqrt() | sqrt(double d); | 与えらられた実数の平方根を求める。 | double d = sqrt(25.0); |
絶対値を求める関数には、abs(),fabs()の二種類があります。状況に応じて使い分けましょう。
ビット演算
サンプルプログラム
今まで数値の演算ばかり扱ってきましたが、ビット演算も大事な演算です。以下にC言語におけるビット演算のサンプルを取り上げてみました。実行してみてください。
listex1-4:main.c#include <stdio.h> #include <stdlib.h> void main(){ // 16進数 unsigned char i = 0xf; // 2進数:00001111 unsigned char j = 0xff; // 2進数:11111111 printf("%x << 1 = %x¥n",i,i << 1); // 1ビット左シフト: 2進数:00011110 = 0x1e0 printf("%x >> 1 = %x¥n",i,i >> 1); // 1ビット右シフト: 2進数:00000111 = 0x7 printf("%x | %x = %x¥n",i,j,i | j); // OR演算 : 2進数:11111111 = 0xff printf("%x & %x = %x¥n",i,j,i & j); // AND演算 : 2進数:00001111 = 0xf printf("~%x = %x¥n",i,(unsigned char)~i); // NOT演算 : 2進数:11110000 = 0xf0 }
f >> 1 = 7
f | ff = ff
f & ff = f
~f = f0
ビット演算の演算子
以下、演算子とその処理の内容を表にまとめます(表1-5)。
演算子 | 意味 | 使用例 |
---|---|---|
<< | 左シフト。指定されたビット数分、数値を左シフトする。 | 00001111 << 1 → 00011110 (1ビット左シフト) |
>> | 右シフト。指定されたビット数分、数値を右シフトする。 | 00001111 >> 1 → 00000111(1ビット右シフト) |
| | 論理和(OR演算)。二つの数値の論理和をとる。 | 00001111 | 11111111 → 11111111 |
& | 論理積(AND演算)。二つの数値の論理積をとる。 | 00001111 & 11111111 → 00001111 |
~ | 否定演算(NOT演算)。入力された1なら0に、0なら1に反転。 | ~00001111 → 11110000 |
これらの演算は、いわゆる数学的な処理とは違いますが、様々な場面で応用されることがあります。基本的な使い方をおさえておきましょう。