プログラミング

C言語超入門⑨ 成績管理プログラムを作ろう

変数、入力、条件分岐、繰り返し、関数、配列、ポインタ、構造体を組み合わせて小さなプログラムを完成させます。

最終回では、これまでに学んだ内容を組み合わせて、複数人の名前と点数を入力する成績管理プログラムを作ります。

この回で作るもの

  • 1人から5人までの人数を入力する
  • それぞれの名前と点数を入力する
  • 点数から合格・不合格を判定する
  • 最後に平均点を表示する

完成コード

#include <stdio.h>

#define MAX_STUDENTS 5

typedef struct {
    char name[50];
    int score;
} Student;

void showResult(const Student *student) {
    printf("%s: %d点 ", student->name, student->score);

    if (student->score >= 60) {
        printf("合格\n");
    } else {
        printf("不合格\n");
    }
}

int main(void) {
    Student students[MAX_STUDENTS];
    int count;
    int total = 0;

    do {
        printf("人数を入力してください(1〜%d): ", MAX_STUDENTS);
        scanf("%d", &count);
    } while (count < 1 || count > MAX_STUDENTS);

    for (int i = 0; i < count; i++) {
        printf("%d人目の名前(空白なし): ", i + 1);
        scanf("%49s", students[i].name);

        do {
            printf("点数(0〜100): ");
            scanf("%d", &students[i].score);
        } while (students[i].score < 0 || students[i].score > 100);

        total += students[i].score;
    }

    printf("\n--- 結果 ---\n");
    for (int i = 0; i < count; i++) {
        showResult(&students[i]);
    }

    printf("平均: %.1f点\n", (double)total / count);
    return 0;
}

コードの流れを確認する

大きく分けると、次の順番で動きます。

  1. Student構造体と配列を用意する
  2. do-while文で正しい人数が入力されるまで待つ
  3. for文で名前と点数を順番に入力する
  4. showResult関数へ構造体のアドレスを渡す
  5. if文で合否を表示する
  6. 合計を人数で割り、平均を表示する

#defineの意味

#define MAX_STUDENTS 5

#defineは、コード中の名前を決めた内容へ置き換える機能です。上限を変えたいときに、5と書かれた場所を何か所も探さずに済みます。

%49sとしている理由

nameは50文字分の配列です。%49sと書くことで、終わりを示す\0の1文字分を残し、最大49文字まで受け取ります。

ただし、この入力方法では空白を含む名前は入力できません。空白を含めたい場合は、第6回で扱ったfgetsを使い、入力欄に残った改行を処理する必要があります。

(double)で型を変える

(double)total / count

int型のtotalを一時的にdouble型へ変えています。このように型を変換することをキャストと呼びます。これにより平均の小数部分も計算できます。

改良してみよう

  • 最高点と最低点を表示する
  • 80点以上を「優」、60点以上を「可」と表示する
  • メニューを作り、入力と表示を選べるようにする
  • 名前に空白を含められるようにする

一度に全部を追加せず、1つ変更するたびにコンパイルして動きを確かめるのがおすすめです。

模範解答

4つの改良をまとめて反映した例です。

#include <stdio.h>
#include <string.h>

#define MAX_STUDENTS 5

typedef struct {
    char name[50];
    int score;
} Student;

void clearInputBuffer(void) {
    int character;
    while ((character = getchar()) != '\n' && character != EOF) {
    }
}

void readLine(char text[], size_t size) {
    fgets(text, size, stdin);
    text[strcspn(text, "\n")] = '\0';
}

Student inputStudent(void) {
    Student student;

    printf("名前: ");
    readLine(student.name, sizeof(student.name));

    do {
        printf("点数(0〜100): ");
        scanf("%d", &student.score);
        clearInputBuffer();
    } while (student.score < 0 || student.score > 100);

    return student;
}

void showResults(const Student students[], int count) {
    if (count == 0) {
        printf("まだデータがありません。\n");
        return;
    }

    int total = 0;
    int highest = students[0].score;
    int lowest = students[0].score;

    for (int i = 0; i < count; i++) {
        int score = students[i].score;
        total += score;

        if (score > highest) {
            highest = score;
        }
        if (score < lowest) {
            lowest = score;
        }

        printf("%s: %d点 ", students[i].name, score);
        if (score >= 80) {
            printf("優\n");
        } else if (score >= 60) {
            printf("可\n");
        } else {
            printf("不合格\n");
        }
    }

    printf("最高点: %d点\n", highest);
    printf("最低点: %d点\n", lowest);
    printf("平均: %.1f点\n", (double)total / count);
}

int main(void) {
    Student students[MAX_STUDENTS];
    int count = 0;
    int menu = -1;

    while (menu != 0) {
        printf("\n1: 入力  2: 表示  0: 終了\n");
        printf("選択: ");
        scanf("%d", &menu);
        clearInputBuffer();

        if (menu == 1) {
            if (count < MAX_STUDENTS) {
                students[count] = inputStudent();
                count++;
            } else {
                printf("これ以上追加できません。\n");
            }
        } else if (menu == 2) {
            showResults(students, count);
        } else if (menu != 0) {
            printf("0〜2を選んでください。\n");
        }
    }

    return 0;
}

解説

highestとlowestを最初の学生の点数で初期化し、より高い点・低い点が見つかるたびに更新します。80点以上、60点以上の順番で判定すると、「優」「可」「不合格」の3段階に分けられます。

fgetsを使うreadLine関数は空白を含む名前を1行で受け取ります。strcspnで入力末尾の改行を探し、\0へ置き換えています。メニューをwhile文で囲んでいるため、0を選ぶまで入力と表示を繰り返せます。

シリーズのまとめ

9回を通して、変数、条件分岐、繰り返し、関数、配列、文字列、ポインタ、構造体を使いました。

ここまで読めたら、短いC言語のコードを読み、自分で小さなプログラムを作るための土台はできています。次は、練習問題を作って少しずつ機能を増やしてみましょう。

第1回から読み直す / C言語の記事一覧 / プログラミング基礎の記事一覧

← 記事一覧へ戻る