Skip to main content

付録 今回の補足

付録 C言語の仕組みとゲームプログラムの考え方

この付録では、第2回と第3回の途中で出てくる、少し引っかかりやすいC言語の仕組みをまとめます。

ゲームを完成させるために、すべてを暗記する必要はありません。

コードの意味が分からなくなったときに、このページへ戻って確認してください。


第2回で出てくるC言語の仕組み

1.文字と文字列は別のもの

C言語では、文字文字列は別のものです。

char c = '3';
char text[] = "3";

見た目はどちらも数字の3ですが、メモリの中身は異なります。

書き方 意味 メモリの中身
'3' 1文字 文字コードを表す1つの値
"3" 文字列 '3'と終端文字'\0'の並び
"1234" 文字列 '1''2''3''4''\0'

1文字は、シングルクォーテーションで囲みます。

char number = '3';

文字列は、ダブルクォーテーションで囲みます。

char number[] = "3";

C言語には、文字列専用の特別な型がありません。

文字列は、char型の配列に文字を順番に並べ、最後に終端文字'\0'を入れたものです。

文字列 "3"

添え字       0      1
配列の中身  '3'   '\0'

4文字の文字列を保存するとき、配列は最低5要素必要です。
最後の1要素を終端文字'\0'に使用します。

char input[5];

2.文字を==で比較できる理由

次のコードでは、2つの文字を==で比較しています。

if (input[i] == input[j])
{
    return 3;
}

文字は、コンピュータの中では文字コードという整数値で扱われます。

今回使用しているVisual Studioの環境では、基本的な英数字はASCIIコードに対応した値になります。

文字 ASCIIコード
'0' 48
'1' 49
'2' 50
'3' 51
'A' 65

例えば、次の比較は、内部では同じ整数値かどうかを調べています。

'3' == '3'
51 == 51

値が同じなので、比較結果は真になります。

input[i] == input[j]

も、2つの配列要素に保存された文字コードが同じかどうかを調べています。

文字列全体は==で比較しない

1文字どうしは==で比較できますが、文字列全体の比較には==を使いません。

if (input == answer)    /* 文字列の内容比較には使わない */
{
}

文字列の内容を比較するときは、strcmp関数を使います。

#include <string.h>

if (strcmp(input, answer) == 0)
{
    printf("正解です。\n");
}

strcmpの戻り値が0なら、2つの文字列の内容が同じです。

(自分で配列の中の文字列がすべて一致してるかどうか調べれば、strcmpを使わなくても比較できるよ。まぁあるものは使おうって話ではあるけど)

正確には、C言語の規格がすべての環境へASCIIコードの使用を強制しているわけではありません。
ただし、今回使用するVisual Studio環境の基本的な英数字はASCIIコードに対応しています。


3.関数の戻り値とは

関数は処理を実行したあと、呼び出した場所へ1つの値を返すことができます。

この値を戻り値といいます。

例えば、入力内容を確認する関数を次のように作れます。

int CheckInput(char input[])
{
    if (strlen(input) != 4)
    {
        return 1;
    }

    /* 数字以外が含まれているか調べる */
    /* 問題があれば return 2; */

    /* 同じ数字が含まれているか調べる */
    /* 問題があれば return 3; */

    return 0;
}

この関数では、戻り値に意味を持たせています。

戻り値 意味
0 正しい入力
1 4桁ではない
2 数字以外が含まれている
3 同じ数字が含まれている

呼び出す側では、戻り値を変数へ受け取ります。

int result;

result = CheckInput(input);

戻り値を調べることで、入力のどこに問題があったのか判断できます。

if (result == 1)
{
    printf("4桁で入力してください。\n");
}
else if (result == 2)
{
    printf("数字だけを入力してください。\n");
}
else if (result == 3)
{
    printf("同じ数字を2回使わないでください。\n");
}

returnが実行されると関数はその場で終わる

次のコードで重複を見つけた場合、return 3;が実行されます。

if (input[i] == input[j])
{
    return 3;
}

returnが実行されると、その関数はその場で終了します。

その後に書かれている比較処理は実行されず、呼び出し元へ3が返ります。

戻り値がない関数

呼び出し元へ値を返さない関数は、戻り値の型をvoidにします。

void MakeProblem(char answer[])
{
    /* 問題を作る */
}

この関数は、問題を作る処理を行いますが、戻り値は返しません。

main関数の戻り値

return 0;

main関数の戻り値は、プログラムを呼び出したOSへ返されます。

一般に0は、プログラムが正常に終了したことを表します。


4..c.cppでは何が違うのか

C言語のプログラムを.cppで保存しても、今回程度のコードであればコンパイルして動くことがあります。

main.c    → C言語としてコンパイル
main.cpp  → C++としてコンパイル

Visual Studioでは、主にファイルの拡張子から使用する言語モードを判断します。

拡張子 扱われる言語
.c C言語
.cpp C++

.cppでも動くのは、今回書いているC言語のコードの多くが、C++でも使用できる書き方だからです。

ただし、.cppで保存した時点で、C言語としてではなくC++としてコンパイルされます

Visual Studioでは同じcl.exeが使われる場合でも、拡張子によってCモードとC++モードが切り替わります。

CとC++は似ていますが、完全に同じ言語ではありません。

  • Cでは動くがC++ではエラーになるコードがある
  • C++だけで使用できる機能がある
  • エラーの判定や型の扱いが異なる場合がある

C言語の授業では、C言語として動作を確認するために.cで保存してください。

.cppで動いたからといって、C言語としてコンパイルされたとは限りません。


第3回で出てくるゲームプログラムの考え方

5.一度で終わるプログラム

C言語の最初の授業では、次のようなプログラムをよく作ります。

#include <stdio.h>

int main(void)
{
    int a;
    int b;

    scanf("%d", &a);
    scanf("%d", &b);

    printf("%d\n", a + b);
    return 0;
}

このプログラムは、次の順番で1回だけ動きます。

入力する
  ↓
計算する
  ↓
結果を表示する
  ↓
終了する

結果を1回表示したら、プログラムは終わります。


6.ゲームは処理を繰り返す

ゲームは、結果を1回表示しただけでは終わりません。

プレイヤーが遊んでいる間、何度も処理を繰り返します。

HIT&BLOWでは、正解するまで次の処理を繰り返します。

入力する
  ↓
HITとBLOWを判定する
  ↓
結果を表示する
  ↓
正解でなければ、もう一度入力する

C言語では、次のように書けます。

while (1)
{
    /* 4桁を入力する */
    /* HITとBLOWを数える */
    /* 結果を表示する */

    if (hit == 4)
    {
        break;
    }
}

7.無限ループはゲームの繰り返し処理のもとになる

while (1)
{
}

条件の1は常に真なので、このwhile文は何度でも繰り返します。

このような処理を無限ループといいます。

「無限」と聞くと、間違ったプログラムのように感じるかもしれません。

しかし、終了条件を用意して使う無限ループは、ゲームでよく使われる考え方です。

if (hit == 4)
{
    break;
}

breakを実行すると、繰り返し処理を終了できます。

リアルタイムゲームのフレーム処理

リアルタイムゲームでは、一般に次の処理を繰り返します。

while (gameRunning)
{
    Input();
    Update();
    Draw();
}
入力を調べる
  ↓
ゲームの状態を更新する
  ↓
画面を描画する
  ↓
次のフレームへ

この1回分の処理を、ゲーム中に何度も繰り返します。

HIT&BLOWはscanfでプレイヤーの入力を待つため、リアルタイムゲームのフレーム処理そのものではありません。

しかし、ゲームが終了するまで入力・判定・表示を繰り返すという基本構造は共通しています。

無限ループには、必ず終了する方法を用意します。
終了条件がなければ、意図せずプログラムが終わらなくなります。


8.入力を促す文字列をプロンプトという

次の表示は、プレイヤーへ入力を促しています。

printf("重複しない4桁の数字を入力してください:");

このように、ユーザーへ入力を求めるために表示する文字列をプロンプトといいます。

次の表示だけでは、何を入力すればよいか分かりません。

>

入力の条件を具体的に書くと、操作方法が分かりやすくなります。

重複しない4桁の数字を入力してください:1234

良いプロンプトには、次の情報を含めます。

  • 何を入力するのか
  • どのような形式で入力するのか
  • 使用できる文字や数値の範囲
  • 入力を間違えた場合、次に何をすればよいか

例えば、入力エラーの後にも案内を表示します。

printf("同じ数字が含まれています。\n");
printf("重複しない4桁の数字を入力してください:");

コンソール画面にもUIがある

UIというと、ボタンや画像がある画面を想像しやすいですが、コンソール画面にもUIがあります。

  • メニューを表示する
  • 入力を促す
  • 入力例を表示する
  • エラーの原因を説明する
  • 次に行う操作を伝える

これらはすべて、ユーザーがプログラムを操作するためのUIです。

プロンプトは、文字だけで作る最も基本的なUIの一つです。


9.戻り値・無限ループ・プロンプトを組み合わせる

入力処理では、ここまで説明した仕組みを組み合わせられます。

int result;

while (1)
{
    printf("重複しない4桁の数字を入力してください:");
    scanf("%254s", input);

    result = CheckInput(input);

    if (result == 0)
    {
        break;
    }
    else if (result == 1)
    {
        printf("4桁で入力してください。\n");
    }
    else if (result == 2)
    {
        printf("数字だけを入力してください。\n");
    }
    else if (result == 3)
    {
        printf("同じ数字を2回使わないでください。\n");
    }
}

処理の流れは次のようになります。

プロンプトを表示する
  ↓
プレイヤーが入力する
  ↓
CheckInput関数が入力を調べる
  ↓
戻り値が0なら入力完了
  ↓
エラーなら理由を表示して、もう一度入力する

この短い処理の中に、次の考え方が入っています。

  • プロンプトによる入力案内
  • 関数の戻り値
  • if文による分岐
  • while文による繰り返し
  • breakによるループの終了

今回のまとめ

  • 文字と文字列は別のもの
  • 文字は文字コードという整数値として扱われる
  • 1文字どうしは==で比較できる
  • 文字列全体の比較にはstrcmpを使う
  • 関数は戻り値を1つ返せる
  • returnが実行されると、その場で関数が終了する
  • .cはC言語、.cppはC++としてコンパイルされる
  • .cppでも動く場合はあるが、C言語として動いているとは限らない
  • ゲームは終了するまで処理を繰り返す
  • 無限ループはゲームループの基本的な考え方につながる
  • 入力を促す文字列をプロンプトという
  • コンソールの文字表示もUIの一部