C语言课程设计:课程管理系统
项目概述与需求分析
1 项目背景 在高校教学管理中,课程信息的管理是一项基础且重要的工作,为了简化教务人员的工作,提高管理效率,开发一个基于C语言的命令行课程管理系统,用于实现对课程信息的增、删、改、查等基本操作,具有很高的实用价值。

2 系统目标
- 信息录入:能够将新课程的信息(如课程编号、课程名称、授课教师、学分、上课时间等)添加到系统中。
- 信息查询:能够按照不同的条件(如课程编号、课程名称)查询课程信息,并显示查询结果。
- 信息修改:能够根据课程编号,查找到对应课程并修改其信息(除课程编号外)。
- 信息删除:能够根据课程编号,从系统中删除指定的课程。
- 数据持久化:系统能够将课程信息保存在文件中,以便在程序关闭后再次启动时可以加载之前的数据。
- 界面友好:提供清晰、简洁的菜单驱动界面,方便用户操作。
3 功能模块划分 根据需求分析,系统可以划分为以下几个核心模块:
- 主菜单模块:提供所有功能的入口。
- 课程添加模块:实现“增加”功能。
- 课程查询模块:实现“查询”功能,可细分为按ID查询和按名称查询。
- 课程修改模块:实现“修改”功能。
- 课程删除模块:实现“删除”功能。
- 数据存储与加载模块:实现从文件读取数据和向文件写入数据。
- 显示所有课程模块:以表格形式列出系统中所有课程信息。
系统设计
1 数据结构设计
为了存储课程信息,我们定义一个结构体 Course,为了管理所有课程,使用一个结构体数组 courses,并通过一个整型变量 count 来记录当前系统中课程的总数。
// 课程信息结构体
typedef struct {
char id[20]; // 课程编号 (唯一标识)
char name[50]; // 课程名称
char teacher[30]; // 授课教师
int credit; // 学分
char time[30]; // 上课时间 ( 周一 8:00-10:00)
} Course;
// 课程管理系统结构体
typedef struct {
Course courses[100]; // 假设最多存储100门课程
int count; // 当前课程数量
} CourseSystem;
2 功能函数设计
| 函数名 | 功能描述 | 参数 | 返回值 |
| :--- | :--- | :--- | :--- |
| main | 程序入口,显示主菜单,调用其他功能函数 | 无 | 无 |
| initSystem | 初始化系统,尝试从文件加载数据 | CourseSystem *sys | 无 |
| saveToFile | 将当前系统中的所有课程信息保存到文件 | CourseSystem *sys | 无 |
| showMenu | 显示系统主菜单 | 无 | 无 |
| addCourse | 添加一门新课程 | CourseSystem *sys | 无 |
| findCourseById | 按课程编号查询课程 | CourseSystem *sys, char *id | 查找到的课程在数组中的索引,未找到返回-1 |
| findCourseByName | 按课程名称查询课程 | CourseSystem *sys, char *name | 查找到的课程在数组中的索引,未找到返回-1 |
| queryCourse | 提供查询子菜单,调用具体查询函数 | CourseSystem *sys | 无 |
| modifyCourse | 修改指定课程的信息 | CourseSystem *sys | 无 |
| deleteCourse | 删除指定编号的课程 | CourseSystem *sys | 无 |
| displayAllCourses | 以表格形式显示所有课程信息 | CourseSystem *sys | 无 |
| pauseAndClear | 暂停屏幕并清屏,提升用户体验 | 无 | 无 |
3 文件设计
使用一个文本文件 courses.dat 来持久化存储数据。
- 文件格式:每行存储一门课程的信息,字段之间用逗号()或制表符(
\t)分隔。 - :
CS101,程序设计基础,张三,4,周一 8:00-10:00 MATH201,高等数学,李四,5,周二 10:00-12:00 ENG102,大学英语,王五,3,周三 14:00-16:00
核心代码实现
下面是各个功能模块的C语言代码实现。
1 头文件 (course_system.h)
#ifndef COURSE_SYSTEM_H
#define COURSE_SYSTEM_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h> // 用于 getch(),Windows下有效,Linux/Mac可用其他方式替代
// 课程信息结构体
typedef struct {
char id[20];
char name[50];
char teacher[30];
int credit;
char time[30];
} Course;
// 课程管理系统结构体
typedef struct {
Course courses[100];
int count;
} CourseSystem;
// 函数声明
void initSystem(CourseSystem *sys);
void saveToFile(CourseSystem *sys);
void showMenu();
void addCourse(CourseSystem *sys);
void queryCourse(CourseSystem *sys);
void modifyCourse(CourseSystem *sys);
void deleteCourse(CourseSystem *sys);
void displayAllCourses(CourseSystem *sys);
int findCourseById(CourseSystem *sys, char *id);
int findCourseByName(CourseSystem *sys, char *name);
void pauseAndClear();
#endif
2 主函数与菜单 (main.c)
#include "course_system.h"
int main() {
CourseSystem sys;
initSystem(&sys); // 初始化系统,加载数据
int choice;
do {
showMenu();
printf("请输入您的选择 (0-6): ");
scanf("%d", &choice);
// 清除输入缓冲区中的回车符
while (getchar() != '\n');
switch (choice) {
case 1:
addCourse(&sys);
break;
case 2:
queryCourse(&sys);
break;
case 3:
modifyCourse(&sys);
break;
case 4:
deleteCourse(&sys);
break;
case 5:
displayAllCourses(&sys);
break;
case 6:
printf("正在保存数据并退出系统...\n");
saveToFile(&sys);
pauseAndClear();
printf("感谢使用,再见!\n");
break;
case 0:
printf("确认要退出吗?(y/n): ");
char confirm = getchar();
if (confirm == 'y' || confirm == 'Y') {
printf("正在保存数据并退出系统...\n");
saveToFile(&sys);
pauseAndClear();
printf("感谢使用,再见!\n");
return 0;
}
break;
default:
printf("无效的输入,请重新选择!\n");
break;
}
pauseAndClear(); // 每次操作后暂停并清屏
} while (choice != 0 && choice != 6);
return 0;
}
3 功能函数实现 (functions.c)
#include "course_system.h"
// 初始化系统,从文件加载数据
void initSystem(CourseSystem *sys) {
sys->count = 0;
FILE *fp = fopen("courses.dat", "r");
if (fp == NULL) {
printf("数据文件不存在或无法打开,将创建新文件,\n");
return;
}
while (fscanf(fp, "%19[^,],%49[^,],%29[^,],%d,%29[^\n]",
sys->courses[sys->count].id,
sys->courses[sys->count].name,
sys->courses[sys->count].teacher,
&sys->courses[sys->count].credit,
sys->courses[sys->count].time) == 5) {
sys->count++;
if (sys->count >= 100) break; // 防止数组越界
}
fclose(fp);
printf("成功加载 %d 门课程信息,\n", sys->count);
}
// 保存数据到文件
void saveToFile(CourseSystem *sys) {
FILE *fp = fopen("courses.dat", "w");
if (fp == NULL) {
printf("无法打开文件进行保存!\n");
return;
}
for (int i = 0; i < sys->count; i++) {
fprintf(fp, "%s,%s,%s,%d,%s\n",
sys->courses[i].id,
sys->courses[i].name,
sys->courses[i].teacher,
sys->courses[i].credit,
sys->courses[i].time);
}
fclose(fp);
}
// 显示主菜单
void showMenu() {
printf("========================================\n");
printf("= 欢迎使用课程管理系统 =\n");
printf("========================================\n");
printf("= 1. 添加课程 =\n");
printf("= 2. 查询课程 =\n");
printf("= 3. 修改课程 =\n");
printf("= 4. 删除课程 =\n");
printf("= 5. 显示所有课程 =\n");
printf("= 6. 保存并退出 =\n");
printf("= 0. 退出(不保存) =\n");
printf("========================================\n");
}
// 添加课程
void addCourse(CourseSystem *sys) {
if (sys->count >= 100) {
printf("课程数量已达上限,无法添加!\n");
return;
}
Course c;
printf("请输入课程编号: ");
scanf("%s", c.id);
// 检查ID是否已存在
if (findCourseById(sys, c.id) != -1) {
printf("错误:该课程编号已存在!\n");
return;
}
printf("请输入课程名称: ");
scanf("%s", c.name);
printf("请输入授课教师: ");
scanf("%s", c.teacher);
printf("请输入学分: ");
scanf("%d", &c.credit);
printf("请输入上课时间: ");
scanf("%s", c.time);
sys->courses[sys->count++] = c;
printf("课程添加成功!\n");
}
// 查询课程
void queryCourse(CourseSystem *sys) {
int subChoice;
printf("--- 查询方式 ---\n");
printf("1. 按课程编号查询\n");
printf("2. 按课程名称查询\n");
printf("请选择查询方式: ");
scanf("%d", &subChoice);
while (getchar() != '\n'); // 清空缓冲区
char keyword[50];
int index = -1;
switch (subChoice) {
case 1:
printf("请输入要查询的课程编号: ");
scanf("%s", keyword);
index = findCourseById(sys, keyword);
break;
case 2:
printf("请输入要查询的课程名称: ");
scanf("%s", keyword);
index = findCourseByName(sys, keyword);
break;
default:
printf("无效的选择!\n");
return;
}
if (index != -1) {
printf("\n--- 查询结果 ---\n");
printf("课程编号: %s\n", sys->courses[index].id);
printf("课程名称: %s\n", sys->courses[index].name);
printf("授课教师: %s\n", sys->courses[index].teacher);
printf("学 分: %d\n", sys->courses[index].credit);
printf("上课时间: %s\n", sys->courses[index].time);
} else {
printf("未找到相关课程!\n");
}
}
// 修改课程
void modifyCourse(CourseSystem *sys) {
char id[20];
printf("请输入要修改的课程编号: ");
scanf("%s", id);
int index = findCourseById(sys, id);
if (index == -1) {
printf("未找到该课程!\n");
return;
}
printf("找到课程:%s\n", sys->courses[index].name);
printf("请输入新的课程名称 (原: %s): ", sys->courses[index].name);
scanf("%s", sys->courses[index].name);
printf("请输入新的授课教师 (原: %s): ", sys->courses[index].teacher);
scanf("%s", sys->courses[index].teacher);
printf("请输入新的学分 (原: %d): ", sys->courses[index].credit);
scanf("%d", &sys->courses[index].credit);
printf("请输入新的上课时间 (原: %s): ", sys->courses[index].time);
scanf("%s", sys->courses[index].time);
printf("课程信息修改成功!\n");
}
// 删除课程
void deleteCourse(CourseSystem *sys) {
char id[20];
printf("请输入要删除的课程编号: ");
scanf("%s", id);
int index = findCourseById(sys, id);
if (index == -1) {
printf("未找到该课程!\n");
return;
}
printf("确认要删除课程 \"%s\" 吗?(y/n): ", sys->courses[index].name);
char confirm = getchar();
if (confirm == 'y' || confirm == 'Y') {
// 将后面的元素前移,覆盖要删除的元素
for (int i = index; i < sys->count - 1; i++) {
sys->courses[i] = sys->courses[i + 1];
}
sys->count--;
printf("课程删除成功!\n");
} else {
printf("删除操作已取消,\n");
}
}
// 显示所有课程
void displayAllCourses(CourseSystem *sys) {
if (sys->count == 0) {
printf("当前没有任何课程信息!\n");
return;
}
printf("\n--- 所有课程信息 ---\n");
printf("-----------------------------------------------------------------\n");
printf("| %-10s | %-20s | %-10s | %-5s | %-15s |\n", "课程编号", "课程名称", "授课教师", "学分", "上课时间");
printf("-----------------------------------------------------------------\n");
for (int i = 0; i < sys->count; i++) {
printf("| %-10s | %-20s | %-10s | %-5d | %-15s |\n",
sys->courses[i].id,
sys->courses[i].name,
sys->courses[i].teacher,
sys->courses[i].credit,
sys->courses[i].time);
}
printf("-----------------------------------------------------------------\n");
printf("总计: %d 门课程\n\n", sys->count);
}
// 按ID查找,返回索引,找不到返回-1
int findCourseById(CourseSystem *sys, char *id) {
for (int i = 0; i < sys->count; i++) {
if (strcmp(sys->courses[i].id, id) == 0) {
return i;
}
}
return -1;
}
// 按名称查找,返回第一个匹配的索引,找不到返回-1
int findCourseByName(CourseSystem *sys, char *name) {
for (int i = 0; i < sys->count; i++) {
if (strstr(sys->courses[i].name, name) != NULL) { // 使用strstr实现模糊查询
return i;
}
}
return -1;
}
// 暂停并清屏
void pauseAndClear() {
printf("\n按任意键继续...");
getch(); // 等待用户按键
#ifdef _WIN32
system("cls"); // Windows清屏
#else
system("clear"); // Linux/Mac清屏
#endif
}
编译与运行
- 保存文件:将上述代码分别保存为
course_system.h、main.c和functions.c。 - 编译:打开命令行(如Windows的CMD或Linux的Terminal),使用GCC进行编译。
gcc main.c functions.c -o course_system.exe
- 在Linux/macOS上,编译命令为:
gcc main.c functions.c -o course_system
- 在Linux/macOS上,编译命令为:
- 运行:
- 在Windows上:
course_system.exe
- 在Linux/macOS上:
./course_system
- 在Windows上:
首次运行:程序会提示“数据文件不存在...”,然后创建一个空的 courses.dat 文件,之后就可以开始添加课程了。
测试方案
-
添加功能测试:
- 输入1,添加多门课程,确保信息正确录入。
- 尝试添加一个已存在的课程ID,系统应提示错误。
- 添加超过100门课程,系统应提示已达上限。
-
查询功能测试:
- 输入2,选择1(按ID查询),查询一个存在的ID,确认显示正确。
- 查询一个不存在的ID,系统应提示“未找到”。
- 输入2,选择2(按名称查询),查询一个存在的名称,确认显示正确。
- 查询一个不存在的名称,系统应提示“未找到”。
-
修改功能测试:
- 输入3,输入一个存在的课程ID,修改其信息,然后通过查询功能验证修改是否成功。
- 输入3,输入一个不存在的课程ID,系统应提示“未找到”。
-
删除功能测试:
- 输入4,输入一个存在的课程ID,确认删除,通过“显示所有课程”功能验证该课程已被删除。
- 输入4,输入一个不存在的课程ID,系统应提示“未找到”。
- 删除后,再次启动程序,确认该课程未被重新加载。
-
数据持久化测试:
- 添加、修改、删除一些课程后,选择“6. 保存并退出”。
- 关闭程序,重新启动,选择“5. 显示所有课程”,检查数据是否与退出前一致。
-
边界与异常测试:
- 在主菜单输入0,确认是否安全退出。
- 在主菜单输入一个无效的数字(如7),系统是否给出错误提示。
- 输入信息时,尝试输入超长的字符串,观察系统行为(当前代码没有做长度限制,实际项目中应增加)。
总结与展望
1 总结 本课程设计实现了一个功能完备、结构清晰的课程管理系统,它成功运用了C语言的核心知识点,包括结构体、数组、文件I/O、函数模块化设计等,系统通过菜单驱动,操作直观,并具备数据持久化能力,达到了预期的设计目标。
2 不足与改进方向
- 数据安全性:当前系统没有密码保护,任何用户都可以修改数据,可以增加用户登录模块,区分管理员和学生等不同角色。
- 输入验证:对用户输入(如学分必须是正整数)的校验不够严格,可以增加输入验证函数,确保数据的合法性。
- 数据结构优化:使用固定大小的数组存储课程,不够灵活,可以改用动态内存分配(如
malloc、realloc)或链表来管理课程,实现动态扩容。 - 用户体验:
pauseAndClear函数使用了平台相关的命令(cls/clear),可以封装一个更通用的清屏函数,菜单和提示信息可以做得更美观。 - 功能扩展:
- 排序功能:增加按学分、教师姓名等字段对课程进行排序的功能。
- 统计功能:统计某位教师教授的课程数、总学分等。
- 模糊查询:在按名称查询时,可以支持更复杂的模糊匹配。
- 图形界面:如果学有余力,可以使用第三方库(如EasyX, GTK, Qt)将命令行界面升级为图形用户界面。
这个项目不仅是一个课程作业,更是一个可以不断迭代和完善的小型应用程序,非常适合用来练习和提升C语言编程能力。
