入門書が教えてくれない配列の本当の使い方

元ネタは何年も前にネットで拾ったもの。もうどこだか覚えたいないし、まだ残っているかわからない。でも「配列は0から始まる整数からオブジェクトへの写像だ」ということを解説しているサイトを見たことがない。

まずはお題。C言語のlocaltime関数から曜日を取得すると日曜日からのオフセット値が得られる。日, 月, 火, 水, 木, 金,土がそれぞれ0, 1, 2, 3, 4, 5, 6となる。取得したオフセット値から日本語で画面に表示するプログラムを書いてみる。

入門書にあるようなやり方ならこんな感じ。

#include <stdio.h>
#include <time.h>
#include <assert.h>

int main(void)
{
    int wday;
    time_t now_time;
    
    now_time = time(NULL);
    /* カレンダの曜日を取得 */
    wday = localtime(&now_time)->tm_wday;
    
    switch (wday) {
    case 0:
        printf("日曜日\n");
        break;
    case 1:
        printf("月曜日\n");
        break;
    case 2:
        printf("火曜日\n");
        break;
    case 3:
        printf("水曜日\n");
        break;
    case 4:
        printf("木曜日\n");
        break;
    case 5:
        printf("金曜日\n");
        break;
    case 6:
        printf("土曜日\n");
        break;
    default:
        assert(0);
        break;
    }

    return 0;
}

だけど少しプログラムになれた人なら配列を使って次のように書くだろう。

#include <stdio.h>
#include <time.h>

static char *weekTable[] = {
    "日", "月", "火", "水", "木", "金", "土",
};

int main(void)
{
    int wday;
    time_t now_time;
    
    now_time = time(NULL);
    /* カレンダの曜日を取得 */
    wday = localtime(&now_time)->tm_wday;
    
    printf("%s曜日\n", weekTable[wday]);
    
    return 0;
}

巨大なswitch文が配列でかけてしまう。このテクニック自体は割とメジャーで少し経験がある人なら誰でも知っているだろう。しかしこれを解説して
「配列は0から始まる整数からオブジェクト*1への写像だ」
といわれ、配列の見方が全く変わった。配列のインデックスは0オリジンの整数で極端に大きな領域をとるとプログラム全体に影響するのでインデックスの範囲も限られる。しかしオブジェクトについてはほぼ制限がない*2


他の人と比較すると私のコードは極端に配列を多用している。変換関数を使うようなケースでテーブル展開する場合が多い。もちろん配列の見方が変わってからハッシュのすばらしさがなおさら身にしみる。キーの制限がなくなるのだからこれほど使いでのあるデータ構造はない。


ついこの間書いたテーブル展開はこんなものだ。
0..15の整数を0..fの文字に変換する配列

/* 文字の配列。ターミネートしていないので注意 */
char dec2hex[16] = "0123456789abcdef";
int i;

/* 処理。iは0..15であること */
printf("%c\n", dec2hex[i]);

dec2hexは所謂「文字列」ではなく「文字の配列」であるので敢えてターミネートしていない。だが1バイト余分に使用することは考えずにすむだろうから以下の書き方の方がベターだろう。

/* decimal to hex 変換テーブル */
char dec2hex[] = "0123456789abcdef";
int i;

/* 処理。iは0..15であること */
printf("%c\n", dec2hex[i]);

*1:C言語のオブジェクトはオブジェクト指向プログラミングのオブジェクトとは違う。メモリ上に配置されるもの位の意味

*2:関数は配列にできないが「関数を指すポインタの配列」は作れるので制限がないといっていいだろう