学生成绩管理系统

本文介绍了一个简单的学生管理系统的设计与实现过程,包括添加、删除、查询学生信息等功能,通过链表存储数据,支持文件读写和基本的用户交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个简单的学生管理系统

1. 一些小技巧

在这之前需要一些小技巧~

1.1 刷新缓冲区

刷新缓冲区有三种方式
1. 利用getchar()刷新
getchar可以刷新缓冲区的一个字符。例如,之前经常会出现的莫名其妙的错误。

#include <stdio.h>
int main(void)
{
    char ch;
    int a;
    scanf("%d", &a);
    scanf("%c", &ch);
    printf("%d %c\n", a, ch);
    return 0;
}

当输入一个数字,再按回车时会出问题。
如下:
错误
如果在第一个scanf后添加一个getchar()就不会出现这样的错误。
2. getchar()升级版

char *s_gets(char *st,int n)
{
    char * ret_val = fgets(st, n, stdin);
    int i = 0;
    if(ret_val)
    {
        while(st[i] != '\n'&&st[i] != '\0')
            i++;
        if(st[i] == '\n')
            st[i] = '\0';
        else
            while(getchar() != '\n')
                continue;
    }
    return ret_val;
}

上述的函数时C Primer Plus中的一个函数,他的作用时限制输入的长度,并将缓冲区中多余的字符清除掉。这个函数的核心如下:

while(getchar() != '\n')
    continue;

利用循环的getchar()将多余的字符清除掉,以免影响后续操作。
3. 利用fflush()函数。
参数是:stdin、stdout。
stdin: 标准输入。
stdout: 标准输出。
stderr: 标准错误。

1.2 一些system的函数

  1. system("cls")清屏函数
  2. system("title 成绩管理系统")
    将窗口的标题设置为“成绩管理系统”
  3. system("color xx")
    颜色属性由两个十六进制数字指定 – 第一个为背景,第二个则为前景。每个数字可以为以下任何值之一:
    0 = 黑色 8 = 灰色
    1 = 蓝色 9 = 淡蓝色
    2 = 绿色 A = 淡绿色
    3 = 湖蓝色 B = 淡浅绿色
    4 = 红色 C = 淡红色
    5 = 紫色 D = 淡紫色
    6 = 黄色 E = 淡黄色
    7 = 白色 F = 亮白色
  4. system("mode con cols=90 lines=30");
    设置窗口的高度和宽度,如上,设置窗口90列,30行。
  5. system("pasue");

1.3 分文件写

可以些一个或多个的头文件
这样做可以利用修改。

这个系统实现的一些功能,即菜单函数。

与学生成绩相关的无非是增、删、该、查,可以额外的使用到文件和排序等增加一些实用性。

void menu(void)
{
    printf("\n\n\n\n\n\n");
    printf("\t\t\t\t---------------欢迎来到成绩管理系统-----------------\n");
    printf("\t\t\t\ta):查看所有学生的信息。\t\tb):添加学生的信息。\n");
    printf("\t\t\t\tc):查看某个学生的信息。\t\td):删除学生的信息。\n");
    printf("\t\t\t\tf):指定位置插入。\t\tg):更改学生信息。\n");
    printf("\t\t\t\th):删除所有的数据。\t\ti):统计学生人数.\n");
    printf("\t\t\t\ts):保存\t\t\t\tq):退出。\n"); 
}

程序的主体是用一个链表完成的,所以刚开始时,应该将之前存在文件中的数据读入链表。

建立链表需要的一些声明

typedef struct student {
    char num[TSIZE];
    char name[TSIZE];
    float math;
    float English;
    float Chinese;
    float sum;
    float average;
}Item;
typedef struct node {
    Item item;
    struct node * next;
} Node;

将链表的数据域设置为一个结构体有利于后续的操作。
从上述代码中可以看出,我设置的学生信息有:学号、姓名、数学、英语和语文成绩。

程序开始的第一步,将数据读入链表

void CreatList(List * plist)
{
    char str[100];
    Item temp;
    FILE *fp;
    if((fp = fopen("学生成绩单.txt", "r")) == NULL){
        fprintf(stderr, "不能打开“学生成绩单”文件。\n");
        exit(EXIT_FAILURE);
    }
    InitializeList(plist);
    while(fscanf(fp, "%s %s %f %f %f %f %f", temp.num, temp.name, &temp.math,
                &temp.English, &temp.Chinese, &temp.sum, &temp.average) == 7)
    {

        AddItem(temp, plist);
    }
    fclose(fp);
}

尽量不要实用feof(),处理不恰当时可能会多判断一步。
AddItem()函数的代码如下:

bool AddItem(Item item, List * plist){
    Node * pnew;
    Node * scan = *plist;

    pnew = (Node *) malloc(sizeof(Node));
    if(pnew == NULL)
        return false;
    CopyToNode(item,pnew);
    pnew->next = NULL;
    if(scan == NULL)
        *plist = pnew;
    else{
        while(scan->next != NULL)
            scan = scan->next;
        scan->next = pnew;
    }

    return true;
}

这样可以方便添加学生。

第一个功能添加学生

添加学生这是一种默认的方式,将学生添加在链表的末尾。

void Addstudent(List * plist)
{
    Item temp;
    printf("请输入学生的姓名(按回车键结束):");
    while(scanf("%s", temp.name) != EOF && temp.name[0] != 'q'){
        printf("请输入学生的学号:");
        scanf("%s", temp.num);
        printf("请输入学生的数学成绩:");
        scanf("%f", &temp.math);
        printf("英语成绩:");
        scanf("%f", &temp.English);
        printf("语文成绩:");
        scanf("%f", &temp.Chinese);
        temp.sum = temp.math + temp.English + temp.Chinese;
        temp.average = temp.sum / 3;
        AddItem(temp, plist);
        printf("请输入学生的姓名(按q键结束):");
    }
}

最好不要每次只能添加一个,这样要每次调用添加命令,显得很麻烦。

第二个功能:显示所有学生的信息

void print(List * plist){
    if(ListIsEmpty( plist )){
        fprintf(stderr, "没有数据!!!\n");
        return ;
    }
    printf("学号\t\t姓名\t数学\t英语\t语文\t总成绩\t平均\n");
    Node * p = *plist;
    while(p != NULL)
    {
        printf("%-12s\t%s\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",p->item.num,
            p->item.name, p->item.math,p->item.English, p->item.Chinese, p->item.sum, p->item.average);
        p = p->next;
     }
}

整个程序都要注意的要点是一点要注意一些特殊情况。
这个程序的要点是一定要将信息对其。

第三个功能:查看某个学生的信息

bool cha(List * plist)
{
    char ch;
    char num[TSIZE]; 
    printf("请输入你想查看的学生的学号:");
    scanf("%s", &num);
    if(ListIsEmpty(plist)){
        printf("没有数据,无法查看!!\n");
        return false;
    }
    Node * p = *plist;
    while(p->next != NULL && strcmp(p->item.num, num)){
        p = p->next;
    }
    if(strcmp(p->item.num, num) != 0 && p->next == NULL){
        fprintf(stderr, "没有找到持学生的信息");
        return false;
    }
    printf("m):数学\te):英语\tc):语文\tq):退出\n"); 
    getchar();
    while(1){
        printf("请书输入你想查找的科目:");
        fflush(stdin);
        ch = getchar();
        while(getchar() != '\n')
            continue;
        switch(ch){
            case 'm':
                printf("%s的数学成绩是:%.2lf\n", p->item.name, p->item.math);
                break;
            case 'e':
                printf("%s的英语成绩是:%.2lf\n", p->item.name, p->item.English);
                break;
            case 'c':
                printf("%s的语文成绩是:%.2lf\n", p->item.name, p->item.Chinese);
                break;
            case 'q':
                return true; 
             default:
                printf("请输入正确的指令!!\n"); 
        }
    }
    return false;
} 

第四个功能:删除学生的信息

bool ListDelete(List * plist, char num[])
{
    Node * p, * q;
    p = *plist;
    if(ListIsEmpty(plist)){
        fprintf(stderr, "没有数据,无法删除!!\n");
        return false;
    }
    if(strcmp(p->item.num, num) == 0){
        *plist = p->next;
        return true;
    }
    else{
        while(p){
            if(strcmp(p->item.num, num) == 0){
                q->next = p->next;
                free(p);
                return true;
            }
            q = p;
            p = p->next;
        }
    }
    return false;
}

这个函数的功能是删除学生的信息,它的方式是传递一个学生的学号,然后按照学号删除。当然你也可以在这个函数里输入学生的学号。
能删除学生的信息,那么就能改变学生的信息。

第五个功能:改变学生的信息

改变学生的信息有两种思路,全改或者部分改。

bool gai(List * plist){
    char ch;
    char num[TSIZE];
    printf("请输入你想改的学生的学号:");
    scanf("%s", num);
    if(ListIsEmpty(plist))
        return false;
    Node * p = *plist;
    while(p->next != NULL && strcmp(p->item.num, num) != 0){
        p = p->next;
    }
    if(strcmp(p->item.num, num) != 0 && p->next == NULL){
        return false;
    }
    printf("是否讲一个学生的信息完全改变(Y:是 N:否):");
    fflush(stdin);
    ch = getchar();
    while(getchar() != '\n')
            continue;
    if(ch == 'Y'){
        printf("请输入学生的姓名:");
        scanf("%s", p->item.name);
        printf("请输入学生的学号:");
        scanf("%s", p->item.num);
        printf("请输入学生的数学成绩:");
        scanf("%f", &p->item.math);
        printf("英语成绩:");
        scanf("%f", &p->item.English);
        printf("语文成绩:");
        scanf("%f", &p->item.Chinese);
        p->item.sum = p->item.math + p->item.English + p->item.Chinese;
        p->item.average = p->item.sum / 3;
    }
    else{
        char ch2;
        printf("请输入你想更改的科目:\na):数学\tb):英语\tc):语文\t\n");
        printf("d):学号\tf):姓名\tq):退出\n");
        while(1){
            printf("请选择:");
            fflush(stdin);
            ch2 = getchar();
            while(getchar() != '\n')
                continue;
            switch(ch2){
                case 'a':
                    printf("请输入学生的数学成绩:");
                    scanf("%f", &p->item.math);
                    getchar();
                    break;
                case 'b':
                    printf("请输入学生的英语成绩:");
                    scanf("%f", &p->item.English);
                    getchar();
                    break;
                case 'c':
                    printf("请输入学生的语文成绩:");
                    scanf("%f",&p->item.Chinese);
                    getchar();
                    break;
                case 'd':
                    printf("请输入学生的学号:"); 
                    scanf("%s", p->item.num);
                    getchar();
                    break;
                case'f':
                    printf("请输入学生的名字:"); 
                    scanf("%s", p->item.name);
                    getchar();
                    break;
                case 'q':
                    p->item.sum = p->item.math + p->item.English + p->item.Chinese;
                    p->item.average = p->item.sum / 3;
                    return true;
                default:
                    printf("请输入有效的命令!!\n");
            }
        } 
    }

    return false;
}

第六个功能:插入

既然是插入,那么每个位置都要能插。当插在某个学生的后面时,第一个位置就插不到。那个就需要选择。当然当没有任何学生信息时,还要询问是否插在最开始。

bool ListInsert(List * plist)
{
    Node *q = *plist;
    char ch;
    Node * p;
    p = (struct node *) malloc(sizeof(struct node));
    if(ListIsEmpty(plist)){
        printf("没有数据,只能插在最开始。\n是否插在最开始?(Y:是 N:退出)\n");
        char ch_1;
        fflush(stdin);
        ch_1 = getchar();
        while(getchar() != '\n')
            continue;
        if(ch_1 == 'Y'){
            ch == 'Y';
        }
        else
            return false;
    } 
    else{
        printf("请问是否插在最开始?(Y:是 N:否)\n");
        fflush(stdin);
        ch = getchar();
        while(getchar() != '\n')
            continue;
    }
    if(ch == 'Y'){
        *plist = p;
        p->next = q;
    }
    else
    {
        char num[TSIZE];
        List r;
        printf("请输入插入后学生之前的那一个学生的学号:");
        scanf("%s", num);
        while(q != NULL && strcmp(q->item.num, num) != 0){
            q = q->next;
        }
        if(q == NULL)
            return false;
        if(q->next == NULL){
            q->next = p;
            p->next = NULL;
        }   
        else
        {
            r = q->next;
            q->next = p;
            p->next = r;
        }
    }
    printf("请输入学生的姓名:");
    scanf("%s", p->item.name);
    printf("请输入学生的学号:");
    scanf("%s", p->item.num);
    printf("请输入学生的数学成绩:");
    scanf("%f", &p->item.math);
    printf("英语成绩:");
    scanf("%f", &p->item.English);
    printf("语文成绩:");
    scanf("%f", &p->item.Chinese);
    p->item.sum = p->item.math + p->item.English + p->item.Chinese;
    p->item.average = p->item.sum / 3;
    return true;
}

第七个功能:排序

List SelectSort(List head)  
{  
    List pfirst;      /* 排列后有序链的表头指针 */  
    List ptail;       /* 排列后有序链的表尾指针 */  
    List pminBefore;  /* 保留键值更小的节点的前驱节点的指针 */  
    List pmin;        /* 存储最小节点   */  
    List p;           /* 当前比较的节点 */  
    if(head == NULL){
        fprintf(stderr, "没有数据。\n");
        return head;
    }
    if(head->next == NULL){
        fprintf(stderr, "只有一组数据无法排序!!\n");
        return head;
    }
    pfirst = NULL;  
    while (head != NULL)         /*在链表中找键值最小的节点。*/  
    {  
    /* 注意:这里for语句就是体现选择排序思想的地方 */  
        for (p = head, pmin = head; p->next != NULL; p = p->next) /*循环遍历链表中的节点,找出此时最小的节点。*/  
        {  
            if (strcmp(pmin->item.num, p->next->item.num) > 0) /*找到一个比当前min小的节点。*/  
            {  
                pminBefore = p;           /*保存找到节点的前驱节点:显然p->next的前驱节点是p。*/  
                pmin = p->next;     /*保存键值更小的节点。*/  
            }  
        }  

    /*上面for语句结束后,就要做两件事;一是把它放入有序链表中;二是根据相应的条件判断,安排它离开原来的链表。*/  

        /*第一件事*/  
        if (pfirst == NULL)     /* 如果有序链表目前还是一个空链表                      */  
        {  
            pfirst = pmin;      /* 第一次找到键值最小的节点。                          */  
            ptail  = pmin;      /* 注意:尾指针让它指向最后的一个节点。                */  
        }  
        else                    /* 有序链表中已经有节点                                */  
        {  
            ptail->next = pmin; /* 把刚找到的最小节点放到最后,即让尾指针的next指向它。*/  
            ptail = pmin;       /* 尾指针也要指向它。                                  */  
        }  

        /*第二件事*/  
        if (pmin == head)        /* 如果找到的最小节点就是第一个节点                    */  
        {  
            head = head->next;   /* 显然让head指向原head->next,即第二个节点,就OK       */  
        }  
        else /*如果不是第一个节点*/  
        {  
            pminBefore->next = pmin->next; /*前次最小节点的next指向当前pmin的next,这样就让pmin离开了原链表。*/  
        }  
    }  

    if (pfirst != NULL)     /*循环结束得到有序链表first                */  
    {  
        ptail->next = NULL; /*单向链表的最后一个节点的next应该指向NULL */   
    }  
    head = pfirst;  
    return head;  
}  

这里是一学号排序的。
还有一些其他的操作就不赘述了。

下面是一个简单的main函数

int main(void)
{
    List head;
    CreatList(&head);
    int ch;
    menu();
    printf("请选择你想选择的操作:");
    ch = getchar();
    while(getchar() != '\n')
            continue;
    while(1){
        switch(ch){
            case 'a':
                print(&head);
                break;
            case 'b' :

                Addstudent(&head);
                count++;
                break;
            case 'c':

                cha(&head);
                break;
            case 'd':
                char num_1[TSIZE];
                printf("请输入你想删除的学生的学号:");
                scanf("%s", num_1);
                if(ListDelete(&head, num_1)){
                    printf("已删除。\n");
                }
                else{
                    printf("删除失败。\n");
                }
                count++;
                break;
            case 'f':
                if(ListInsert(&head)){
                    printf("插入成功!!\n");
                    count++;
                }
                else{
                    printf("插入失败!!\n");
                }
                break;
            case 'g':
                if(!gai(&head)){
                    fprintf(stderr, "没有找到持学生的信息");
                }
                count++;
                break;
            case 'h':
                int ch_2;
                getchar();
                printf("是否将所有数据都删除(Y:是 N:否):");
                ch_2 = getchar();
                if(ch_2 == 'Y'){
                    EmptyTheList(&head);
                    InitializeList(&head);
                    count++;
                }
                break; 
            case 'i':
                printf("储存有%d个学生的成绩。\n", ListItemCount(&head));
                break;
            case 's':
                save(&head);
                break;
            case 'm':
                menu();
                break;
            case 'p':
                head = SelectSort(head);
                count++;
                break;
            case 'q':
                if(count != 0 && count2 == 0)
                {
                    char ch2;
                    printf("是否保存?(Y:是  N:否)\n");
                    fflush(stdin);
                    ch2 = getchar();
                    if(ch2 == 'Y')
                        save(&head);
                }
                return 0;
            default:
                printf("请书如正确的指令!!\n"); 
        }
        printf("\n\n");
        fflush(stdin);
        printf("请选择你想选择的操作:");
        ch = getchar();
        while(getchar() != '\n')
            continue; 
    }
    return 0;
}

总结

当然这个还有好多的优化,这里提供的仅仅只是一个思想,这个程序最大的缺点是缺少错误处理。
优化措施:
1. 增加错误处理,当输入学生的信息出错是提醒并重新输入。
2. 学号查重。
3. 美化界面等。

相信优秀的你能够写出更好的程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值