C言語による学生情報管理システムの実装

構造体と動的メモリ管理を用いた学生データベース

ヘッダーファイルの設計(student.h)

本システムでは、#pragma once を使用して二重インクルードを防止しています。学生一人の情報を保持するStudent構造体と、複数の学生を管理するBook構造体を定義しています。

// student.h
#pragma once

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

// 各フィールドの最大長を定義
#define MAX_ID_LEN     20
#define MAX_NAME_LEN   20
#define MAX_AGE_LEN     5
#define MAX_SEX_LEN     5
#define MAX_CLASS_LEN  20
#define INIT_CAPACITY   1

// 学生データ構造体
typedef struct {
    char id[MAX_ID_LEN];
    char name[MAX_NAME_LEN];
    char age[MAX_AGE_LEN];
    char sex[MAX_SEX_LEN];
    char class_name[MAX_CLASS_LEN];
} Student;

// 管理用コンテナ構造体
typedef struct {
    Student* entries;
    int count;
    int capacity;
} StudentBook;

// 関数プロトタイプ宣言
void display_menu();
void initialize_book(StudentBook* book);
void load_from_file(StudentBook* book);
void save_to_file(const StudentBook* book);
void add_student(StudentBook* book);
void show_all_students(const StudentBook* book);
void release_memory(StudentBook* book);
void clear_screen();
void resize_if_needed(StudentBook* book);

機能実装(student.c)

主な処理は動的配列に基づいており、必要に応じてメモリを拡張します。ファイル操作にはバイナリモードを使用し、データの整合性を確保しています。

// student.c
#include "student.h"

void clear_screen() {
#ifdef _WIN32
    system("cls");
#else
    system("clear");
#endif
}

void display_menu() {
    printf("\n=====================================\n");
    printf("  1. 追加    2. 表示    0. 終了\n");
    printf("=====================================\n");
    printf("選択してください (0-2): ");
}

void resize_if_needed(StudentBook* book) {
    if (book->count >= book->capacity) {
        int new_cap = (book->capacity == 0) ? INIT_CAPACITY : book->capacity * 2;
        Student* temp = (Student*)realloc(book->entries, new_cap * sizeof(Student));
        
        if (!temp) {
            fprintf(stderr, "メモリ再割り当て失敗\n");
            return;
        }
        book->entries = temp;
        book->capacity = new_cap;
    }
}

void initialize_book(StudentBook* book) {
    book->count = 0;
    book->capacity = 0;
    book->entries = NULL;
    load_from_file(book);

    if (book->count == 0) {
        book->entries = (Student*)calloc(INIT_CAPACITY, sizeof(Student));
        if (!book->entries) {
            fprintf(stderr, "初期メモリ割り当て失敗\n");
            exit(EXIT_FAILURE);
        }
        book->capacity = INIT_CAPACITY;
    }
}

void load_from_file(StudentBook* book) {
    FILE* fp = fopen("Studentbook.txt", "rb");
    if (!fp) return;

    Student buffer;
    while (fread(&buffer, sizeof(Student), 1, fp)) {
        resize_if_needed(book);
        book->entries[book->count++] = buffer;
    }
    fclose(fp);
}

void save_to_file(const StudentBook* book) {
    FILE* fp = fopen("Studentbook.txt", "wb");
    if (!fp) {
        printf("ファイル書き込みエラー\n");
        return;
    }

    for (int i = 0; i < book->count; ++i) {
        fwrite(&book->entries[i], sizeof(Student), 1, fp);
    }
    fclose(fp);
}

void add_student(StudentBook* book) {
    resize_if_needed(book);
    Student* current = &book->entries[book->count];

    printf("学籍番号: "); scanf("%s", current->id);
    printf("氏名: ");     scanf("%s", current->name);
    printf("年齢: ");     scanf("%s", current->age);
    printf("性別: ");     scanf("%s", current->sex);
    printf("クラス: ");   scanf("%s", current->class_name);

    book->count++;
    printf("登録完了!\n");
}

void show_all_students(const StudentBook* book) {
    if (book->count == 0) {
        printf("データがありません。\n");
        return;
    }

    printf("\n%-18s %-14s %-6s %-7s %-20s\n", "学籍番号", "氏名", "年齢", "性別", "クラス");
    printf("------------------------------------------------------------\n");
    for (int i = 0; i < book->count; ++i) {
        const Student* s = &book->entries[i];
        printf("%-18s %-14s %-6s %-7s %-20s\n",
               s->id, s->name, s->age, s->sex, s->class_name);
    }
}

void release_memory(StudentBook* book) {
    free(book->entries);
    book->entries = NULL;
    book->count = 0;
    book->capacity = 0;
}

メイン制御ロジック(main.c)

列挙型を使用して操作コードを明確化し、ループによるメニュー駆動型インターフェースを実装しています。

// main.c
#include "student.h"

typedef enum {
    QUIT = 0,
    INSERT,
    DISPLAY
} Operation;

int main(void) {
    StudentBook registry;
    initialize_book(&registry);

    int choice;
    do {
        int proceed;
        printf("続行するには1を入力: ");
        if (scanf("%d", &proceed) != 1 || proceed != 1) {
            printf("無効な入力です。\n");
            break;
        }

        clear_screen();
        display_menu();

        if (scanf("%d", &choice) != 1) {
            printf("数値を入力してください。\n");
            while (getchar() != '\n');
            continue;
        }

        switch (choice) {
            case INSERT:
                clear_screen();
                add_student(&registry);
                break;
            case DISPLAY:
                clear_screen();
                show_all_students(&registry);
                break;
            case QUIT:
                save_to_file(&registry);
                printf("保存して終了しました。\n");
                break;
            default:
                printf("無効なオプションです。\n");
        }
    } while (choice != QUIT);

    release_memory(&registry);
    return 0;
}

実行例

続行するには1を入力: 1
=====================================
  1. 追加    2. 表示    0. 終了
=====================================
選択してください (0-2): 1
学籍番号: 1001
氏名: 山田太郎
年齢: 19
性別: 男
クラス: 情報工学A
登録完了!
続行するには1を入力: 1
学籍番号      氏名         年齢   性別    クラス              
------------------------------------------------------------
1001         山田太郎     19     男      情報工学A           

タグ: C言語 構造体 動的メモリ管理 ファイル入出力 バイナリファイル

6月7日 16:13 投稿