main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Menu.h"
#include "function.h"
Link phead;
int main()
{
short i;
//short I;
phead = NewList(); //调用NewList函数,并返回一个指向该新链表的指针
//此处用标签的方式配合goto语句实现循环,也可使用while进行
Menu1: Menu_A(); //调用菜单函数,显示菜单
back: scanf("%hd",&i); //读取用户操作并进行相应的响应
if(i == -1){
exit(1);
}
// 判断键盘输入
switch(i){
case 1:Distinguish(1);goto Menu1;
case 2:Distinguish(2);goto Menu1;
case 3:Distinguish(3);goto Menu1;
case 4:Distinguish(4);goto Menu1;
default:printf("无匹配项,请重新输入\n");goto back;
}
}
menu.c
菜单功能实现源文件。
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "Menu.h"
void Menu_A(){ //主菜单函数
SetConsoleOutputCP(CP_UTF8);
unsigned short i,k;
for(k=1;k<14;k++){ //利用循环画出边框
for(i=1;i<61;i++){
if(k == 1 || k == 2 || k == 13 || k == 12){
printf("%c",42);
}
}
//显示菜单选项
if(k == 4)
printf(" 1.查询学生信息 ");
if(k == 6)
printf(" 2.添加学生信息 ");
if(k == 8)
printf(" 3.删除学生信息 ");
if(k == 10)
printf(" 4.显示所有信息 ");
printf("\n");
}
printf("请进行输入(按-1返回)\n");
}
menu.h
#ifndef MENU_H
#define MENU_H
void Menu_A();
#endif
function.c
NewList函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "function.h"
// 以下代码为初始化初链表数据
char name[5][20] = {"TOM", "Bob", "Mike", "Alice", "Jenny"};
char sex[5][20] = {"male", "male", "male", "female", "female"};
int number[5] = {1, 2, 3, 4, 5};
extern Link phead;
Link NewList()
{ // 新建链表
Link rear;
Link head;
// 下面语句为申请一个头结点,并且对头结点进行初始化
head = (Link)malloc(sizeof(Node));
strcpy(head->data.Name, "NULL");
strcpy(head->data.Sex, "NULL");
head->data.Number = 0;
head->next = NULL;
rear = head;
for (short i = 0; i < 5; i++)
{ // 尾插法
Link node = (Link)malloc(sizeof(Node)); // 新建一个结点
strcpy(node->data.Name, name[i]); // 逐一为链表中每一个结点分配对应的数据
strcpy(node->data.Sex, sex[i]);
node->data.Number = number[i];
rear->next = node;
rear = node;
}
rear->next = NULL;
return head;
}
NewList的函数用于新建一个链表,其调用放在了主函数中最靠前的位置,用于初始化一个默认链表,后续的所有操作将在基于这个链表的基础之上进行。采用链表的尾插法,对每个结点进行正序链接。
ClearInputBuff函数
void ClearInputBuff()
{ // 清空键盘输入缓存
char ch;
while ((ch = getchar()) != '\n' && ch != EOF)
{
}
}
该函数用于清空输入缓冲区的所有字符直到遇到换行符或文件结束符。用于排除可能存在的多余输入的干扰。
CountList函数
char CountList(Link head)
{ // 统计链表中目前项数
char count = 0;
Link p = head; //初始化工作指针p
while (p->next != NULL)
{
count++;
p = p->next;
}
return count;
}
该函数我们用于更新链表的结点数,在每次调用后返回目前链表中还有多少个结点。
StringInput函数
void StringInput(char *p)
{ // 输入功能函数。读取用户键盘输入并且剔除换行符
if (fgets(p, 20, stdin) != NULL)
{
p[strcspn(p, "\n")] = '\0'; // 查找到换行符位置并将其更换为结束符
}
else
{
printf("Wrong!\n");
}
}
该函数我们用于输入一个字符串存储到一个字符数组中。这里不直接使用fgets的原因在于fgets从键盘读入字符串时,会将换行符'\n'一并读入并存储。而这个多余的换行符在接下来的程序中可能会给我们带来非预期的格式错误。因此我们通过该函数来剔除掉多余的换行符。
CheckList函数
void CheckList(Link head)
{ // 遍历链表,实现全部输出学生信息
Link p = head;
short i;
while (p->next != NULL)
{
printf("姓名:%s\n", p->next->data.Name);
printf("性别:%s\n", p->next->data.Sex);
printf("学号:%d\n", p->next->data.Number);
printf("\n\n");
p = p->next;
}
while (1)
{
printf("按-1返回\n");
if (scanf("%hd", &i) != 1)
{ // 检测输入是否为非数值型
printf("非数值型输入,请重试\n");
ClearInputBuff(); // 清空输入缓冲区。防止因非数值型输入而导致无限循环bug
}
if (i == -1)
return; // 结束函数
else
printf("输入错误,请重试\n");
}
}
SearchStudents函数
void SearchStudents(Link head)
{ // 通过学号查找学生信息
short n;
Link p = head;
back1:
printf("请输入要查询的学生学号(按-1返回)\n");
if (scanf("%hd", &n) != 1)
{ // 检测输入是否为非数值型
printf("非数值型输入,请重试\n");
ClearInputBuff(); // 清空输入缓冲区。防止因非数值型输入而导致无限循环bug
goto back1;
}
if (n <= 0 && n != -1)
{ // 检测输入的学号是否合理
printf("错误输入,请重试\n");
goto back1;
}
if (n == -1)
{
return; // 结束程序
}
char count = CountList(head); // 获取目前链表项数
while (p->next != NULL && n <= count)
{
if (p->next->data.Number == n)
{ // 遍历整个表,对学号进行比较。若符合则输出相对应的数据
printf("姓名:%s\t", p->next->data.Name);
printf("性别:%s\t", p->next->data.Sex);
printf("学号:%d\n", p->next->data.Number);
break;
}
p = p->next;
}
if (p->next == NULL || n > count)
{
printf("该学号不存在,请重新输入\n");
goto back1;
}
p = head; // 重置工作指针
goto back1;
}
该函数用于查询表中学生信息。由用户输入一个学生编号来对链表中的学生信息进行查询,若用户输入的编号合理,则遍历整个链表直到找到相匹配的结点,并输出其学生的个人数据。若用户输入不合理,则给出相应提示,并返回到重新输入,直到用户输入正确编号或退回主菜单。
AddElement函数
void AddElement(Link head)
{
Link p = head;
short i = CountList(head); // 链表元素个数
while (p->next != NULL)
{ // 移动工作指针至目前链表最后一个结点
p = p->next;
}
while (1)
{
ClearInputBuff(); // 清空输入缓冲区
Link node = malloc(sizeof(Node)); // 创建新结点
// 创建临时变量,存储学生信息
char _Name[20];
char _Sex[20];
// 输入要添加的学生信息,并将其存储在临时变量中
printf("输入学生姓名(按-1返回)\n");
StringInput(_Name);
if (_Name[1] == 49 && _Name[0] == 45)
{ // 检测输入是否为-1
return;
}
printf("请输入学生性别\n");
StringInput(_Sex);
// 将输入的学生信息分配到新结点中
strcpy(node->data.Name, _Name);
strcpy(node->data.Sex, _Sex);
node->data.Number = ++i; // 由于是逐项添加学生,因此学号可借由递增自行计算
// 对结点进行链接
p->next = node;
node->next = NULL;
p = node;
// 用户自行选择接下来的操作
printf("(输入任意键继续或按-1返回)\n");
short temp;
scanf("%hd", &temp);
if (temp == -1)
return; // 退出函数
}
}
该函数用于添加学生信息。由于要实现的效果是依序往后添加学生信息,因此我们先调用countlist函数获取目前链表的结点数目后,将工作指针指向最后一位结点。随后新建结点,输入如新学生信息,进行链接。
DeleteStudentInfo函数
void DeleteStudentInfo(Link head)
{ // 定义删除学生信息函数
Link p = head; // 初始化工作指针
short n;
short cnt = CountList(head); // 调用统计函数,计算目前链表的结点数
while (1)
{
// 对输入的数据进行检查
while (1)
{
printf("请输入您要删除学生的学号(按-1返回)\n");
ClearInputBuff(); // 清空输入缓冲区,为接下来的输入做准备
if (scanf("%hd", &n) != 1)
{
printf("非数值型输入,请重试\n");
continue;
}
if (n == -1)
{
return;
}
if (n > cnt || ((n <= 0) && (n != -1)))
{
printf("该学生不存在,请重新输入\n");
continue;
}
break;
}
// 移动工作指针到要删除结点的前驱结点
for (short i = 1; i < n; i++)
{
p = p->next;
}
// 删除结点,释放空间
Link node = p->next;
p->next = node->next;
free(node);
printf("成功删除\t");
cnt--; // 更新整个链表结点数
while (p->next != NULL)
{ // 对删除项之后的所有项学生学号进行更新
p->next->data.Number -= 1;
p = p->next;
}
p = head; // 重置指针
// 流程控制
printf("按任意键继续(或按-1退出)\n");
short temp;
scanf("%hd", &temp);
if (temp == -1)
{
return;
}
}
}
该函数用于删除用户指定的学生信息。由用户输入一个编号,对编号进行合理性检查。若不合理则打回重新输入,合理则将工作指针移到其前驱结点,对目标结点进行摘除。
function.h
#ifndef FUNCTION_H
#define FUNCTION_H
typedef struct Student{ //定义学生信息结构体
char Name[20];
char Sex[5];
int Number;
}stu;
typedef struct Node{ //定义结点
stu data;
struct Node* next;
}Node,*Link;
void Distinguish(short i); //传递函数
Link NewList(); //新建链表函数
void CheckList(Link head); //遍历链表函数
void SearchStudents(Link head); //学生信息查询函数
char CountList(Link head); //统计链表项数函数
void ClearInputBuff(); //清空键盘输入缓冲区
void AddElement(Link head); //添加学生信息函数
void DeleteStudentInfo(Link head); //删除学生信息函数
void StringInput(char *p); //字符串输入函数
#endif
程序部分运行示意图