こんにちは、コン(@pippi_kon)です。
この記事では『C言語でのクイズゲームの作り方』をご紹介しています。
前回は、構造体を使用してプログラムを見やすくする方法をご紹介しました。
変数名(構造体のメンバー名)に意味を持たせることができ、可読性がアップしました。
しかし、構造体に登録する処理、具体的には「strncpy関数の発行」が多くなってしまい、その点ではプログラムが少々見づらくなってしまいました。
なので今回は、構造体にデータを登録する関数を作成して可読性をアップさせます。
今回の目標
今回作成するプログラムの出力結果です。
出力結果自体は前回と変わりませんが、中身のプログラム処理が変わります。
問題文の「リンゴは英語で何と言う?」や選択肢の「apple」などを、メイン関数内で愚直に構造体に登録するのではなく、関数で行うように変更しています。
プログラム全文
今回作成したプログラムのご紹介です。
のちほど、プログラムの解説を行います。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
typedef struct {
char mondai[100];
char sentaku[3][100];
int answer_no;
}quiz_t;
void set_quiz(quiz_t* t, char* m, char* s1, char* s2, char* s3, int a)
{
strncpy(t->mondai, m, sizeof(t->mondai));
strncpy(t->sentaku[0], s1, sizeof(t->sentaku[0]));
strncpy(t->sentaku[1], s2, sizeof(t->sentaku[1]));
strncpy(t->sentaku[2], s3, sizeof(t->sentaku[2]));
t->answer_no = a;
}
int main(void)
{
int ans;
int i;
quiz_t quiz[3];
set_quiz(&quiz[0], "リンゴは英語で何と言う?", "apple", "orange", "banana", 1);
set_quiz(&quiz[1], "大正->昭和->○○->令和\n○○に入る年号は?", "慶応", "明治", "平成", 3);
set_quiz(&quiz[2], "世界三大珍味はどれ?", "イカスミ", "キャビア", "チーズ", 2);
for (i = 0; i < 3; i++) {
printf("[第%d問]\n", i + 1);
printf("%s\n", quiz[i].mondai);
printf("1:%s 2:%s 3:%s\n", quiz[i].sentaku[0], quiz[i].sentaku[1], quiz[i].sentaku[2]);
printf(">>> ");
scanf("%d", &ans);
if (ans == quiz[i].answer_no) {
printf("正解!\n\n");
}
else {
printf("不正解...\n");
printf("正解は %d:%s です。\n\n", quiz[i].answer_no, quiz[i].sentaku[quiz[i].answer_no - 1]);
}
}
return 0;
}
プログラムの解説
構造体登録関数の作成
void set_quiz(quiz_t* t, char* m, char* s1, char* s2, char* s3, int a)
{
strncpy(t->mondai, m, sizeof(t->mondai));
strncpy(t->sentaku[0], s1, sizeof(t->sentaku[0]));
strncpy(t->sentaku[1], s2, sizeof(t->sentaku[1]));
strncpy(t->sentaku[2], s3, sizeof(t->sentaku[2]));
t->answer_no = a;
}
構造体の各メンバーにデータを設定する関数「set_quiz」を作成しました。
引数の意味は以下の通り。
引数 | 意味 |
---|---|
quiz_t* t | データ設定する構造体 |
char* m | 問題文 |
char* s1 | 選択肢1 |
char* s2 | 選択肢2 |
char* s3 | 選択肢3 |
int a | 答えの番号 |
第1引数「quiz_t* t」で指定した構造体に、第2~6引数で指定した値を設定します。
前回と同様に、問題文・選択肢はstrncpy関数を使用し、答えの番号は直接代入しますが、構造体メンバーの指定方法が異なります。
前回は『quiz[0].mondai』のように構造体名とメンバー名を「.(ドット)」でつなげていました。
これは、メイン関数にある構造体の実体をそのまま参照していたため。
構造体の実体は以下の箇所で宣言しています。
quiz_t quiz[3];
しかし、今回はメイン関数ではなくサブ関数(set_quiz)から構造体を参照することになります。
サブ関数に実体を渡して、データ設定して、実体をメイン関数に返す、という方法もありますが、処理がちょっと複雑になってしまいます。
そのため、実体ではなく構造体の場所(アドレス)を渡し、サブ関数から直接構造体にデータ設定します。
アドレス参照する際は、『t->mondai』のようにドットではなく矢印(->)でつなげます。
構造体登録関数の呼び出し
set_quiz(&quiz[0], "リンゴは英語で何と言う?", "apple", "orange", "banana", 1);
set_quiz(&quiz[1], "大正->昭和->○○->令和\n○○に入る年号は?", "慶応", "明治", "平成", 3);
set_quiz(&quiz[2], "世界三大珍味はどれ?", "イカスミ", "キャビア", "チーズ", 2);
作成した関数を使用して構造体にデータを登録します。
一つ目の構造体(quiz[0])に登録するときの引数の値は以下の通り。
引数 | 値 |
---|---|
quiz_t* t | &quiz[0] |
char* m | "リンゴは英語で何と言う?" |
char* s1 | "apple" |
char* s2 | "orange" |
char* s3 | "banana" |
int a | 1 |
ここで重要なのが第一引数の指定方法です。
第一引数にはデータを設定する構造体を指定しますが、そのまま指定するのではなく、「&」を付けてアドレス参照形式で指定します。