vlambda博客
学习文章列表

基于C语言的学生管理系统(含文件)(二)





NO.1

篇幅较长,请空闲时观看

前言

整个项目包括三个文件
——mylist.h
    |
    —mylist.c
    |
    —student.c

mylist.h和mylist.c写有链表创建和文件读写,student.c则是写界面和功能。


上篇文章中主要讲了学生信息管理系统的界面内容,本文将重点介绍单链表的创建和文件的读写操作。
单链表


什么是链表?

链表是一种链式存储的数据结构,与内存存储连续的数组不同,链表中以结点为单位,相邻结点用指针相连,结点在内存中也不是连续存储的。

结点包括数据域和指针域,每个结点只有一个链域的链表称为单链表



节点结构

基于C语言的学生管理系统(含文件)(二)



单链表结构

基于C语言的学生管理系统(含文件)(二)



本文中单链表与项目结合讲解。


首先用结构体声明学生信息和链表结点,结点中又包括学生信息和指向结点的指针,这样结点即可以存放数据,又可以指向下一个结点。

struct student//学生信息
{

  char id[15];
  char name[10];
  char gender[10];
  int age;
  float math;
  float english;
  float sumscore;
};
struct Node//链表结点
{

  struct student data;
  struct Node *next;
};



创建一个链表


链表有很多种,在这只介绍最简单的单链表(含空头结点)。首先创建一个空头结点,链表的增长都在这个头结点的基础上进行。


基于C语言的学生管理系统(含文件)(二)


//创建一个有表头链表
struct Node *creatList()
{
    //有表头链表:第一个结点不存放数据
    //无表头链表:第一个结点存放数据
    //产生一个结构体变量
    struct Node *StudentListhead;
    //为其动态分配内存
    //程序结束后,系统自动回收
    StudentListhead=(struct Node*)malloc(sizeof(struct Node));
    //初始化
    StudentListhead->next =NULL;

    return StudentListhead;
}



创建一个新结点并插入到链表中


将结点插入链表分为两种情况:

一是链表中一个结点也没有,那么可以直接将新结点插入到头结点后面;


二是链表中已经有了结点,则不仅要将头结点指向新节点,还要将新结点指向先前头结点的下一个结点(头插法)。


单链表插入图形

基于C语言的学生管理系统(含文件)(二)



//创建一个新结点
struct Node *creatnode(struct student data)
{
  //为新结点动态分配内存
  //程序结束后,系统自动回收
  struct Node *newnode=(struct Node*)malloc(sizeof(struct Node));

  newnode->data = data;
  newnode->next=NULL;

  return newnode;
}
//插入新结点:录入信息
void insertnode(struct Node *StudentListhead,struct student data)
{
  //接收creatnode()返回值
  struct Node *newnode=creatnode(data);

  //使用头插法将新结点插入链表中
  newnode->next=StudentListhead->next;
  StudentListhead->next=newnode;
}



删除结点


从链表中删除指定结点,就是将结点从链表中摘除以及将删除结点所占的内存空间释放。


拆除结点只需将所删除结点的前驱结点直接指向所删除结点的后驱结点即可。


最终使用free()手动释放该结点。


项目中使用学号和姓名删除学生,源码类似,只展示按学号的代码。(下文查找,排序亦是如此)


单链表删除图形

基于C语言的学生管理系统(含文件)(二)



//按学号删除学生信息
void Deletenode_byid(struct Node *StudentListhead,char *id)
{
    //定义前置结点并指向头结点
    struct Node *posFrontNode=StudentListhead;
    //定义一个位置结点并指向头结点的下一个结点
    struct Node *posNode=StudentListhead->next;

    if(posNode==NULL)//当位置结点为NULL,即链表为空时
    {
       printf("\n\t\t\t\t无相关内容,无法删除!\n");
       return;
    }
    else
    {
       //若链表不为空,循环链表,查找要删除的学生
       while(strcmp(posNode->data.id,id))
       {
          posFrontNode=posNode;
          posNode= posFrontNode->next;

          if(posNode == NULL)//未找到相关学生
          {
             printf("\n\t\t\t\t无相关内容,无法删除!\n");
             return;
          }
       }
       //若找到,则删除相关学生
       posFrontNode->next=posNode->next;
       free(posNode);
       printf("\n\t\t\t\t删除成功!\n");
    }
}



查找结点


从头结点开始遍历整个链表,将要查找的学生学号与结点数据域的学生学号一一对比,查找成功则返回该结点,并将该学生信息打印到界面上。

//按学号查找学生
struct Node *searchnode_byid(struct Node *StudentListhead,char *id)
{
    //定义移动结点并指向头结点的下一个结点
    struct Node *pmove=StudentListhead->next;

    if(pmove==NULL)//链表为空
    {
        //返回空指针
       return pmove;
    }
    else
    {
       //若链表不为空,遍历链表,查找学生
       while(strcmp(pmove->data.id,id))
       {
          pmove=pmove->next;
   
          //若未查到,返回空指针
          if(pmove==NULL)
          {
             break;
          }
       }
       return pmove;
    }
}


//打印查找的学生信息
void printsearch(struct Node *curnode/*当前结点*/)
{
 printf("\t学号\t\t姓名\t性别\t年龄\t高数成绩\t英语成绩\t成绩总和\n");
 printf("\t%-10s\t%-4s\t%-6s\t%-3d\t%.1f\t\t%.1f\t\t%.1f\n",curnode->data.id,curnode->data.name,curnode->data.gender,curnode->data.age,curnode->data.math,curnode->data.english,curnode->data.sumscore);
}



排序结点


采用冒泡排序将链表排序,即每次比较两个相邻的结点数据域,如果它们的顺序错误就把它们交换过来。


void sortList_byid(struct Node *StudentListhead)//按学号排序
{
  //定义临时结构体变量
  struct student temp;
  //定义前置结点并指向头结点
  struct Node *posFrontNode=StudentListhead;
  //定义一个位置结点并指向头结点的下一个结点
  struct Node *posNode=StudentListhead->next;

  if(posNode==NULL)//当位置结点为NULL,即链表为空时
  {
   printf("\n\t\t\t\t无相关内容,无法排序!\n");
   return;
  }
  //遍历链表
  while(posNode!=NULL)
  {
   while(posNode->next != NULL)
   {
    if(strcmp(posNode->data.id,posNode->next->data.id) > 0)//按学号从低到高排序
    {
     temp=posNode->data;
     posNode->data=posNode->next->data;
     posNode->next->data=temp;
    }
    posNode=posNode->next;
   }
   posNode=posFrontNode->next;
   posFrontNode=posNode;
  }

}



文件写入


创建文件指针使用fopen()关联指定文件,创建临时指针遍历链表,然后使用fprintf()将链表中的数据写入到文件中,最后关闭文件流。


//将学生信息存入文件
void saveToFile(char *File_student,struct Node *StudentListhead)
{
  //定义文件指针并指向一个文件
 FILE *fpstu=fopen(File_student,"w");
 //定义临时结点指针
 struct Node *pmove=StudentListhead->next;

 while(pmove)//遍历链表
  {
    //将链表中学生信息存储到文件中
   fprintf(fpstu,"%s\t%s\t%s\t%d\t%.1f\t\t%.1f\t\t%.1f\n",pmove->data.id,
    pmove->data.name,pmove->data.gender,pmove->data.age,pmove->data.math,pmove->data.english,pmove->data.sumscore);
   pmove=pmove->next;
  }
  //关闭文件
  fclose(fpstu);
}



文件读取


创建文件指针使用fopen()关联指定文件,创建临时结构体变量,遍历文件,使用fscanf()读取文件中内容到临时变量,再将临时变量插入到链表中,最后关闭文件流。


//从文件中读取学生信息
void readFromFile(char *File_student,struct Node *StudentListhead)
{
  //定义文件指针并指向一个文件
 FILE *fpstu=fopen(File_student,"r");
  //如果不存在该文件,重新创建
  if(fpstu==NULL)
  {
   fpstu=fopen(File_student,"w");
  }

  //临时变量
  struct student tempData;

  //遍历文件
  while(fscanf(fpstu,"%s\t%s\t%s\t%d\t%f\t\t%f\t\t%f\n",tempData.id,
   tempData.name,tempData.gender,&tempData.age,&tempData.math,&tempData.english,&tempData.sumscore)!=EOF)
  {
   //将学生信息插入到链表
   insertnode(StudentListhead,tempData);
  }
  //关闭文件
  fclose(fpstu);
}


以上便是项目的全部内容,希望对你有所帮助。



基于C语言的学生管理系统(含文件)(二)

作者:瑾年辰良
一位热爱生活的快乐青年



"即见君子,云胡不喜?"