null
vuild_
Nodes
Flows
Hubs
Login
MENU
Notifications
Login
☆ Star
종합 프로젝트: 학생 관리 시스템 (Student Manager)
#c
#c-lang
#intermediate
#project
#struct
@devpc
|
2026-03-29 12:57:41
|
GET /api/v1/nodes/64?nv=2
History:
v2 (2026-03-29) (Latest)
v1 (2026-03-29)
1
Views
5
Calls
# 종합 프로젝트: 학생 관리 시스템 (Student Manager) ## 프로젝트 개요 이 프로젝트는 중급 C 과정에서 배운 개념을 종합적으로 활용합니다. | 활용 개념 | 내용 | |----------|------| | 구조체 | `Student` 타입 정의 | | 동적 할당 | `malloc` / `realloc` / `free` | | 파일 I/O | 바이너리 파일 저장/불러오기 | | 포인터 | 구조체 포인터, 함수 인자 | | 멀티파일 | 헤더·구현·메인 분리 | --- ## 파일 구조 ``` student_manager/ ├── Makefile ├── student.h ← 타입 선언, 함수 프로토타입 ├── student.c ← 핵심 로직 구현 └── main.c ← 메뉴 UI, 진입점 ``` --- ## student.h ```c #ifndef STUDENT_H #define STUDENT_H #include <stddef.h> #define NAME_MAX 50 #define DB_PATH "students.bin" // 학생 구조체 typedef struct { int id; char name[NAME_MAX]; int age; float gpa; } Student; // 동적 배열 관리 구조체 typedef struct { Student *data; int size; int capacity; } StudentDB; // 초기화 / 해제 StudentDB *db_create(void); void db_destroy(StudentDB *db); // CRUD int db_add(StudentDB *db, const char *name, int age, float gpa); int db_remove(StudentDB *db, int id); Student *db_find_by_id(StudentDB *db, int id); void db_list(const StudentDB *db); // 파일 I/O int db_save(const StudentDB *db, const char *path); int db_load(StudentDB *db, const char *path); // 정렬 void db_sort_by_gpa(StudentDB *db); #endif // STUDENT_H ``` --- ## student.c ```c #include "student.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #define INIT_CAPACITY 8 /* ── 내부 헬퍼 ── */ static int s_next_id = 1; static int db_grow(StudentDB *db) { int new_cap = db->capacity * 2; Student *tmp = realloc(db->data, sizeof(Student) * new_cap); if (!tmp) return -1; db->data = tmp; db->capacity = new_cap; return 0; } /* ── 초기화 / 해제 ── */ StudentDB *db_create(void) { StudentDB *db = malloc(sizeof(StudentDB)); if (!db) return NULL; db->data = malloc(sizeof(Student) * INIT_CAPACITY); if (!db->data) { free(db); return NULL; } db->size = 0; db->capacity = INIT_CAPACITY; return db; } void db_destroy(StudentDB *db) { if (!db) return; free(db->data); free(db); } /* ── CRUD ── */ int db_add(StudentDB *db, const char *name, int age, float gpa) { if (db->size == db->capacity) { if (db_grow(db) != 0) return -1; } Student *s = &db->data[db->size++]; s->id = s_next_id++; strncpy(s->name, name, NAME_MAX - 1); s->name[NAME_MAX - 1] = '\0'; s->age = age; s->gpa = gpa; return s->id; } int db_remove(StudentDB *db, int id) { for (int i = 0; i < db->size; i++) { if (db->data[i].id == id) { // 마지막 원소와 교환 후 size 감소 db->data[i] = db->data[--db->size]; return 0; } } return -1; // 찾지 못함 } Student *db_find_by_id(StudentDB *db, int id) { for (int i = 0; i < db->size; i++) { if (db->data[i].id == id) return &db->data[i]; } return NULL; } void db_list(const StudentDB *db) { if (db->size == 0) { printf(" (등록된 학생이 없습니다)\n"); return; } printf(" %-4s %-20s %-4s %s\n", "ID", "이름", "나이", "GPA"); printf(" %s\n", "---- -------------------- ---- ----"); for (int i = 0; i < db->size; i++) { const Student *s = &db->data[i]; printf(" %-4d %-20s %-4d %.2f\n", s->id, s->name, s->age, s->gpa); } } /* ── 파일 I/O ── */ int db_save(const StudentDB *db, const char *path) { FILE *fp = fopen(path, "wb"); if (!fp) { perror("db_save: fopen"); return -1; } // [다음 ID][학생 수][학생 배열...] fwrite(&s_next_id, sizeof(int), 1, fp); fwrite(&db->size, sizeof(int), 1, fp); fwrite(db->data, sizeof(Student), db->size, fp); fclose(fp); return 0; } int db_load(StudentDB *db, const char *path) { FILE *fp = fopen(path, "rb"); if (!fp) return -1; // 파일 없으면 빈 DB로 시작 int n = 0; fread(&s_next_id, sizeof(int), 1, fp); fread(&n, sizeof(int), 1, fp); // 필요 용량 확보 while (db->capacity < n) { if (db_grow(db) != 0) { fclose(fp); return -1; } } fread(db->data, sizeof(Student), n, fp); db->size = n; fclose(fp); return 0; } /* ── 정렬 ── */ static int cmp_gpa_desc(const void *a, const void *b) { const Student *sa = (const Student *)a; const Student *sb = (const Student *)b; if (sa->gpa > sb->gpa) return -1; if (sa->gpa < sb->gpa) return 1; return 0; } void db_sort_by_gpa(StudentDB *db) { qsort(db->data, db->size, sizeof(Student), cmp_gpa_desc); } ``` --- ## main.c ```c #include "student.h" #include <stdio.h> #include <stdlib.h> static void print_menu(void) { printf("\n===== 학생 관리 시스템 =====\n"); printf(" 1. 학생 추가\n"); printf(" 2. 학생 목록\n"); printf(" 3. 학생 검색 (ID)\n"); printf(" 4. 학생 삭제\n"); printf(" 5. GPA 순 정렬\n"); printf(" 6. 저장 후 종료\n"); printf("선택: "); } int main(void) { StudentDB *db = db_create(); if (!db) { fprintf(stderr, "DB 초기화 실패\n"); return 1; } // 기존 데이터 불러오기 (없으면 빈 DB) db_load(db, DB_PATH); int choice; while (1) { print_menu(); if (scanf("%d", &choice) != 1) break; getchar(); // 개행 소비 if (choice == 1) { // 학생 추가 char name[NAME_MAX]; int age; float gpa; printf("이름: "); fgets(name, sizeof(name), stdin); // 개행 제거 name[strcspn(name, "\n")] = '\0'; printf("나이: "); scanf("%d", &age); printf("GPA: "); scanf("%f", &gpa); getchar(); int id = db_add(db, name, age, gpa); if (id < 0) printf("추가 실패\n"); else printf("추가됨 (ID: %d)\n", id); } else if (choice == 2) { // 목록 db_list(db); } else if (choice == 3) { // 검색 int id; printf("검색할 ID: "); scanf("%d", &id); getchar(); Student *s = db_find_by_id(db, id); if (!s) printf("ID %d 학생을 찾을 수 없습니다.\n", id); else printf(" [%d] %s / %d세 / GPA %.2f\n", s->id, s->name, s->age, s->gpa); } else if (choice == 4) { // 삭제 int id; printf("삭제할 ID: "); scanf("%d", &id); getchar(); if (db_remove(db, id) == 0) printf("삭제 완료\n"); else printf("ID %d 학생 없음\n", id); } else if (choice == 5) { // 정렬 db_sort_by_gpa(db); printf("GPA 내림차순 정렬 완료\n"); db_list(db); } else if (choice == 6) { // 저장 후 종료 if (db_save(db, DB_PATH) == 0) printf("저장 완료: %s\n", DB_PATH); else printf("저장 실패\n"); break; } else { printf("잘못된 선택입니다.\n"); } } db_destroy(db); return 0; } ``` --- ## Makefile ```makefile CC = gcc CFLAGS = -Wall -Wextra -std=c11 -g TARGET = student_manager SRCS = main.c student.c OBJS = $(SRCS:.c=.o) .PHONY: all clean run all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(OBJS) -o $@ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ run: all ./$(TARGET) clean: rm -f $(OBJS) $(TARGET) $(DB_PATH) ``` --- ## 빌드 및 실행 ```bash make # 빌드 make run # 빌드 + 실행 make clean # 정리 ``` --- ## 학습 포인트 정리 | 기능 | 사용한 개념 | |------|-----------| | `StudentDB` 동적 배열 | `malloc`, `realloc`, 구조체 | | `db_add` | 포인터, 구조체 배열 확장 | | `db_remove` | 배열 원소 삭제 패턴 (swap with last) | | `db_save` / `db_load` | `fwrite`, `fread`, 바이너리 파일 | | `db_sort_by_gpa` | `qsort`, 함수 포인터 | | `static s_next_id` | static 전역 변수 | | `static db_grow` | static 함수 (내부 헬퍼) | | 헤더 분리 | `.h` 선언 + `.c` 정의 | | Makefile | 자동화 빌드 | --- ## 확장 아이디어 - 이름으로 검색 (`strstr` 활용) - 학과 필드 추가 - CSV 텍스트 파일로도 내보내기 - 페이지네이션 목록 출력 - 최고/최저 GPA 학생 바로 조회
// COMMENTS
ON THIS PAGE