C语言选课系统如何实现学生课程选择功能?

99ANYc3cd6
预计阅读时长 56 分钟
位置: 首页 C语言 正文

C语言课程设计:学生选课系统

项目概述与需求分析

项目目标: 开发一个基于命令行的学生选课系统,模拟真实环境中学生选课的基本流程,该系统应能够实现学生信息管理、课程信息管理、选课、退课、查询等功能,为用户提供一个简洁、易用的操作界面。

c语言课程设计学生选课系统
(图片来源网络,侵删)

核心功能需求:

  1. 数据管理:

    • 学生信息: 存储学生的学号、姓名、密码、已选课程列表。
    • 课程信息: 存储课程的课程号、课程名、授课教师、学分、容量、已选人数。
  2. 核心功能:

    • 学生登录: 学生通过学号和密码登录系统。
    • 课程浏览: 登录后,学生可以查看所有可选课程列表。
    • 选课: 学生可以选择一门未满且自己未选的课程。
    • 退课: 学生可以退掉自己已经选中的课程。
    • 查看个人课表: 学生可以查看自己当前已选的所有课程。
    • 查询课程信息: 学生可以根据课程号或课程名查询特定课程的详细信息。
  3. 管理员功能 (可选,但推荐):

    c语言课程设计学生选课系统
    (图片来源网络,侵删)
    • 管理员登录: 使用预设的管理员账号和密码登录。
    • 添加学生: 向系统中添加新的学生账号。
    • 添加课程: 向系统中添加新的课程信息。
    • 查看所有学生/课程信息: 查看系统中的所有学生或课程的详细信息。
  4. 数据持久化:

    • 系统启动时,能从文件中加载学生和课程数据。
    • 系统退出时,能将最新的学生和课程数据保存回文件,确保数据不丢失。

系统设计

1 数据结构设计

使用结构体来表示核心数据实体。

// 学生结构体
typedef struct {
    char id[20];        // 学号
    char name[50];      // 姓名
    char password[20];  // 密码
    int selected_courses[100]; // 存储已选课程的课程号
    int course_count;   // 已选课程数量
} Student;
// 课程结构体
typedef struct {
    char id[20];        // 课程号
    char name[50];      // 课程名
    char teacher[30];   // 授课教师
    int credit;         // 学分
    int capacity;       // 课程容量
    int selected_count; // 已选人数
} Course;

2 功能模块设计

c语言课程设计学生选课系统
(图片来源网络,侵删)

将整个系统划分为几个功能模块,每个模块由一组相关的函数实现。

  • 主菜单模块: showMainMenu()

    负责显示登录选项(学生/管理员/退出)。

  • 学生模块:
    • studentLogin(): 学生登录验证。
    • showStudentMenu(): 显示学生子菜单(选课、退课、查看课表等)。
    • browseCourses(): 浏览所有课程。
    • selectCourse(): 执行选课逻辑。
    • dropCourse(): 执行退课逻辑。
    • viewMySchedule(): 查看个人课表。
  • 管理员模块:
    • adminLogin(): 管理员登录验证。
    • showAdminMenu(): 显示管理员子菜单(添加学生、添加课程等)。
    • addStudent(): 添加新学生。
    • addCourse(): 添加新课程。
    • showAllStudents(): 显示所有学生信息。
    • showAllCourses(): 显示所有课程信息。
  • 数据管理模块:
    • loadData(): 从文件 students.datcourses.dat 加载数据。
    • saveData(): 将数据保存到文件。
    • initializeData(): 初始化一些默认的学生和课程数据(用于首次运行)。
  • 工具模块:
    • findStudentById(): 根据学号查找学生。
    • findCourseById(): 根据课程号查找课程。
    • clearScreen(): 清屏函数,使界面更整洁。

3 文件结构设计

  • students.dat: 存储所有学生信息的二进制文件。
  • courses.dat: 存储所有课程信息的二进制文件。

核心代码实现

下面是关键部分的C语言代码实现,你可以基于此框架进行扩展和完善。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h> // 用于 getch(),Windows下可用,Linux下需用 ncurses
#define MAX_STUDENTS 100
#define MAX_COURSES 50
#define FILENAME_STUDENTS "students.dat"
#define FILENAME_COURSES "courses.dat"
// --- 结构体定义 ---
typedef struct {
    char id[20];
    char name[50];
    char password[20];
    int selected_courses[100];
    int course_count;
} Student;
typedef struct {
    char id[20];
    char name[50];
    char teacher[30];
    int credit;
    int capacity;
    int selected_count;
} Course;
// --- 全局变量 ---
Student students[MAX_STUDENTS];
Course courses[MAX_COURSES];
int student_count = 0;
int course_count = 0;
Student *current_student = NULL; // 当前登录的学生指针
// --- 函数声明 ---
void loadData();
void saveData();
void initializeData();
void clearScreen();
void showMainMenu();
void studentLogin();
void showStudentMenu();
void browseCourses();
void selectCourse();
void dropCourse();
void viewMySchedule();
void adminLogin();
void showAdminMenu();
void addStudent();
void addCourse();
void showAllStudents();
void showAllCourses();
Student* findStudentById(const char *id);
Course* findCourseById(const char *id);
// --- 主函数 ---
int main() {
    loadData();
    showMainMenu();
    saveData(); // 退出前保存数据
    return 0;
}
// --- 数据管理函数 ---
void loadData() {
    FILE *sf = fopen(FILENAME_STUDENTS, "rb");
    FILE *cf = fopen(FILENAME_COURSES, "rb");
    if (sf) {
        fread(&student_count, sizeof(int), 1, sf);
        fread(students, sizeof(Student), student_count, sf);
        fclose(sf);
    }
    if (cf) {
        fread(&course_count, sizeof(int), 1, cf);
        fread(courses, sizeof(Course), course_count, cf);
        fclose(cf);
    } else {
        // 如果文件不存在,则初始化数据
        initializeData();
    }
}
void saveData() {
    FILE *sf = fopen(FILENAME_STUDENTS, "wb");
    FILE *cf = fopen(FILENAME_COURSES, "wb");
    if (sf) {
        fwrite(&student_count, sizeof(int), 1, sf);
        fwrite(students, sizeof(Student), student_count, sf);
        fclose(sf);
    }
    if (cf) {
        fwrite(&course_count, sizeof(int), 1, cf);
        fwrite(courses, sizeof(Course), course_count, cf);
        fclose(cf);
    }
}
void initializeData() {
    // 初始化默认学生
    strcpy(students[0].id, "2025001");
    strcpy(students[0].name, "张三");
    strcpy(students[0].password, "123456");
    students[0].course_count = 0;
    student_count++;
    strcpy(students[1].id, "2025002");
    strcpy(students[1].name, "李四");
    strcpy(students[1].password, "123456");
    students[1].course_count = 0;
    student_count++;
    // 初始化默认课程
    strcpy(courses[0].id, "CS101");
    strcpy(courses[0].name, "C语言程序设计");
    strcpy(courses[0].teacher, "王老师");
    courses[0].credit = 4;
    courses[0].capacity = 100;
    courses[0].selected_count = 0;
    course_count++;
    strcpy(courses[1].id, "CS102");
    strcpy(courses[1].name, "数据结构");
    strcpy(courses[1].teacher, "李老师");
    courses[1].credit = 5;
    courses[1].capacity = 80;
    courses[1].selected_count = 0;
    course_count++;
    saveData(); // 保存初始数据到文件
}
// --- 界面与菜单函数 ---
void clearScreen() {
    system("cls"); // Windows
    // system("clear"); // Linux
}
void showMainMenu() {
    int choice;
    while (1) {
        clearScreen();
        printf("\n\n\n");
        printf("\t\t\t学生选课系统\n");
        printf("\t\t------------------------\n");
        printf("\t\t  1. 学生登录\n");
        printf("\t\t  2. 管理员登录\n");
        printf("\t\t  0. 退出系统\n");
        printf("\t\t------------------------\n");
        printf("\t\t请选择: ");
        scanf("%d", &choice);
        getchar(); // 消耗换行符
        switch (choice) {
            case 1: studentLogin(); break;
            case 2: adminLogin(); break;
            case 0: 
                printf("\n\t感谢使用,再见!\n");
                return;
            default: printf("\t无效输入,请按任意键重试..."); getch();
        }
    }
}
// --- 学生模块 ---
void studentLogin() {
    char id[20], password[20];
    clearScreen();
    printf("\n\n\n");
    printf("\t\t\t学生登录\n");
    printf("\t\t------------------------\n");
    printf("\t\t学号: "); scanf("%s", id);
    printf("\t\t密码: "); scanf("%s", password);
    getchar();
    current_student = findStudentById(id);
    if (current_student != NULL && strcmp(current_student->password, password) == 0) {
        printf("\t\t登录成功!按任意键进入菜单...");
        getch();
        showStudentMenu();
    } else {
        printf("\t\t学号或密码错误!按任意键返回...");
        getch();
    }
}
void showStudentMenu() {
    int choice;
    while (1) {
        clearScreen();
        printf("\n\n\n");
        printf("\t\t欢迎, %s (学号: %s)\n", current_student->name, current_student->id);
        printf("\t\t------------------------\n");
        printf("\t\t  1. 浏览课程\n");
        printf("\t\t  2. 选课\n");
        printf("\t\t  3. 退课\n");
        printf("\t\t  4. 查看我的课表\n");
        printf("\t\t  0. 退出登录\n");
        printf("\t\t------------------------\n");
        printf("\t\t请选择: ");
        scanf("%d", &choice);
        getchar();
        switch (choice) {
            case 1: browseCourses(); break;
            case 2: selectCourse(); break;
            case 3: dropCourse(); break;
            case 4: viewMySchedule(); break;
            case 0: 
                current_student = NULL;
                printf("\n\t已退出登录,按任意键返回主菜单...");
                getch();
                return;
            default: printf("\t无效输入,请按任意键重试..."); getch();
        }
    }
}
void browseCourses() {
    clearScreen();
    printf("\n\n\n");
    printf("\t\t\t所有课程列表\n");
    printf("\t\t------------------------------------------------\n");
    printf("\t\t%-10s %-20s %-10s %-5s %-5s\n", "课程号", "课程名", "教师", "学分", "状态");
    printf("\t\t------------------------------------------------\n");
    for (int i = 0; i < course_count; i++) {
        char status[20];
        if (courses[i].selected_count >= courses[i].capacity) {
            strcpy(status, "已满");
        } else {
            strcpy(status, "可选");
        }
        printf("\t\t%-10s %-20s %-10s %-5d %-5s\n", 
               courses[i].id, courses[i].name, courses[i].teacher, courses[i].credit, status);
    }
    printf("\t\t------------------------------------------------\n");
    printf("\t\t按任意键返回...");
    getch();
}
void selectCourse() {
    char course_id[20];
    Course *course_to_select;
    clearScreen();
    printf("\n\n\n");
    printf("\t\t\t选课\n");
    printf("\t\t请输入要选的课程号: ");
    scanf("%s", course_id);
    getchar();
    course_to_select = findCourseById(course_id);
    if (course_to_select == NULL) {
        printf("\t\t课程不存在!按任意键返回...");
        getch();
        return;
    }
    if (course_to_select->selected_count >= course_to_select->capacity) {
        printf("\t\t课程已满!无法选择,按任意键返回...");
        getch();
        return;
    }
    // 检查是否已选
    for (int i = 0; i < current_student->course_count; i++) {
        if (current_student->selected_courses[i] == atoi(course_id)) { // 假设课程号是数字
            printf("\t\t您已经选过这门课了!按任意键返回...");
            getch();
            return;
        }
    }
    // 执行选课
    current_student->selected_courses[current_student->course_count++] = atoi(course_id);
    course_to_select->selected_count++;
    printf("\t\t选课成功!按任意键返回...");
    getch();
}
void dropCourse() {
    char course_id[20];
    int course_to_drop_idx = -1;
    clearScreen();
    printf("\n\n\n");
    printf("\t\t\t退课\n");
    printf("\t\t请输入要退的课程号: ");
    scanf("%s", course_id);
    getchar();
    // 查找课程在学生列表中的索引
    for (int i = 0; i < current_student->course_count; i++) {
        if (current_student->selected_courses[i] == atoi(course_id)) {
            course_to_drop_idx = i;
            break;
        }
    }
    if (course_to_drop_idx == -1) {
        printf("\t\t您没有选这门课!按任意键返回...");
        getch();
        return;
    }
    // 执行退课
    Course *course_to_drop = findCourseById(course_id);
    if (course_to_drop) {
        course_to_drop->selected_count--;
    }
    // 从学生列表中移除
    for (int i = course_to_drop_idx; i < current_student->course_count - 1; i++) {
        current_student->selected_courses[i] = current_student->selected_courses[i + 1];
    }
    current_student->course_count--;
    printf("\t\t退课成功!按任意键返回...");
    getch();
}
void viewMySchedule() {
    clearScreen();
    printf("\n\n\n");
    printf("\t\t\t%s 的课表\n", current_student->name);
    printf("\t\t------------------------------------------------\n");
    printf("\t\t%-10s %-20s %-10s %-5s\n", "课程号", "课程名", "教师", "学分");
    printf("\t\t------------------------------------------------\n");
    int total_credits = 0;
    for (int i = 0; i < current_student->course_count; i++) {
        int c_id = current_student->selected_courses[i];
        Course *c = findCourseById(c_id); // 需要修改 findCourseById 以支持整数ID
        if (c) {
            printf("\t\t%-10s %-20s %-10s %-5d\n", c->id, c->name, c->teacher, c->credit);
            total_credits += c->credit;
        }
    }
    printf("\t\t------------------------------------------------\n");
    printf("\t\t总学分: %d\n", total_credits);
    printf("\t\t按任意键返回...");
    getch();
}
// --- 管理员模块 (示例) ---
void adminLogin() {
    // 简化版,直接进入管理员菜单
    printf("\n管理员登录成功!按任意键进入菜单...");
    getch();
    showAdminMenu();
}
void showAdminMenu() {
    int choice;
    while (1) {
        clearScreen();
        printf("\n\n\n");
        printf("\t\t\t管理员菜单\n");
        printf("\t\t------------------------\n");
        printf("\t\t  1. 添加学生\n");
        printf("\t\t  2. 添加课程\n");
        printf("\t\t  3. 查看所有学生\n");
        printf("\t\t  4. 查看所有课程\n");
        printf("\t\t  0. 退出登录\n");
        printf("\t\t------------------------\n");
        printf("\t\t请选择: ");
        scanf("%d", &choice);
        getchar();
        switch (choice) {
            case 1: addStudent(); break;
            case 2: addCourse(); break;
            case 3: showAllStudents(); break;
            case 4: showAllCourses(); break;
            case 0: 
                printf("\n\t已退出登录,按任意键返回主菜单...");
                getch();
                return;
            default: printf("\t无效输入,请按任意键重试..."); getch();
        }
    }
}
// --- 查找函数 ---
Student* findStudentById(const char *id) {
    for (int i = 0; i < student_count; i++) {
        if (strcmp(students[i].id, id) == 0) {
            return &students[i];
        }
    }
    return NULL;
}
Course* findCourseById(const char *id) {
    for (int i = 0; i < course_count; i++) {
        if (strcmp(courses[i].id, id) == 0) {
            return &courses[i];
        }
    }
    return NULL;
}
// --- 其他管理员函数实现 (略) ---
// addStudent(), addCourse(), showAllStudents(), showAllCourses() 的实现逻辑
// 与 browseCourses() 和 viewMySchedule() 类似,主要是数据写入和遍历输出。
// 注意:在添加学生/课程后,需要更新 student_count/course_count 并调用 saveData()。

如何运行与使用

  1. 编译代码:

    • 将以上代码保存为 course_selection.c
    • 使用GCC编译器(Windows下推荐MinGW或TDM-GCC)进行编译:
      gcc course_selection.c -o course_selection.exe
  2. 运行程序:

    • 在命令行中运行生成的可执行文件:
      ./course_selection.exe
  3. 使用流程:

    • 首次运行: 程序会自动创建 students.datcourses.dat 文件,并初始化默认数据(2个学生,2门课程)。
    • 学生登录: 使用默认账号登录,
      • 学号: 2025001
      • 密码: 123456
    • 选课/退课: 登录后,按照菜单提示操作,选课时注意查看课程状态是否为“可选”。
    • 查看课表: 可以随时查看自己已选的课程和总学分。
    • 管理员功能: 可以实现管理员菜单来添加更多学生和课程,丰富系统数据。

项目扩展与优化建议

这是一个基础版本,你可以从以下几个方面进行扩展和优化,使其功能更强大、代码更健壮:

  1. 数据结构优化:

    • 使用动态内存分配(malloc, realloc)代替固定大小的数组,以支持不确定数量的学生和课程。
    • 使用链表来管理学生和课程,便于数据的插入和删除。
  2. 功能增强:

    • 课程查询: 实现按课程名模糊查询。
    • 成绩管理: 在学生结构体中增加成绩字段,实现成绩录入和查询功能。
    • 时间冲突检测: 为课程增加“上课时间”字段,并在选课时检测是否与已选课程时间冲突。
    • 密码加密: 使用简单的加密算法(如MD5, SHA1)对用户密码进行哈希存储,提高安全性。
    • 用户角色区分: 增加教师角色,实现教师查看选课学生名单等功能。
  3. 代码健壮性:

    • 输入验证: 对所有用户输入进行严格验证,防止非法输入导致程序崩溃(输入字母代替数字)。
    • 文件操作错误处理: 对文件的打开、读写操作进行错误检查,处理文件不存在或读写失败的情况。
    • 模块化与封装: 将功能进一步细化,每个函数只做一件小事,提高代码的可读性和可维护性。
  4. 用户体验:

    • 分页显示: 当课程或学生数量很多时,实现分页显示功能。
    • 排序功能: 允许用户按学分、课程名等对课程列表进行排序。

这个项目作为C语言的课程设计非常合适,它涵盖了结构体、文件操作、数组、函数、循环、条件判断等核心知识点,并有一定的综合性和实用性,祝你项目顺利!

-- 展开阅读全文 --
头像
C语言中a 10是什么意思?
« 上一篇 03-08
dede当前栏目地址如何获取?
下一篇 » 03-08

相关文章

取消
微信二维码
支付宝二维码

目录[+]