针对在校大学生的C语言入门学习——学生管理系统
发布时间
阅读量:
阅读量
针对在校大学生的C语言入门学习——学生管理系统
-
有了前面的链表作为数据结构,接下来我们就开始学生管理系统的项目。项目的功能主要就是对学生信息的增、删、改、查。我简单画了一个图来表达一下我们的需求。

-
主界面显示学生的信息,可以根据姓名和各科的成绩升序降序排序。修改、删除、添加都会切换到其他界面进行。数据的持久化我就使用C语言的文件读写来实现。
学生结构体封装
/*
学生信息结构体
*/
typedef struct Student
{
char name[32];
float language;
float english;
float math;
}Student;
- 结构体包含存放名字的字符数组和语数外三科成绩。这里要强调的是我们不可以在结构体中定义一个指针变量来保存名字。比如
char* name这样是错误的。因为指针变量只能保存地址,不能保存字符串。
界面切换
- 每个界面都要定义一个函数。但是切换界面的时候如果只是简单的函数调用,那程序就太不灵活了。我使用函数指针的形式来实现界面的切换。请看下面代码。
int mainMenu();
int addMenu();
int updateMenu();
int removeMenu();
int(*currentMenu)() = mainMenu;
int main(void)
{
while(1)
{
if(currentMenu())
break;
}
return 0;
}
/*
主菜单
*/
int mainMenu()
{
}
/*
添加学生信息菜单
*/
int addMenu()
{
}
/*
修改学生信息菜单
*/
int updateMenu()
{
}
/*
删除学生信息菜单
*/
int removeMenu()
{
}
- currentMenu是函数指针,在项目中它将会指向每个界面函数。main函数中使用currentMenu来执行各个界面。每个界面在结束之前要给currentMenu指向下一个界面,然后结束当前界面的函数。界面函数的返回值是1就结束整个程序,否则继续执行下一个界面。下面我把几个界面函数添加一些显示内容和切换的操作。
主菜单
/*
主菜单
*/
#define MEIN_MENU_ITEMS 3
void mainMenuShow(int cursor)
{
char* menus[] = {"添加学生信息","修改学生信息","删除学生信息"};
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf(" --------------------------------- \n");
printf("input: 1 up 2 down 3 ok\n");
int i;
for(i = 0;i < MEIN_MENU_ITEMS;i++)
{
if(i == cursor)//当前选中项前面加*
printf("*%s\n", menus[i]);
else
printf("%s\n",menus[i]);
}
}
int mainMenu()
{
int cursor = 0;
while(1)
{
mainMenuShow(cursor);
int ctrl;
scanf("%d",&ctrl);
}
}

- 现在有了主菜单的显示内容,下一步是移动光标。
/*
调整光标
*/
int cursorCtrl(int* cursor)
{
int ctrl;
scanf("%d", &ctrl);
switch(ctrl)
{
case 1://上移
*cursor = *cursor==0?MEIN_MENU_ITEMS-1:*cursor-1;
return 0;
case 2://下移
*cursor = *cursor==MEIN_MENU_ITEMS-1?0:*cursor+1;
return 0;
case 3://ok
return 1;
}
}
- 函数返回0表示用户操作的是up和down,返回1表示用户操作的是ok。下面请看mainMenu中的调用。
int mainMenu()
{
int cursor = 0;
while(1)
{
mainMenuShow(cursor);
if(cursorCtrl(&cursor))
{
//根据光标做事
}
}
}
- 接下来我们封装用户选中ok的时候的函数。目前只有三件事可做,那就是分别切换到不同的界面。
/*
主菜单功能函数
*/
int mainMenuCtrl(int cursor)
{
switch(cursor)
{
case 0://跳转到 添加学生信息界面
currentMenu = addMenu;
return 1;
case 1://跳转到 修改学生信息界面
currentMenu = updateMenu;
return 1;
case 2://跳转到 删除学生信息界面
currentMenu = removeMenu;
return 1;
}
}
- 函数返回1表示要切换界面,返回0表示不切换界面。目前我们只做切换界面的功能。下面是另外三个界面的简单实现。
/*
主菜单
*/
int mainMenu()
{
int cursor = 0;
while(1)
{
mainMenuShow(cursor);
if(cursorCtrl(&cursor))
{
if(mainMenuCtrl(cursor))
{
return 0;
}
}
}
}
/*
添加学生信息菜单
*/
int addMenu()
{
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf("| Add Menu |\n");
printf(" --------------------------------- \n");
printf("input: 0 back\n");
int ctrl;
scanf("%d",&ctrl);
if(ctrl == 0)
{
currentMenu = mainMenu;
return 0;
}
}
/*
修改学生信息菜单
*/
int updateMenu()
{
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf("| Update Menu |\n");
printf(" --------------------------------- \n");
printf("input: 0 back\n");
int ctrl;
scanf("%d",&ctrl);
if(ctrl == 0)
{
currentMenu = mainMenu;
return 0;
}
}
/*
删除学生信息菜单
*/
int removeMenu()
{
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf("| Remove Menu |\n");
printf(" --------------------------------- \n");
printf("input: 0 back\n");
int ctrl;
scanf("%d",&ctrl);
if(ctrl == 0)
{
currentMenu = mainMenu;
return 0;
}
}
- 目前我们实现了4个界面之间的切换。按照我的方法,4个界面之间可以任意切换,非常方便。
添加学生信息界面
- 我们现在开始认真做每个界面,从添加学生信息界面开始做是为了测试方便。因为有个数据我们才好进行后面的操作。
- 首先创建链表,使用全局的指针。
Link* link;//学生信息链表
int main(void)
{
link = createLink();
while(1)
{
if(currentMenu())
break;
}
deleteLink(link);
return 0;
}
/*
添加学生信息菜单
*/
int addMenu()
{
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf("| Add Menu |\n");
printf(" --------------------------------- \n");
while(1)
{
Student* stu = (Student*)malloc(sizeof(Student));
//输入数据
printf("input name:\n");
scanf("%s", stu->name);
printf("input language:\n");
scanf("%f", &stu->language);
printf("input english:\n");
scanf("%f", &stu->english);
printf("input math:\n");
scanf("%f", &stu->math);
printf("input 0 ok 1 cancel\n");
int ctrl;
scanf("%d",&ctrl);
if(ctrl == 0)//ok
{
addItem(link, stu);//数据添加链表
}
else//取消
{
currentMenu = mainMenu;
free(stu);
return 0;
}
}
}
- 操作可能有点小辛苦,而且想取消返回主界面的话还得输出一个学生的信息才行。在控制台上目前没有想到更好的操作方式。接下来主界面添加显示学生信息的代码。
void mainMenuShow(int cursor)
{
char* menus[] = {"添加学生信息","修改学生信息","删除学生信息"};
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf(" --------------------------------- \n");
printf("input: 1 up 2 down 3 ok\n");
int i;
for(i = 0;i < MEIN_MENU_ITEMS;i++)
{
if(i == cursor)//当前选中项前面加*
printf("*%s\n", menus[i]);
else
printf("%s\n",menus[i]);
}
//遍历链表显示学生学生信息
printf("id name language english math\n");
Iterator iter;
int id = 0;
for(iter = createIterator(link);!iteratorEnd(iter);iter = nextIterator(iter),id++)
{
Student* stu = (Student*)iteratorData(iter);
printf("%2d %8s %8.1f %7.1f %4.1f\n", id, stu->name, stu->language, stu->english, stu->math);
}
}

- 输入部分只演示一点点。接下来是返回主界面的显示。

数据持久化
- 如果大家把上面的代码运行过就会体会到,输入数据真的太麻烦了。为了不每次都输入一大堆数据,我们先把数据持久化做了。这也是无奈之举……我的策略就是把数据全部保存到文件中。
/*
链表写入文件
*/
void saveData()
{
FILE* file = fopen("data", "w");
Iterator iter;
for(iter = createIterator(link);!iteratorEnd(iter);iter = nextIterator(iter))
{
Student* stu = (Student*)iteratorData(iter);
fwrite(stu, sizeof(Student), 1, file);
}
fclose(file);
}
/*
读取文件数据插入链表
*/
void readData()
{
FILE* file = fopen("data", "r");
if(file == NULL)//没有数据
return;
while(1)
{
Student* stu = (Student*)malloc(sizeof(Student));
if(fread(stu, sizeof(Student), 1, file) == 0)//数据读取完毕
{
free(stu);
return;
}
addItem(link, stu);
}
}
- 文件的读写就很简单了,函数不熟的同学可以查查资料。我用的都是C语言自带的函数。需要强调的是我每次保存数据都是把之前的文件删除后重新创建文件并写入数据。因为数据可能会发生增加删除排序等不可预知的改动,所以重新写入是一个不错的选择。接下来就是把main函数和主菜单稍作修改。
#define MEIN_MENU_ITEMS 4
int main(void)
{
link = createLink();
readData();
while(1)
{
if(currentMenu())
break;
}
deleteLink(link);
return 0;
}
- 主菜单的选项个数改4。在main函数中初始化是读取文件生成链表。
char* menus[] = {"添加学生信息","修改学生信息","删除学生信息","保存学生信息"};
- mainMenuShow函数的显示菜单选项字符串添加保存学生信息项。
/*
主菜单功能函数
*/
int mainMenuCtrl(int cursor)
{
switch(cursor)
{
case 0://跳转到 添加学生信息界面
currentMenu = addMenu;
return 1;
case 1://跳转到 修改学生信息界面
currentMenu = updateMenu;
return 1;
case 2://跳转到 删除学生信息界面
currentMenu = removeMenu;
return 1;
case 3:
saveData();
return 0;
}
}
- 添加保存学生信息功能不跳转界面,所以返回0。
修改学生信息界面
- 现在我们可以放心的写剩下的功能,就从修改学生信息界面继续吧。
/*
修改学生信息菜单
*/
int updateMenu()
{
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf("| Update Menu |\n");
printf(" --------------------------------- \n");
while(1)
{
printf("input: update id\n");
int id;
scanf("%d",&id);
if(id>=link->size)//超出链表范围
{
printf("out of range\n");
continue;
}
Student* stu = (Student*)malloc(sizeof(Student));
//输入数据
printf("input: name:\n");
scanf("%s", stu->name);
printf("input: language:\n");
scanf("%f", &stu->language);
printf("input: english:\n");
scanf("%f", &stu->english);
printf("input: math:\n");
scanf("%f", &stu->math);
printf("input: 0 ok 1 cancel\n");
int ctrl;
scanf("%d",&ctrl);
if(ctrl == 0)//ok
{
updateItemByIndex(link, id, stu);//修改数据
}
else//取消
{
currentMenu = mainMenu;
free(stu);
return 0;
}
}
}
- 有的同学可能发现学生信息输入的部分和添加学生信息界面一模一样,所以这部分我们应该封装成函数。
/*
创建学生结构体
*/
Student* createStudent()
{
Student* stu = (Student*)malloc(sizeof(Student));
//输入数据
printf("input: name:\n");
scanf("%s", stu->name);
printf("input: language:\n");
scanf("%f", &stu->language);
printf("input: english:\n");
scanf("%f", &stu->english);
printf("input: math:\n");
scanf("%f", &stu->math);
}
-
addMenu和updateMenu函数中调用即可,我就不在这里浪费篇幅了。


- 三娃被修改成二娃,成绩也相应的修改成功。
删除学生信息界面
- 最后一个界面了,不知道有多少人能够耐心的读到这里呢……写这篇文章既是对职业的热爱,也是希望同样热爱编程的同学能够通过我的文章有所收获。
/*
删除学生信息菜单
*/
int removeMenu()
{
printf(" --------------------------------- \n");
printf("| Welcome to use |\n");
printf("| Sea Student Manage System |\n");
printf("| Remove Menu |\n");
printf(" --------------------------------- \n");
printf("input: remove id\n");
int id;
scanf("%d",&id);
if(id >= link->size)
{
printf("out of range\n");
}
else
{
removeItemByIndex(link, id);
}
currentMenu = mainMenu;
return 0;
}
-
不管删除成功与否都直接返回主界面,个人认为这样的操作合理。当然了,我们不讨论什么样的操作合理,只讨论完成功能的代码!


- 可以看到大娃被成功删除。
排序查看
- 关于排序,我的设想是把结构体的每个属性都升序降序排序。看起来很麻烦,其实还好吧。如果上一课的链表原理掌握了,我们只需要写8个比较函数就ok。
/*
名字升序
*/
int cmpNameUp(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
return strcmp(stu1->name, stu2->name);
}
/*
名字降序
*/
int cmpNameDown(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
return strcmp(stu1->name, stu2->name)*-1;
}
/*
语文升序
*/
int cmpLanguageUp(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
if(stu1->language > stu2->language)
{
return 1;
}
else if(stu1->language < stu2->language)
{
return -1;
}
else
{
return 0;
}
}
/*
语文降序
*/
int cmpLanguageDown(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
if(stu1->language < stu2->language)
{
return 1;
}
else if(stu1->language > stu2->language)
{
return -1;
}
else
{
return 0;
}
}
/*
数学升序
*/
int cmpMathUp(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
if(stu1->math > stu2->math)
{
return 1;
}
else if(stu1->math < stu2->math)
{
return -1;
}
else
{
return 0;
}
}
/*
数学降序
*/
int cmpMathDown(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
if(stu1->math < stu2->math)
{
return 1;
}
else if(stu1->math > stu2->math)
{
return -1;
}
else
{
return 0;
}
}
/*
英语升序
*/
int cmpEnglishUp(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
if(stu1->english > stu2->english)
{
return 1;
}
else if(stu1->english < stu2->english)
{
return -1;
}
else
{
return 0;
}
}
/*
英语降序
*/
int cmpEnglishDown(void* s1, void* s2)
{
Student* stu1 = (Student*)s1;
Student* stu2 = (Student*)s2;
if(stu1->english < stu2->english)
{
return 1;
}
else if(stu1->english > stu2->english)
{
return -1;
}
else
{
return 0;
}
}
- 接下来mainMenuShow函数中显示字符串数组添加选项。
char* menus[] = {"添加学生信息","修改学生信息","删除学生信息","保存学生信息",
"名字升序","名字降序","语文升序","语文降序","数学升序","数学降序",
"英语升序","英语降序"};
- 选项数量宏定义
#define MEIN_MENU_ITEMS 12
/*
主菜单功能函数
*/
int mainMenuCtrl(int cursor)
{
switch(cursor)
{
case 0://跳转到 添加学生信息界面
currentMenu = addMenu;
return 1;
case 1://跳转到 修改学生信息界面
currentMenu = updateMenu;
return 1;
case 2://跳转到 删除学生信息界面
currentMenu = removeMenu;
return 1;
case 3:
saveData();
return 0;
case 4:
sort(link, cmpNameUp);
return 0;
case 5:
sort(link, cmpNameDown);
return 0;
case 6:
sort(link, cmpLanguageUp);
return 0;
case 7:
sort(link, cmpLanguageDown);
return 0;
case 8:
sort(link, cmpMathUp);
return 0;
case 9:
sort(link, cmpMathDown);
return 0;
case 10:
sort(link, cmpEnglishUp);
return 0;
case 11:
sort(link, cmpEnglishDown);
return 0;
}
}
总结
- 以上就是学生管理系统的所有代码。也是我这一系列C语言教学的最后练习。希望能对热爱编程的同学带来一些帮助。专做专职讲师之后,我的热爱不仅仅是对技术方面。如果我传授的知识能对热爱编程的同学提供一点点的帮助都会让我由衷的产生成就感。学习中遇到问题的同学也欢迎留言交流。
源码下载点击
全部评论 (0)
还没有任何评论哟~
