今回は複数の条件が存在した場合の処理の分岐について説明していきたいと思います。この条件分岐は意外と使う機会は多いんですよー
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。
C言語基礎講座インデックス

※ 2022/10/08 19:00 追加
すいません、列挙体の説明が C# と混同していました。C 言語での正しい記述方法に書き換えておきました。大変失礼しました。

※ 2022/10/12 09:00 追加
純粋なC言語だと列挙体変数宣言時に enum が必要と指摘を受けました。これまた大変失礼しました。私は C++ に慣れすぎてますね(汗


  • 複数の条件を処理する
複数の条件を判定したい事があります。if でそれを実装すると、以下のような表示となります。

int dir; printf ("方向を入力 "); scanf_s("%d", &dir) if (dir == 0)
{ printf("上"); } else if (dir == 1)
{ printf("下"); } else if (dir == 2)
{ printf("左"); } else if (dir == 3)
{ printf("右"); } else { printf("不正な方向"); }

これ、要素が増えれば増えるほど、else if が増えていってプログラムが読みづらくなりますよね。こんな時は switch の出番です。この switch は、条件別に処理を記述できるという便利な命令です。上記の長ったらしいプログラムも switch に置き換えると

int dir; printf ("方向を入力 "); scanf_s("%d", &dir) switch (dir) { case 0: printf("上"); break; case 1: printf("下"); break; case 2: printf("左"); break; case 3: printf("右"); break; default: printf("不正な方向"); break; }

こんな風に処理を分岐する事が出来ます。switch の () の中に変数が入っていますが、この変数の値が検査する対象です。{} スコープで括っているのは、以下の処理が switch の範囲である事を意味しています。そして、case に dir を記述する事で、dir の値と比較して同じであれば、そのケース以降の処理を実行します。数値は定数しか記述できません。また、その定数の後ろには必ず:コロンを記述します。

default: は全ての case に合致しなかった場合の処理となります、default: は必須ではありませんが、出来る限り記述しておくのを推奨します。case や default: 内の処理は break; により switch のスコープから抜け出します。break; は {} スコープから脱出するための命令だと覚えておくと良いでしょう。

さて、私は case から break; までを一列で記述してしまいましたが、このような書き方は、処理が短い時だけとしてください。本来の推奨される記述としては下記の通りとなります。

int dir; printf ("方向を入力 "); scanf_s("%d", &dir) switch (dir) { case 0: printf("上"); break; case 1: printf("下"); break; case 2: printf("左"); break; case 3: printf("右"); break; default: printf("不正な方向"); break; }

結局長くなってしまうんですけどね汗。まあそれでも if の羅列よりは全然見やすいと思います。
※ 水没時にクルマから脱出するための必需品。平時から備えておくと安心です。最近は線状降水帯とかでいきなり増水しますからねぇ…


  • 列挙体は便利
突然ですが、#define で好きな数値や文字列に別名を付けるとプログラムコードが見やすくて良いですよね。プログラム内に直接数値を書き入れる事をマジックナンバーと呼びます。このマジックナンバーは、普遍的な数値以外は別名定義して管理しないと、仕様変更などでバグを生み出す要因になります。

普遍的な数値とは円周率 3.1415926 や重力加速度 9.8 など、我々が生きている間に、その数値と意味が変わる事がまずあり得ない数値です。往々にして、このような普遍的な数値は、システム側で定数定義が済ませてあったりします。3.14 等はパッと見て、ああこれは円周率だと分かりますが、それでも別名定義しておく事をオススメしています。

さて、データ管理に連番を使う事はよくあります。0 なら氏名、1 ならよみがな、2 なら電話番号というように使ったりします。これ、いちいち #define していると、うっかり番号を重複して記述して、要らんバグを生み出す事にもなりかねません。そこでオススメが enum です。

enum は自分で作成する、int や float のような型だと思ってください。定義方法は以下の通りとなります。

enum タグ名 { 要素名1, 要素名2, 要素名3, 要素名4, 要素名5, 要素名6, };

タグ名とは自分で作り出す型の名前です。その型で作成された変数には、定義時に設定された要素名しか代入できません。また、この要素名はいくつでも記述する事が出来ます。先の氏名やよみがなを例として記述するなら、以下のようになります。

enum EType { Name, Ruby, Phone, };

これで EType という型が専用で用意されました。使い方も int 等とほぼ同じです。

enum EType type; // type という変数を用意 type = Name; // type に名前を代入する

参考までに C++ だと列挙体使用時に enum の記述は不要です。

EType type; // type という変数を用意 type = Name; // type に名前を代入する

Name や Ruby 等の各要素はそのまま記述します。これで通常の使用方法であれば、列挙体の要素名で数値が被る事はありません。また、その Etype の状態では四則演算は出来ません。

実は列挙体は内部的には最初が 0 から始まり、順番に +1 されて定義されています。また、列挙体にはキャストが出来ますので

int num = (int)Phone;

このように int 型に列挙の要素を代入する事が出来ます。この場合は 2 という数値が num に入ります。その逆も出来ますが…

enum EType type1 = (enum EType)1; // Ruby が代入される enum EType type2 = (enum EType)5; // 要素として存在しないのでエラー

存在しない数値をキャストしようとすると、エラーが発生します。このエラーは例外とか実行時エラーとか言って、その後の実行が停止します。Windows 上ではエラーが表示されてプログラムが閉じるだけですが、ゲーム機などではそのままフリーズする事になります。この辺りは注意ポイントですね。

どんな時に列挙体を使うかですが、その値に意味があり、またその範囲以外にデータがあり得ないモノです。それはオブジェクトタイプだったり、移動方向であったりです。例えば、先に示した移動方向のような判定に列挙体を使うとこんなイメージになります。

enum EDir { Up, Down, Left, Right }; enum Edir dirPlayer = Down; switch (dirPlayer) { case Up: printf("上"); break; case Down: printf("下"); break; case Left: printf("左"); break; case Right: printf("右"); break;
default: printf("不正な方向"); break; }

dirPlayer 変数が int より、この場合は列挙体を使用した方が分かりやすいと思いますが如何でしょうか?

>> C言語012 乱数に進む

※ 発煙筒は使用期限がありますが、これは電池があればいつまでも使えます。しかも車検対応。長い目で見たらこっちのほうが良いかと!