構造体と動的メモリ管理を用いた学生データベース
ヘッダーファイルの設計(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(®istry);
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(®istry);
break;
case DISPLAY:
clear_screen();
show_all_students(®istry);
break;
case QUIT:
save_to_file(®istry);
printf("保存して終了しました。\n");
break;
default:
printf("無効なオプションです。\n");
}
} while (choice != QUIT);
release_memory(®istry);
return 0;
}
実行例
続行するには1を入力: 1 ===================================== 1. 追加 2. 表示 0. 終了 ===================================== 選択してください (0-2): 1 学籍番号: 1001 氏名: 山田太郎 年齢: 19 性別: 男 クラス: 情報工学A 登録完了!
続行するには1を入力: 1 学籍番号 氏名 年齢 性別 クラス ------------------------------------------------------------ 1001 山田太郎 19 男 情報工学A