第22回
データ構造(1)~配列とその扱い

列挙型

配列と似たデータ構造に「列挙型」があります。配列は変数を格納するため、各要素の値を自在に変更できますが、列挙型は複数の定数を並べてそれに名前を付けたものです。

列挙型の定義

列挙型を定義するにはenum命令を使います。書式は以下のようになります。
enum <名前>{<要素名リスト...>};

<名前>はこの列挙型を識別するための名前です。
<要素名リスト...>には、列挙する値に対応する名前を「,」で区切って複数記述します。

すると、{ }内の先頭の要素名を0とし、1ずつ増加する値が要素名に当てはめられていきます。

曜日に連番を割り当てる

例えば、曜日を示すSun、Monなどの英略字に0~6までの番号を当てはめる場合、以下のようにすると非常に面倒です。
#define Sun 0
#define Mon 1
#define Tue 2
      :
そのような場合に、enum命令を使って以下のように宣言します。
enum Week{Sun, Mon, Tue, Wed, Thu, Fri, Sat};
こうすると、ソース内では
Week.Sun
とすると値0が、
Week.Wed
とすると値3が参照されます。

先頭の値を0以外に設定する場合は、以下のように先頭の要素名に初期値を代入します。
enum Week{Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat};
これで、Week.Sunが1、Week.Wedが4となります。


サンプルのファイルについて

今回紹介したリスト1~リスト3のソースは、それぞれmain関数を使ったプログラムとしてコンパイル可能な形にしたものをサンプルとして用意しました。また、それらをコンパイルした実行形式ファイル(MS-DOSまたはWindowsのコマンド プロンプトから実行可能な.exeファイル)も作ってあります。

リスト1:ソース"ex2201.c"、実行形式"EX2201.EXE"
リスト2:ソース"ex2202.c"、実行形式"EX2202.EXE"
リスト3:ソース"ex2203.c"、実行形式"EX2203.EXE"

あとがき ~scanf関数とgets関数について~

これまで、キーボードから値を受け取る処理ではscanf関数を使ってきました。しかし、リスト2ではgets関数を使いました。その理由を説明しておきます。

scanf関数のクセ

リスト2では、forによる繰り返しの中でキーボードからの入力を実行しています。このような場合、scanf関数独特のクセが目立ってしまうのです。

プログラムの中でscanf関数を1回だけ実行するのであれば、特に問題はありません。しかしscanf関数を2回実行すると、2回目に呼び出されたscanf関数は直前(1回目)に呼び出されたscanf関数で値の後に押された[Enter]キーをCR(キャリッジリターン=改行)コードの1文字として受け取ってしまいます。

そのため、2回目以降に実行するscanf関数では、引数の先頭に1文字を受け取る%cを記述し、その値を無視する……という形にしなければなりません。そのためには、cの前に代入抑止文字'*'を記述します。つまり、数値を1つ受け取って変数nに保存する処理では、1回目の呼び出しで
scanf("%d", &n);
としたあと、2回目以降は
scanf("%*c %d", &n);
のように記述しなければならないのです。

1回目と2回目以降の書き方が異なるため、繰り返し処理の構造が繁雑になります。一方gets関数は、入力終了を意味するCRコードまでを受け取って、CRコードの直前までを文字列として返すため、2回目以降にCRコードが残されることはありません。

そもそもキーボード入力向けではない

元々scanf関数は、キーボードからユーザーが逐一値を入力するような処理を前提としたものではありません。計測機器など複数のデータを連続してコンピュータに送ってくる装置からの入力を処理するための関数なのです。

例えば、投擲(とうてき=放り投げること)された物体の位置を0.1秒刻みで計測する機器があったとします。その機器がケーブルを介してコンピュータに送ってくる値は、以下のような内容だとします。
開始時刻(float型)
0.1秒後の到達距離(cm:int型)
0.2秒後の到達距離(cm:int型)
0.3秒後の到達距離(cm:int型)
0.4秒後の到達距離(cm:int型)
0.5秒後の到達距離(cm:int型)
終了時刻(float型)
1回の計測ごとに、これらの値がスペースで区切られて送られてくるならば、以下のような形でそれぞれの値を変数に一気に代入できます。
int pos[5];
float start, finish;
    :
scanf("%f %d %d %d %d %d %f",
       &start_t, pos[0], pos[1], pos[2], pos[3], pos[4], &finish);
一般的なプログラムでは、キーボードから複数の値がスペース区切りで入力されるということ自体、あまりありません。getsなど他の入力用関数を紹介していなかったため、これまではscanf関数を便宜的にキーボード入力に用いてきましたが、今後は状況に応じた実用的な関数を使っていきます。