第32回
データ構造(11)~構造体を前後に移動できる双方向リンク

双方向リンクの仕組み~キー入力で前後に移動する

構造体のリンクができあがったら、次はキーボードからのキー入力に対処する処理に入ります。これは、キーボードから受け取った1文字をwhileループで判断し、それが'Q'または'q'でない間は処理を繰り返す──という形にします。

pとは別の変数を使う

現在処理対象としている構造体を示すには、先に利用した変数pを使っても構いません。が、異なる処理であることがわかるよう、ここではnowという別の変数を宣言して使っています。
  now = start; -------- 先頭のポインタを代入
  printf("ID\tName\tScore\n");
  printf("%04d\t%s\t%d\n", now->id, now->name, now->score);

gets関数で文字列を受け取る

キーボードからの入力は1文字なので、getchar関数を使うことも考えられます。しかし、getchar関数は1文字入力した後の改行コード(CR/LF)を含めてキーバッファに取り込んでしまうため、今回のループ処理では例えば[N][Enter]とキーを操作すると'B'の次に改行コードを2文字目として処理してしまいます。

それを避けるため、ここでは文字列を入力するgets関数でキーボードからの入力をchar型配列qansに受け取り、その先頭(ans[0])を処理するようにしています。
  printf("? ");
  gets(ans); -------- 文字列を受け取る

switch caseで分岐する

whileループ内ではswitch case文を使って、入力がBまたはbなら次の構造体へ、Nまたはnなら1つ前の構造体へ……と現在処理対象の構造体(nowで示す構造体)へ移動し、メンバをprintf関数で出力しています。
  while ((ans[0] != 'Q') && (ans[0] != 'q')) {
    switch (ans[0]) {
      case 'N' :
      case 'n' :
        if (now->next != NULL) {
        now = now->next;
        }
        break;
        case 'B' :
      case 'b' :
        if (now->prev != NULL) {
          now = now->prev;
        }
      defaulet : break;
    }
    printf("%04d\t%s\t%d\n", now->id, now->name, now->score);
    printf("? ");
    gets(ans);
  }

大文字/小文字で同じ処理を行う

switch case文では、受け取った1文字が大文字であっても小文字であっても(例えば'N'であっても'n'であっても)同じ処理を行います。この処理を正直に記述すると、以下のようになります。
    switch (ans[0]) {
      case 'N' :
        if (now->next != NULL) {
        now = now->next;
        }
        break;
      case 'n' :
        if (now->next != NULL) {
        now = now->next;
        }
        break;
          :

コードの無駄を省く

しかし、これでは同じ処理を行うコードを2回記述しなければならず、無駄が生じます。そこで、Cのswitch case文はbreak命令を記述しなければ続くコードを実行する──ということを利用して、以下のように簡略化しています。
    switch (ans[0]) {
      case 'N' :
      case 'n' :
        if (now->next != NULL) {
        now = now->next;
        }
        break;

このようにすることで、1文字が'N'であった場合はそのまま次の'n'であった場合の処理が実行されます。

あとがき

hiropの『ちょっと気になる専門用語』~《「定数」の読み方》

#defineプリプロセッサ指令で、定数に名前を付けることができます。これには、ソース中に突然何らかの『値』が登場しては意味がわかりにくくなることと、後の修正で複数箇所を書き換えることによって生じるバグを防ぐ効果があります。

#define __MAXCOUNT 1000
のようにしておけば「1000」という数値の意味が明確になり、さらにこの行を書き直すだけでソース中に記述された__MAXCOUNTの値が一斉に変更でき、ソースのあちこちを書き直す手間を省けます。

このような『処理の中で一切変更されない値』を『定数』と呼びますが、みなさんはこれをどう読んでいますか?

一般には「ていすう」と読む人が多いようです。K&R──カーニハンとリッチーの著したC言語の定番教科書「プログラミング言語C」の日本語版(共立出版)でも、索引で「定数」は「て」の項に分類されています。

しかし、「ていすう」と発音すると「衆議院定数」のように「一定の大きさの入れ物の容量」を示す意味合いが強くなります。『定数』は元々『常数』と書いて「常に一定の値を保っている数」のことを示すので、僕はその読みにならって「じょうすう」と読んでいます。

システム関係の仕事では「ていすう」と読んでも「じょうすう」と読んでも大きな問題は生じませんが、実務関係の仕事では「会議室の席数」や「議会の定数」などなど、「ていすう」という言葉が別の意味を示すことも多いので、意図的に使い分けています。

ただ、「じょうすう」と読むと「乗数」と間違われることもあります。これは、会話の文脈で判断すべきことなのかもしれません。みなさんがどちらの「読み」を使っているか、アンケートを取ってみたいですね。