[C言語]列挙型(enum)の具体的な使い方とススメ[コード付]

C言語

どうも久しぶりです。
突然ですが皆さんは列挙型(enum)を知っていますか?
知っている方も使っていますか?

列挙型そのものは計算量が減ったり、コードの量が減ったりするような関数などではありません。
しかし、コードを見やすくする上でめっちゃ重要なので備忘録的な意味も込めて記事にしたいと思います。

列挙型(enum)とは

まずはここから。
enumの読み方はイーナムとかエニュームっていうらしいです。(筆者はイーナム派)

enumがどういうものなのかを調べるとたくさん出てきますが、筆者的に噛み砕いて表すと

関連性のある複数の列挙子を一つの型にまとめてしまうこと

です。

???????

まあまあ。
どういうことかを少し具体的に解説しましょう。
たとえば何らかのコードで現在の曜日をint型の変数weekに入れるとしましょう。

int main(void){
    int weekday;
    //1=sun,2=mon,3=thu,4=wed,5=thr,6=fri,7=stu
    weekday = 2;//今日が月曜日だった場合
    switch(weekday){
         case 1:
              printf("Sunday");break;
          case 2:
              printf("Monday");break;
////////////////////////////////////
          case 7:
              printf("Sthuday");break;
          default:
            break;
    }
    return 0;
}

コメントアウトに書いてある通り1から7の数字にそれぞれの曜日を当てはめることでそれぞれの曜日を表しています。

でも少しわかりにくいですよね。
というのも、weekdayに代入している数値が曜日を表す上で割り振られた数なのか、それとも数値として意味を持つ数なのかがコードをパッと見ただけでは判別が難しいからです。

そこでenumです!
上に書いた通りenumは複数の列挙子を一つの集合としてまとめることができます。
ここでいう列挙子というのは普通に文字列だと捉えてもらって大丈夫です。
具体的にコードを書いてみます。

enum week{
    SUNDAY,
    MONDAY,
    TUSESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY
};

こんな感じでブロック外に
enum 集合名{
列挙子1,列挙子2, ,,,, 列挙子n
}
という感じで記述します。構造体みたいな感じですね。
そんでもってこれがどうなるかというと

enum week{
    SUNDAY,
    MONDAY,
    TUSESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY
};


int main(int argc, const char * argv[]) {
    enum week weekday;
    
    weekday = MONDAY;
    switch (weekday) {
        case SUNDAY:
            printf("Sunday"); break;
        case MONDAY:
            printf("Monday"); break;
            /////////////////////////////////
        case SATURDAY:
            printf("Saturday"); break;
        default:
            break;
    }
}

先ほどのコードと違い、変数weekdayがint型ではなく、ブロック外で定義したenum week型に変えています。
こうすることで、weekdayには定義した際にまとめた列挙子を代入することができます
つまりMONDAYとかWEDNESDAYとかですね。
このように記述する方がパッとみただけでわかりやすいですし、曜日を数値で管理していると何かの間違いでインクリメントだったりしても気づきにくいですよね。

何となくイメージはつかめましたでしょうか?
ここからはより具体的な使用例について書いていきます〜

サイコロの状態を保持するプログラム

この前筆者がpaizaで問題を解いていた時にサイコロを用いたものに出会いました。
入力された指示に従ってサイコロをどんどん転がしていき、その度にサイコロの目を出力する的な処理が求められていました。

んー、正面から見たサイコロのそれぞれの面に対して番号を降って状態を管理するのが良いかな、、 
面の数が6だからint dice_state[6]みたいな感じでサイコロの状態を保持する変数を定義して、、

こんな感じですね。
ただ方針としては合っていますがこれも上の曜日の時と同じくぱっと見でわかりにくいですよね。
しかも曜日であれば何となく1〜7で日〜土といったイメージはありますが、立体的なサイコロに番号を振ると連続的で無い部分があるのでそこがさらにややこしくしていますよね、、

てことでenumを使いましょう!
今回はこのように定義します!

enum dice{
    Bottom,
    Down,
    Roof,
    Up,
    Right,
    Left
};


画像のように正面から見たサイコロのそれぞれの面に名前(列挙子)を定義しました!
その上でU,D,L,Rと入力するとそれぞれサイコロを上、下、左、右に一面だけ倒すようなプログラムを組んでいきます!

#include <stdio.h>
#include <string.h>

enum dice{
    Bottom,
    Down,
    Top,
    Up,
    Right,
    Left
};

//サイコロを転がした際の面の変更処理の関数
void change_dice(enum dice a, enum dice b, enum dice c, enum dice d, int dice_state[6]){
    int dice_temp;//aがb,bがc,cがd,dがaに変わる
    dice_temp = dice_state[a];
    dice_state[a] = dice_state[b];
    dice_state[b] = dice_state[c];
    dice_state[c] = dice_state[d];
    dice_state[d] = dice_temp;
}

int main(int argc, const char * argv[]) {
    char s[64];
    scanf("%d",s);

    //初期のサイコロの状態
    int dice_state[6];
    dice_state[Bottom]=6;
    dice_state[Top]=1;
    dice_state[Up]=5;
    dice_state[Down]=2;
    dice_state[Right]=4;
    dice_state[Left]=3;
    
    for(int i=0; i<N; i++){
        switch(s[i]){
            case 'U':
                change_dice(Bottom,Down,Top,Up,dice_state);
                break;
            case 'D':
                change_dice(Up,Top,Down,Bottom,dice_state);
                break;
            case 'L':
                change_dice(Bottom,Left,Top,Right,dice_state);
                break;
            case 'R':
                change_dice(Bottom,Right,Top,Left,dice_state);
                break;
            default:
                break;
        }
    printf("%d",dice_state[Top]);//現在のサイコロの目を表示する
    }
    
    return 0;
}

もし上のBottomだとかTopが数値だったら物凄く見づらいですよね、、

というわけでenumを用いれば見やすくなりますし。コーディングのミスも減らせるというわけでした。

コメント

タイトルとURLをコピーしました