C语言学生成绩管理系统

C语言——学生成绩管理系统详解

引言:最近写了C语言的大作业,感觉做得还行,记录一下,给后人一些参考,给自己留更深印象方便后续改进!

不知道发了这篇博客会不会认为我抄袭…害怕.jpg

一、系统概述

使用单向链表和文件作为基本数据结构,设计一个学生成绩管理程序,管理某学校学生成绩。
[1]插入数据
[2]修改对应数据项的数据
[3]删除对应学号的信息
[4]查找某学号的信息
[5]查找不及格学生
[6]对学生成绩进行排名
[7]统计各个等级的学生人数
[8]输出所有学生信息
[9]分页显示学生信息

二、数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
//枚举
enum sex{women,man};
struct StuLink{
int xh; //学号
char xm[20]; //姓名
enum sex xb; //性别
int cj; //成绩,范围[0,100] 前四个为输入项
char dj; //等级
int mc; //名次
struct StuLink *next; //下一项
};
char sex[][3]={"女","男"}; //用于输出“男女”中文字符
int size=sizeof(struct StuLink); //节点字节大小
  1. 注意姓名是xm[20],是字符串;而等级是cj,单个字符;这会影响到赋值问题
  2. 其中姓名、性别、成绩是输入项,名字、等级、名次由程序计算得出

三、自定义函数说明

1. main 函数

函数首部:void main

参数列表:
[1] menu1:用于接收一级菜单选择
[2] menu2:用于接收一级菜单选择
[3] i:用于 for 循环迭代变量
[4]n:用于接收“插入数据”功能的学生个数
[5] dj_add[5]:用于储存各等级人数
[6] head:用于储存头节点的地址
[7]pw:用于储存尾节点的地址

返回值:无

实现功能:选择对应功能并实现

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
void main(){
// 声明变量
int menu1,menu2; //menu1:一级菜单 menu2:二级菜单
int i=0,n,dj_add[5]={0,0,0,0,0};
struct StuLink *head=NULL,*pw; //*head:学生信息链表头指针 pw:尾节点

// 声明函数
// 从数据文件中逐行读取学生信息生成学生链表,返回头指针
struct StuLink *ReadFromFile();
// 先将学生链表按学号升序排序,再将学生链表中的数据逐行保存到数据文件
void SaveToFile(struct StuLink *head);
// SortLink函数:按指定数据项的顺序【1:学号(升序)】或者【2:成绩(降序)】对学生链表进行排序
struct StuLink *SortLink(struct StuLink *head,int i);
// InsertNode函数:在链表尾插入一个新结点。新结点的学号是链表中最大学号加1,姓名和成绩从键盘输入
// (注意:成绩必须在[0,100]区间的整数),根据成绩计算等级。
// 注意:插入结点会导致链表中各结点名次的变化。
struct StuLink *InsertNode(struct StuLink *pw);
// EditNode函数:修改链表中指定学号的结点(学号不能修改,成绩必须在[0,100]区间的整数)
// 注意:当修改成绩时会导致等级和名次的变化
void EditNode(struct StuLink *head);
//DeleteNode函数:删除链表中指定学号的结点。注意:删除结点会导致链表中各结点名次的变化
struct StuLink *DeleteNode(struct StuLink *head);
// QueryNode函数:查询链表中指定学号的结点,并显示查询结果。
void QueryNode(struct StuLink *head);
// QueryLink函数:查询链表中不及格的所有结点,并显示查询结果。
void QueryLink(struct StuLink *head);
//RankLink函数:计算链表中每个结点的名次。名次规则:按成绩降序排名,从第1名开始依次排名,
//若出现并列名次,则名次需要叠加。例如,若出现5个并列第1名,则没有第2名,下一个名次是第6名,依此类推。
void RankLink(struct StuLink *head);
//AnalysisLink函数:统计并返回各等级人数。等级标准:
//A:90及以上 B:80及以上 C:70及以上 D:60及以上 E:60以下
void AnalysisLink(struct StuLink *head,int *dj_add);
//OutputLink_1函数:按指定数据项的顺序【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表、各等级人数。
//学生成绩表每行输出一个学生信息(依次为学号、姓名、性别、成绩、等级和名次,各项间以1个空格隔开),
//各等级人数分行输出。
void OutputLink_1(struct StuLink *head,int i);
//OutputLink_2函数:分页显示全部学生的信息。
//分页功能:每页显示10个学生信息,有上一页、下一页、首页和最后一页的翻页功能。
void OutputLink_2(struct StuLink *head,int i);

// 执行语句
head=ReadFromFile(); //读取文件到链表,获取头指针


// 菜单
while(1){
system("cls"); //清控制台,Windows
printf("========================================\n");//40个=
printf("= =\n");
printf("= 学生成绩管理程序 =\n");
printf("= by:xxx =\n");
printf("========================================\n");
printf("= =\n");
printf("= 1-数据维护 2-数据查询 =\n");
printf("= 3-统计分析 4-报表输出 =\n");
printf("= 0-退出 =\n");
printf("= =\n");
printf("========================================\n");
// printf("\n");
printf("请选择:");
scanf("%d",&menu1);
switch(menu1){ //一级菜单
case 1:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("= 1-数据插入 2-数据修改 =\n");
printf("= 3-数据删除 0-返回上级 =\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
printf("请输入要插入的学生个数(n>0): ");
scanf("%d",&n);
while(n<=0){
printf("!!!请输入正确的学生个数!!!: ");
scanf("%d",&n);
}
pw=head;
if(pw){
while(pw->next) //获取尾部节点
pw=pw->next;
}

for(i=0;i<n;i++){
pw=InsertNode(pw);
if(head==NULL) head=pw;
}
// 计算名次
RankLink(head);
// SaveToFile(head);
break;
case 2:
EditNode(head);
// 计算名次
RankLink(head);
// SaveToFile(head);
break;
case 3:
head=DeleteNode(head);
// 计算名次
RankLink(head);
// SaveToFile(head);
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 2:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("=1-学号查询 2-不及格学生查询 0-返回上级=\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
QueryNode(head);
printf("\n"); system("pause");
break;
case 2:
QueryLink(head);
printf("\n"); system("pause");
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 3:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("= 1-成绩名次计算 2-成绩频次分析 =\n");
printf("= 0-返回上级 =\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
RankLink(head);
break;
case 2:
AnalysisLink(head,dj_add);
printf("'A'有%d人;'B'有%d人,'C'有%d人,'D'有%d人,'E'有%d人,共有%d人\n",
dj_add[0],dj_add[1],dj_add[2],dj_add[3],dj_add[4],dj_add[0]+dj_add[1]+dj_add[2]+dj_add[3]+dj_add[4]);
printf("\n"); system("pause");
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 4:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("=1-排序显示学生信息 2-分页显示学生信息=\n");
printf("= 0-返回上级 =\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
printf("请选择按【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表:");
scanf("%d",&i);
while((i!=1)&&(i!=2)){
printf("!!!请选择正确选项!!!1 or 2 : ");
scanf("%d",&i);
}
OutputLink_1(head,i);
printf("\n");
system("pause");
break;
case 2:
printf("请选择按【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表:");
scanf("%d",&i);
while((i!=1)&&(i!=2)){
printf("!!!请选择正确选项!!!1 or 2 : ");
scanf("%d",&i);
}
OutputLink_2(head,i);
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 0:
printf("========================================\n");
printf("= =\n");
printf("= 你已经退出学生管理系统 =\n");
printf("= =\n");
printf("========================================\n");
return; //退出main函数
}
}

}

2. ReadFromFile 函数

函数首部:struct StuLink *ReadFromFile()

参数列表:
[1]fp:用于储存储存在缓冲区的文件地址
[2]p1:用于储存开辟的新节点的地址
[3]p2:用于储存尾节点的地址
[4]head:用于储存首节点的地址
[5]ch:用于接收文件第一个字符

返回值:head 的地址

实现功能:
1.写打开文件
2.2.读取文件第一个字符,若是 EOF 文件终止符,则返回 head 的指针,函数结束;否则将 fp 的地址重置为文件开头
3.3.循环:若 fp 指针不为空,则开辟新节点,并且接入链表的尾节点(根据情况),读取 文件第一行信息并且赋值到 p1 指向的节点中
4.4.写关闭文件,返回 head 的地址

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct StuLink *ReadFromFile(){
FILE *fp; //指向student.dat文件
struct StuLink *p1,*p2,*head=NULL; //head:头节点 p1:开辟新节点 p2:尾节点
char ch;

if((fp=fopen("student.dat","r"))==NULL){ //读开student.dat
printf("打开student.dat失败\n");
exit(0);
}
ch=fgetc(fp); //读取文件第一个字符
if(ch==EOF){ //EOF文件终止符
printf("文件为空\n");
return head;
}else{
rewind(fp); // 将fp的地址重置为文件开头
}

while(!feof(fp)){
if((p1=(struct StuLink *)malloc(size))==NULL) {
printf("不能成功分配储存块");
exit(0);
}
p1->next=NULL;
if(head==NULL) head=p1; //首节点
else p2->next=p1; //非首节点,表尾插入新节点
p2=p1; // p2指向新的表尾结点
fscanf(fp,"%d %s %d %d %s %d\n",&p1->xh,p1->xm,&p1->xb,&p1->cj,&p1->dj,&p1->mc);
// 读取\n是防止fp以为文件未完
printf("%-5d%-10s%-5d%-5d%-5c%-5d\n",p1->xh,p1->xm,p1->xb,p1->cj,p1->dj,p1->mc);
}
fclose(fp); //关闭student.dat
return head;
}

3. SortLink 函数

函数首部:struct StuLink *SortLink(struct StuLink *head,int i)

参数列表:
[1]head:储存首节点的地址
[2]i:用于储存排序方式的选项
[3]xh:用于储存学号
[4]xb:用于储存性别
[5]cj:用于储存成绩
[6]mc:用于储存名次
[7]xm:用于储存姓名
[8]dj:用于储存等级
[9]p1:用于表示被比较节点的地址
[10]p2:用于表示比较节点的地址
[11]p3:用于表示中间节点的地址

返回值:head 的地址

实现功能:

  1. 判断 head 是否为空,为空则返回 head 并且结束函数
  2. 进行排序,快速排序,交换连个节点的数据项,而不是改变链表的节点信息
  3. 返回 head,结束函数

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct StuLink *SortLink(struct StuLink *head,int i){
if(head==NULL)return head;
// 使用交换数据的形式交换两个节点
int xh,xb,cj,mc;
char xm[20],dj;
// p1:被比较节点 p2:比较节点 p3:快速排序储存节点
struct StuLink *p1=head,*p2=head->next,*p3=p1;
switch(i){
case 1:
// 快速排序
while(p1->next){
p2=p1->next;
p3=p1;
while(p2){
if(p3->xh>p2->xh)
p3=p2;
p2=p2->next;
}
if(p3->xh<p1->xh){
xh=p3->xh; xb=p3->xb;
cj=p3->cj; mc=p3->mc;
strcpy(xm,p3->xm); dj=p3->dj;
p3->xh=p1->xh; p3->xb=p1->xb;
p3->cj=p1->cj; p3->mc=p1->mc;
strcpy(p3->xm,p1->xm); p3->dj=p1->dj;
p1->xh=xh; p1->xb=xb;
p1->cj=cj; p1->mc=mc;
strcpy(p1->xm,xm); p1->dj=dj;
}
p1=p1->next;
}
break;
case 2:
// 快速排序
while(p1->next){
p2=p1->next;
p3=p1;
while(p2){
if(p3->cj<p2->cj)
p3=p2;
p2=p2->next;
}
if(p3->cj>p1->cj){
xh=p3->xh; xb=p3->xb;
cj=p3->cj; mc=p3->mc;
strcpy(xm,p3->xm); dj=p3->dj;
p3->xh=p1->xh; p3->xb=p1->xb;
p3->cj=p1->cj; p3->mc=p1->mc;
strcpy(p3->xm,p1->xm); p3->dj=p1->dj;
p1->xh=xh; p1->xb=xb;
p1->cj=cj; p1->mc=mc;
strcpy(p1->xm,xm); p1->dj=dj;
}
p1=p1->next;
}
break;
}
return head;
}

4. SaveToFile 函数

函数首部:void SaveToFile(struct StuLink *head)

参数列表:
[1]head:用于表示首节点的地址
[2]fp:用于表示缓冲区文件的地址
[3]i:用于储存排序方式

返回值:无

实现功能:写开文件,循环将链表中的信息储存到文件中

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	void SaveToFile(struct StuLink *head){
FILE *fp;
int i=1;
head=SortLink(head,i);
struct StuLink *p=head;
if((fp=fopen("student.dat","w"))==NULL){
printf("写开文件失败!");
exit(0);
}
while(p){
// 写入文件
fprintf(fp,"%d %s %d %d %c %d\n",p->xh,p->xm,p->xb,p->cj,p->dj,p->mc);
p=p->next;
}
fclose(fp);
}

5. InsertNode 函数

函数首部:struct StuLink *InsertNode

参数列表:
[1]pw:用于表示链表尾节点的地址
[2]p1:用于表示新节点的地址
[3]xm:用于表示姓名字符串的地址

返回值:pw 的地址

实现功能:开辟一个新节点,用 p1 指向新节点,并向其赋值,同时检查性别和成绩的正确性, 根据成绩计算等级,再将 p1 接在 pw 后面

算法描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct StuLink *InsertNode(struct StuLink *pw){
struct StuLink *p1; //pw:原链表尾节点 p1:开辟新节点
if((p1=(struct StuLink *)malloc(size))==NULL) {
printf("不能成功分配储存块\n");
exit(0);
}
p1->next=NULL;
if(pw)
p1->xh=pw->xh+1;
else
p1->xh=1;
char xm[20];
printf("请依次输入姓名、性别(男1女0)、成绩[0,100]: ");
scanf("%s%d%d",xm,&p1->xb,&p1->cj);
while(p1->xb!=0&&p1->xb!=1){
printf("! ! ! 性别只能是1或0,请重新输入性别1:男 0:女 ");
scanf("%d",&p1->xb);
}
while(p1->cj<0 || p1->cj>100){
printf("! ! ! 成绩范围是0~100,请重新输入成绩 ");
scanf("%d",&p1->cj);
}
strcpy(p1->xm,xm);

// p1->mc=1; // 名次暂时命名为1
if(p1->cj>=90) p1->dj='A';
else if(p1->cj>=80) p1->dj='B';
else if(p1->cj>=70) p1->dj='C';
else if(p1->cj>=60) p1->dj='D';
else p1->dj='E';
if(pw==NULL) pw=p1;
else{
pw->next=p1;
pw=pw->next;
}
return pw;
}

6. EditNode函数

函数首部:void EditNode(struct StuLink *head)

参数列表:
[1]head:用于表示首节点的地址
[2]p1:用于表示循环寻找对应学号地址
[3]xm:用于表示姓名字符串
[4]n:用于表示要修改的学号
[5]re:用于表示要修改的数据项

返回值:无 实现功能:先判断链表是否为空,为空则退出函数。输入一个学号,判断是否存在,不存在则退 出函数;存在则输出对应学生信息,选择要修改的数据项进行修改

返回值:无

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
void EditNode(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}

struct StuLink *p1=head;
char xm[20];

int n,re;
printf("请输入你要修改的学生信息的学号:");
scanf("%d",&n) ;
while(p1){
if(p1->xh==n){
break;
}else{
p1=p1->next;
}
}
if(p1){
printf("你要修改的学生信息为:\n");
printf("学号 姓名 性别 成绩 等级 名次 ##男1女0##\n");
printf("%-6d%-10s%-6d%-6d%-6c%-6d\n",p1->xh,p1->xm,p1->xb,p1->cj,p1->dj,p1->mc);
} else{
printf("你输入的学号有误!\n");
printf("\n"); system("pause");
return;
}
printf("请选择要修改的数据项:0-不修改 1-姓名 2-性别 3-成绩 ");
scanf("%d",&re);
while(re<0||re>3){
printf("输入有误!重新输入: ");
scanf("%d",&re);
}
switch(re){
case 0:break;
case 1:
printf("请输出新的姓名:");
scanf("%s",xm);
strcpy(p1->xm,xm);
break;
case 2:
printf("请输入新的性别:");
scanf("%d",&p1->xb);
while(p1->xb!=0&&p1->xb!=1){
printf("! ! ! 性别只能是1或0,请重新输入性别1:男 0:女 ");
scanf("%d",&p1->xb);
}
break;
case 3:
printf("请输入新的成绩:");
scanf("%d",&p1->cj);
while(p1->cj<0 || p1->cj>100){
printf("! ! ! 成绩范围是0~100,请重新输入成绩 ");
scanf("%d",&p1->cj);
}
if(p1->cj>=90) p1->dj='A';
else if(p1->cj>=80) p1->dj='B';
else if(p1->cj>=70) p1->dj='C';
else if(p1->cj>=60) p1->dj='D';
else p1->dj='E';
break;
}
}

**7. DeleteNode 函数 **

函数首部:struct StuLink *DeleteNode(struct StuLink *head)

参数列表:
[1]head:用于表示首节点的地址
[2]p1:用于表示循环查找对应节点的地址
[3]p2:用于表示 p1 指向的节点的前驱节点的地址
[4]del_xh:用于表示被删除学生的学号

返回值:head 的地址

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct StuLink *DeleteNode(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return head;
}
struct StuLink *p1=head,*p2;
int del_xh;
printf("请输入要删除的学号:");
scanf("%d",&del_xh);
while((p1->xh!=del_xh)&&(p1->next!=NULL)){
p2=p1;p1=p1->next;
}
if(p1->xh==del_xh){ //找到
if(head==p1) head=head->next; //首节点
else p2->next=p1->next; //非首节点
free(p1);
printf("删除成功!\n");
}else printf("未找到此学号对应节点!\n");
printf("\n"); system("pause");
return head;
}

8. QueryNode 函数

函数首部:void QueryNode(struct StuLink *head)

参数列表:
[1]head:用于表示首节点的地址
[2]query_xh:用于表示查询的学号
[3]p1:用于表示循环查找对应节点的地址
[4]p2:用于表示 p1 指向的节点的前驱节点的地址

返回值:无

实现功能:先判断链表是否为空,为空则退出函数。接收要查询的学号,循环寻找,找到则输出学学 生信息;否则输出未找到

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void QueryNode(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
int query_xh;
struct StuLink *p1=head,*p2;
printf("请输入要查询的学生学号:");
scanf("%d",&query_xh);
while((p1->xh!=query_xh)&&(p1->next!=NULL)){
p2=p1;p1=p1->next;
}
if(p1->xh==query_xh){ //找到
printf("查找结果为:\n");
printf("学号:%-5d姓名:%-10s成绩:%-5d等级:%-5c名次:%-5d",p1->xh,p1->xm,p1->cj,p1->dj,p1->mc);
if(p1->xb==1) printf("性别:男\n");
else printf("性别:女\n");
}else printf("未找到该学号对应学生!\n");
}

9. QueryLink 函数

函数首部:void QueryLink(struct StuLink *head)

参数列表:
[1]head:用于表示首节点的地址
[2]p1:用于表示循环链表各节点的地址
[3]n:用于表示循环次数

返回值:无

实现功能:先判断链表是否为空,为空则退出函数。循环查找不及格学习,符合则输出,不符合则下 一个。若一个不及格的都没有,则输出没有不及格学生

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void QueryLink(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
struct StuLink *p1=head;
int n=0;
while(p1){
if(p1->cj<60){
n++; if(n==1) printf("未及格学生信息为:\n");
printf("学号:%-5d姓名:%-10s成绩:%-5d等级:%-5c名次:%-5d",p1->xh,p1->xm,p1->cj,p1->dj,p1->mc);
if(p1->xb==1) printf("性别:男\n");
else printf("性别:女\n");
}
p1=p1->next;
}
if(n==0) printf("没有不及格学生!\n");
}

10. RankLink 函数

函数首部:void RankLink(struct StuLink *head)

参数列表:
[1]head:用于表示首节点的地址
[2]i:用于表示排序的升降序
[3]p1:用于表示循环链表中节点的地址
[4]p2:用于表示 p1 指向的节点的前驱节点的地址
[5]n:用于表示循环次数

返回值:无

实现功能:先判断链表是否为空,为空则退出函数。学生按成绩降序排序,再计算链表中每个结 点的名次,最后保存(学号升序)到文件中。

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void RankLink(struct StuLink *head){
int i=2;
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
head=SortLink(head,i);
struct StuLink *p1=head,*p2;

int n=1;
while(p1){
p1->mc=n;
n++;
if((p1!=head)&&((p1->cj)==(p2->cj))) p1->mc=p2->mc;
p2=p1;
p1=p1->next;
}
SaveToFile(head);
}

11. AnalysisLink 函数

函数首部:void AnalysisLink(struct StuLink *head,int *dj_add)

参数列表:
[1]head:用于表示首节点的地址
[2]dj_add:用于表示各个等级的人数数组
[3]p1:用于表示循环链表中的节点

返回值:无

实现功能:先判断链表是否为空,为空则退出函数。循环各节点,判断成绩且对应等级统计数递增

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void AnalysisLink(struct StuLink *head,int *dj_add){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
struct StuLink *p1=head;
while(p1){
if(p1->cj>=90) dj_add[0]++;
else if(p1->cj>=80) dj_add[1]++;
else if(p1->cj>=70) dj_add[2]++;
else if(p1->cj>=60) dj_add[3]++;
else dj_add[5]++;
p1=p1->next;
}
}

12. OutputLink_1 函数

函数首部:void OutputLink_1(struct StuLink *head,int i)

参数列表:
[1]head:用于表示首节点的地址
[2]p1:用于表示循环链表节点的地址

返回值:无

实现功能:先判断链表是否为空,为空则退出函数。循环各节点,输出信息

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void  OutputLink_1(struct StuLink *head,int i){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
head=SortLink(head,i);
struct StuLink *p1=head;
printf("所有学生信息为:\n");
printf("学号 姓名 性别 成绩 等级 名次\n");
while(p1){

printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
p1=p1->next;
}
}

13. OutputLink_2 函数

函数首部:void OutputLink_2(struct StuLink *head,int i)

参数列表:
[1]head:用于表示首节点的地址
[2]i:用于表示排序的升降序
[3]sum:用于表示学生总数
[4]n:用于表示当前输出个数
[5]num1:用于表示菜单选择
[6]page_sum:用于表示总页数
[7]page_now:用于表示当前页数
[8]pf_num:用于表示当前输出的个数,控制不超过 10 个

返回值:无

实现功能:先判断链表是否为空,为空则退出函数。先输出第一页,判断总人数,是否有下一页, 无则退出函数,有则继续。接收输入选择功能数字,判断数字时候合理,不合理重新输入。使用 switch 跳到对应功能。1:用上次输出的当前页数*10-20 就等于“上一页”的第一个的学生个数,再用 n 控制 p1 循环到此处进行输出。2:跟着 p1 继续输出。3:输出第一页。4:用(sum/10)*10 就等于最后一页的第一 个学生个数,用 p1 循环到此处,输出即可。

算法描述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
void  OutputLink_2(struct StuLink *head,int i){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
head=SortLink(head,i);
// sum:学生总数 n:当前输出个数
int n=1,num1,sum=0,page_sum,page_now=1,pf_num=0;
struct StuLink *p1=head;
while(p1){
sum++;
p1=p1->next;
}
p1=head;
page_sum=sum/10+1;
system("cls");
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
if(sum<=10){
printf("\n只有一页,暂无上下页功能\n");
system("pause"); return ;
}else{
printf("\n 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<2||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}
while(1) {
switch(num1){
case 1:
system("cls");
p1=head;
n=1;
while(n<=page_now*10-20){
p1=p1->next;
n++;
}
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
page_now--; //当前页数-1
if(page_now==1){
printf("\n 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<2||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}else{
printf("\n1-上一页 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n");
scanf("%d",&num1);
while(num1<1||num1>4){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}
break;
case 2:
system("cls");
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
page_now++; //当前页数+1
if(page_now==page_sum){
printf("\n1-上一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<1||num1>5||num1==2){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}else{
printf("\n1-上一页 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n");
scanf("%d",&num1);
while(num1<1||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}
break;
case 3:
system("cls");
p1=head;
page_now=1;
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
printf("\n 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<2||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
break;
case 4:
system("cls");
p1=head;
page_now=page_sum;
n=1;
while(n<=(sum/10)*10){
p1=p1->next;
n++;
}
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
printf("\n1-上一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<1||num1>5||num1==2){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
break;
case 5:
break;
}
if(num1==5) break;
}
}

四、运行界面

一:主菜单

二:二级菜单——数据维护

![](https://cdn.jsdelivr.net/gh/WU-Kave/TC//2020/06/06StudentAchievementManagementSystemSecondMenuDatA maintenance.png)

  1. 数据插入

  2. 数据修改

  3. 数据删除

三:二级菜单——数据查询

  1. 学号查询

  1. 不及格学生查询

四:二级菜单——统计分析

  1. 成绩名次计算:无输出
  2. 成绩频次分析

五:二级菜单——报表输出

  1. 排序显示学生信息

​ 学号升序:

​ 成绩降序:

  1. 分页显示学生信息

六、退出

五、源代码

注意要在同个目录新建一个student.dat文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//枚举
enum sex{women,man};
struct StuLink{
int xh; //学号
char xm[20]; //姓名
enum sex xb; //性别
int cj; //成绩,范围[0,100] 前四个为输入项
char dj; //等级
int mc; //名次
struct StuLink *next; //下一项
};
char sex[][3]={"女","男"}; //用于输出“男女”中文字符
int size=sizeof(struct StuLink); //节点字节大小

void main(){
// 声明变量
int menu1,menu2; //menu1:一级菜单 menu2:二级菜单
int i=0,n,dj_add[5]={0,0,0,0,0};
struct StuLink *head=NULL,*pw; //*head:学生信息链表头指针 pw:尾节点

// 声明函数
// 从数据文件中逐行读取学生信息生成学生链表,返回头指针
struct StuLink *ReadFromFile();
// 先将学生链表按学号升序排序,再将学生链表中的数据逐行保存到数据文件
void SaveToFile(struct StuLink *head);
// SortLink函数:按指定数据项的顺序【1:学号(升序)】或者【2:成绩(降序)】对学生链表进行排序
struct StuLink *SortLink(struct StuLink *head,int i);
// InsertNode函数:在链表尾插入一个新结点。新结点的学号是链表中最大学号加1,姓名和成绩从键盘输入
// (注意:成绩必须在[0,100]区间的整数),根据成绩计算等级。
// 注意:插入结点会导致链表中各结点名次的变化。
struct StuLink *InsertNode(struct StuLink *pw);
// EditNode函数:修改链表中指定学号的结点(学号不能修改,成绩必须在[0,100]区间的整数)
// 注意:当修改成绩时会导致等级和名次的变化
void EditNode(struct StuLink *head);
//DeleteNode函数:删除链表中指定学号的结点。注意:删除结点会导致链表中各结点名次的变化
struct StuLink *DeleteNode(struct StuLink *head);
// QueryNode函数:查询链表中指定学号的结点,并显示查询结果。
void QueryNode(struct StuLink *head);
// QueryLink函数:查询链表中不及格的所有结点,并显示查询结果。
void QueryLink(struct StuLink *head);
//RankLink函数:计算链表中每个结点的名次。名次规则:按成绩降序排名,从第1名开始依次排名,
//若出现并列名次,则名次需要叠加。例如,若出现5个并列第1名,则没有第2名,下一个名次是第6名,依此类推。
void RankLink(struct StuLink *head);
//AnalysisLink函数:统计并返回各等级人数。等级标准:
//A:90及以上 B:80及以上 C:70及以上 D:60及以上 E:60以下
void AnalysisLink(struct StuLink *head,int *dj_add);
//OutputLink_1函数:按指定数据项的顺序【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表、各等级人数。
//学生成绩表每行输出一个学生信息(依次为学号、姓名、性别、成绩、等级和名次,各项间以1个空格隔开),
//各等级人数分行输出。
void OutputLink_1(struct StuLink *head,int i);
//OutputLink_2函数:分页显示全部学生的信息。
//分页功能:每页显示10个学生信息,有上一页、下一页、首页和最后一页的翻页功能。
void OutputLink_2(struct StuLink *head,int i);

// 执行语句
head=ReadFromFile(); //读取文件到链表,获取头指针


// 菜单
while(1){
system("cls"); //清控制台,Windows
printf("========================================\n");//40个=
printf("= =\n");
printf("= 学生成绩管理程序 =\n");
printf("= by:xxx =\n");
printf("========================================\n");
printf("= =\n");
printf("= 1-数据维护 2-数据查询 =\n");
printf("= 3-统计分析 4-报表输出 =\n");
printf("= 0-退出 =\n");
printf("= =\n");
printf("========================================\n");
// printf("\n");
printf("请选择:");
scanf("%d",&menu1);
switch(menu1){ //一级菜单
case 1:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("= 1-数据插入 2-数据修改 =\n");
printf("= 3-数据删除 0-返回上级 =\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
printf("请输入要插入的学生个数(n>0): ");
scanf("%d",&n);
while(n<=0){
printf("!!!请输入正确的学生个数!!!: ");
scanf("%d",&n);
}
pw=head;
if(pw){
while(pw->next) //获取尾部节点
pw=pw->next;
}

for(i=0;i<n;i++){
pw=InsertNode(pw);
if(head==NULL) head=pw;
}
// 计算名次
RankLink(head);
// SaveToFile(head);
break;
case 2:
EditNode(head);
// 计算名次
RankLink(head);
// SaveToFile(head);
break;
case 3:
head=DeleteNode(head);
// 计算名次
RankLink(head);
// SaveToFile(head);
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 2:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("=1-学号查询 2-不及格学生查询 0-返回上级=\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
QueryNode(head);
printf("\n"); system("pause");
break;
case 2:
QueryLink(head);
printf("\n"); system("pause");
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 3:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("= 1-成绩名次计算 2-成绩频次分析 =\n");
printf("= 0-返回上级 =\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
RankLink(head);
break;
case 2:
AnalysisLink(head,dj_add);
printf("'A'有%d人;'B'有%d人,'C'有%d人,'D'有%d人,'E'有%d人,共有%d人\n",
dj_add[0],dj_add[1],dj_add[2],dj_add[3],dj_add[4],dj_add[0]+dj_add[1]+dj_add[2]+dj_add[3]+dj_add[4]);
printf("\n"); system("pause");
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 4:
while(1){ //循环输出二级菜单
system("cls");
printf("\n");
printf("========================================\n");
printf("= =\n");
printf("=1-排序显示学生信息 2-分页显示学生信息=\n");
printf("= 0-返回上级 =\n");
printf("= =\n");
printf("========================================\n");
printf("请选择:");
scanf("%d",&menu2);
switch(menu2){ //二级菜单
case 1:
printf("请选择按【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表:");
scanf("%d",&i);
while((i!=1)&&(i!=2)){
printf("!!!请选择正确选项!!!1 or 2 : ");
scanf("%d",&i);
}
OutputLink_1(head,i);
printf("\n");
system("pause");
break;
case 2:
printf("请选择按【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表:");
scanf("%d",&i);
while((i!=1)&&(i!=2)){
printf("!!!请选择正确选项!!!1 or 2 : ");
scanf("%d",&i);
}
OutputLink_2(head,i);
break;
case 0:
break;
}
if(menu2==0) break;
}
break;
case 0:
printf("========================================\n");
printf("= =\n");
printf("= 你已经退出学生管理系统 =\n");
printf("= =\n");
printf("========================================\n");
return; //退出main函数
}
}

}

// 自 定 义 函 数
// 从数据文件中逐行读取学生信息生成学生链表,返回头指针
struct StuLink *ReadFromFile(){
FILE *fp; //指向student.dat文件
struct StuLink *p1,*p2,*head=NULL; //head:头节点 p1:开辟新节点 p2:尾节点
char ch;

if((fp=fopen("student.dat","r"))==NULL){ //读开student.dat
printf("打开student.dat失败\n");
exit(0);
}
ch=fgetc(fp); //读取文件第一个字符
if(ch==EOF){ //EOF文件终止符
printf("文件为空\n");
return head;
}else{
rewind(fp); // 将fp的地址重置为文件开头
}

while(!feof(fp)){
if((p1=(struct StuLink *)malloc(size))==NULL) {
printf("不能成功分配储存块");
exit(0);
}
p1->next=NULL;
if(head==NULL) head=p1; //首节点
else p2->next=p1; //非首节点,表尾插入新节点
p2=p1; // p2指向新的表尾结点
fscanf(fp,"%d %s %d %d %s %d\n",&p1->xh,p1->xm,&p1->xb,&p1->cj,&p1->dj,&p1->mc);
// 读取\n是防止fp以为文件未完
printf("%-5d%-10s%-5d%-5d%-5c%-5d\n",p1->xh,p1->xm,p1->xb,p1->cj,p1->dj,p1->mc);
}
fclose(fp); //关闭student.dat
return head;
}

// SortLink函数:按指定数据项的顺序【1:学号(升序)】或者【2:成绩(降序)】对学生链表进行排序
struct StuLink *SortLink(struct StuLink *head,int i){
if(head==NULL)return head;
// 使用交换数据的形式交换两个节点
int xh,xb,cj,mc;
char xm[20],dj;
// p1:被比较节点 p2:比较节点 p3:快速排序储存节点
struct StuLink *p1=head,*p2=head->next,*p3=p1;
switch(i){
case 1:
// 快速排序
while(p1->next){
p2=p1->next;
p3=p1;
while(p2){
if(p3->xh>p2->xh)
p3=p2;
p2=p2->next;
}
if(p3->xh<p1->xh){
xh=p3->xh; xb=p3->xb;
cj=p3->cj; mc=p3->mc;
strcpy(xm,p3->xm); dj=p3->dj;
p3->xh=p1->xh; p3->xb=p1->xb;
p3->cj=p1->cj; p3->mc=p1->mc;
strcpy(p3->xm,p1->xm); p3->dj=p1->dj;
p1->xh=xh; p1->xb=xb;
p1->cj=cj; p1->mc=mc;
strcpy(p1->xm,xm); p1->dj=dj;
}
p1=p1->next;
}
break;
case 2:
// 快速排序
while(p1->next){
p2=p1->next;
p3=p1;
while(p2){
if(p3->cj<p2->cj)
p3=p2;
p2=p2->next;
}
if(p3->cj>p1->cj){
xh=p3->xh; xb=p3->xb;
cj=p3->cj; mc=p3->mc;
strcpy(xm,p3->xm); dj=p3->dj;
p3->xh=p1->xh; p3->xb=p1->xb;
p3->cj=p1->cj; p3->mc=p1->mc;
strcpy(p3->xm,p1->xm); p3->dj=p1->dj;
p1->xh=xh; p1->xb=xb;
p1->cj=cj; p1->mc=mc;
strcpy(p1->xm,xm); p1->dj=dj;
}
p1=p1->next;
}
break;
}
return head;
}

// 先将学生链表按学号升序排序,再将学生链表中的数据逐行保存到数据文件
void SaveToFile(struct StuLink *head){
FILE *fp;
int i=1;
head=SortLink(head,i);
struct StuLink *p=head;
if((fp=fopen("student.dat","w"))==NULL){
printf("写开文件失败!");
exit(0);
}
while(p){
// 写入文件
fprintf(fp,"%d %s %d %d %c %d\n",p->xh,p->xm,p->xb,p->cj,p->dj,p->mc);
p=p->next;
}
fclose(fp);
}

// InsertNode函数:在链表尾插入一个新结点。新结点的学号是链表中最大学号加1,姓名和成绩从键盘输入
// (注意:成绩必须在[0,100]区间的整数),根据成绩计算等级。
// 注意:插入结点会导致链表中各结点名次的变化。
struct StuLink *InsertNode(struct StuLink *pw){
struct StuLink *p1; //pw:原链表尾节点 p1:开辟新节点
if((p1=(struct StuLink *)malloc(size))==NULL) {
printf("不能成功分配储存块\n");
exit(0);
}
p1->next=NULL;
if(pw)
p1->xh=pw->xh+1;
else
p1->xh=1;
char xm[20];
printf("请依次输入姓名、性别(男1女0)、成绩[0,100]: ");
scanf("%s%d%d",xm,&p1->xb,&p1->cj);
while(p1->xb!=0&&p1->xb!=1){
printf("! ! ! 性别只能是1或0,请重新输入性别1:男 0:女 ");
scanf("%d",&p1->xb);
}
while(p1->cj<0 || p1->cj>100){
printf("! ! ! 成绩范围是0~100,请重新输入成绩 ");
scanf("%d",&p1->cj);
}
strcpy(p1->xm,xm);

// p1->mc=1; // 名次暂时命名为1
if(p1->cj>=90) p1->dj='A';
else if(p1->cj>=80) p1->dj='B';
else if(p1->cj>=70) p1->dj='C';
else if(p1->cj>=60) p1->dj='D';
else p1->dj='E';
if(pw==NULL) pw=p1;
else{
pw->next=p1;
pw=pw->next;
}
return pw;
}

// EditNode函数:修改链表中指定学号的结点(学号不能修改,成绩必须在[0,100]区间的整数)
// 注意:当修改成绩时会导致等级和名次的变化
void EditNode(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}

struct StuLink *p1=head;
char xm[20];

int n,re;
printf("请输入你要修改的学生信息的学号:");
scanf("%d",&n) ;
while(p1){
if(p1->xh==n){
break;
}else{
p1=p1->next;
}
}
if(p1){
printf("你要修改的学生信息为:\n");
printf("学号 姓名 性别 成绩 等级 名次 ##男1女0##\n");
printf("%-6d%-10s%-6d%-6d%-6c%-6d\n",p1->xh,p1->xm,p1->xb,p1->cj,p1->dj,p1->mc);
} else{
printf("你输入的学号有误!\n");
printf("\n"); system("pause");
return;
}
printf("请选择要修改的数据项:0-不修改 1-姓名 2-性别 3-成绩 ");
scanf("%d",&re);
while(re<0||re>3){
printf("输入有误!重新输入: ");
scanf("%d",&re);
}
switch(re){
case 0:break;
case 1:
printf("请输出新的姓名:");
scanf("%s",xm);
strcpy(p1->xm,xm);
break;
case 2:
printf("请输入新的性别:");
scanf("%d",&p1->xb);
while(p1->xb!=0&&p1->xb!=1){
printf("! ! ! 性别只能是1或0,请重新输入性别1:男 0:女 ");
scanf("%d",&p1->xb);
}
break;
case 3:
printf("请输入新的成绩:");
scanf("%d",&p1->cj);
while(p1->cj<0 || p1->cj>100){
printf("! ! ! 成绩范围是0~100,请重新输入成绩 ");
scanf("%d",&p1->cj);
}
if(p1->cj>=90) p1->dj='A';
else if(p1->cj>=80) p1->dj='B';
else if(p1->cj>=70) p1->dj='C';
else if(p1->cj>=60) p1->dj='D';
else p1->dj='E';
break;
}
}

//DeleteNode函数:删除链表中指定学号的结点。注意:删除结点会导致链表中各结点名次的变化
struct StuLink *DeleteNode(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return head;
}
struct StuLink *p1=head,*p2;
int del_xh;
printf("请输入要删除的学号:");
scanf("%d",&del_xh);
while((p1->xh!=del_xh)&&(p1->next!=NULL)){
p2=p1;p1=p1->next;
}
if(p1->xh==del_xh){ //找到
if(head==p1) head=head->next; //首节点
else p2->next=p1->next; //非首节点
free(p1);
printf("删除成功!\n");
}else printf("未找到此学号对应节点!\n");
printf("\n"); system("pause");
return head;
}

// QueryNode函数:查询链表中指定学号的结点,并显示查询结果。
void QueryNode(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
int query_xh;
struct StuLink *p1=head,*p2;
printf("请输入要查询的学生学号:");
scanf("%d",&query_xh);
while((p1->xh!=query_xh)&&(p1->next!=NULL)){
p2=p1;p1=p1->next;
}
if(p1->xh==query_xh){ //找到
printf("查找结果为:\n");
printf("学号:%-5d姓名:%-10s成绩:%-5d等级:%-5c名次:%-5d",p1->xh,p1->xm,p1->cj,p1->dj,p1->mc);
if(p1->xb==1) printf("性别:男\n");
else printf("性别:女\n");
}else printf("未找到该学号对应学生!\n");
}

// QueryLink函数:查询链表中不及格的所有结点,并显示查询结果。
void QueryLink(struct StuLink *head){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
struct StuLink *p1=head;
int n=0;
while(p1){
if(p1->cj<60){
n++; if(n==1) printf("未及格学生信息为:\n");
printf("学号:%-5d姓名:%-10s成绩:%-5d等级:%-5c名次:%-5d",p1->xh,p1->xm,p1->cj,p1->dj,p1->mc);
if(p1->xb==1) printf("性别:男\n");
else printf("性别:女\n");
}
p1=p1->next;
}
if(n==0) printf("没有不及格学生!\n");
}

//RankLink函数:计算链表中每个结点的名次。名次规则:按成绩降序排名,从第1名开始依次排名,
//若出现并列名次,则名次需要叠加。例如,若出现5个并列第1名,则没有第2名,下一个名次是第6名,依此类推。
void RankLink(struct StuLink *head){
int i=2;
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
head=SortLink(head,i);
struct StuLink *p1=head,*p2;

int n=1;
while(p1){
p1->mc=n;
n++;
if((p1!=head)&&((p1->cj)==(p2->cj))) p1->mc=p2->mc;
p2=p1;
p1=p1->next;
}
SaveToFile(head);
}

//AnalysisLink函数:统计并返回各等级人数。等级标准:
//A:90及以上 B:80及以上 C:70及以上 D:60及以上 E:60以下
void AnalysisLink(struct StuLink *head,int *dj_add){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
struct StuLink *p1=head;
while(p1){
if(p1->cj>=90) dj_add[0]++;
else if(p1->cj>=80) dj_add[1]++;
else if(p1->cj>=70) dj_add[2]++;
else if(p1->cj>=60) dj_add[3]++;
else dj_add[5]++;
p1=p1->next;
}
}

//OutputLink_1函数:按指定数据项的顺序【1:学号(升序)】或者【2:成绩(降序)】输出学生成绩表、各等级人数。
//学生成绩表每行输出一个学生信息(依次为学号、姓名、性别、成绩、等级和名次,各项间以1个空格隔开),
//各等级人数分行输出。
void OutputLink_1(struct StuLink *head,int i){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
head=SortLink(head,i);
struct StuLink *p1=head;
printf("所有学生信息为:\n");
printf("学号 姓名 性别 成绩 等级 名次\n");
while(p1){

printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
p1=p1->next;
}
}

//OutputLink_2函数:分页显示全部学生的信息。
//分页功能:每页显示10个学生信息,有上一页、下一页、首页和最后一页的翻页功能。
void OutputLink_2(struct StuLink *head,int i){
if(head==NULL){
printf("学生数据为空!\n");
printf("\n"); system("pause");
return;
}
head=SortLink(head,i);
// sum:学生总数 n:当前输出个数
int n=1,num1,sum=0,page_sum,page_now=1,pf_num=0;
struct StuLink *p1=head;
while(p1){
sum++;
p1=p1->next;
}
p1=head;
page_sum=sum/10+1;
system("cls");
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
if(sum<=10){
printf("\n只有一页,暂无上下页功能\n");
system("pause"); return ;
}else{
printf("\n 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<2||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}
while(1) {
switch(num1){
case 1:
system("cls");
p1=head;
n=1;
while(n<=page_now*10-20){
p1=p1->next;
n++;
}
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
page_now--; //当前页数-1
if(page_now==1){
printf("\n 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<2||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}else{
printf("\n1-上一页 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n");
scanf("%d",&num1);
while(num1<1||num1>4){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}
break;
case 2:
system("cls");
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
page_now++; //当前页数+1
if(page_now==page_sum){
printf("\n1-上一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<1||num1>5||num1==2){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}else{
printf("\n1-上一页 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n");
scanf("%d",&num1);
while(num1<1||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
}
break;
case 3:
system("cls");
p1=head;
page_now=1;
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
printf("\n 2-下一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<2||num1>5){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
break;
case 4:
system("cls");
p1=head;
page_now=page_sum;
n=1;
while(n<=(sum/10)*10){
p1=p1->next;
n++;
}
printf("学号 姓名 性别 成绩 等级 名次\n");
pf_num=1;
while(p1&&pf_num<=10){
printf("%-6d%-10s",p1->xh,p1->xm);
if(p1->xb==1) printf("男 ");
else printf("女 ");
printf("%-6d%-6c%-6d\n",p1->cj,p1->dj,p1->mc);
pf_num++;
p1=p1->next;
}
printf("\n1-上一页 3-首页 4-尾页 5-返回上级\n");
printf(" 当前第%-2d页,共%-2d页\n",page_now,page_sum);
scanf("%d",&num1);
while(num1<1||num1>5||num1==2){
printf("请输入正确的数字: ");
scanf("%d",&num1);
}
break;
case 5:
break;
}
if(num1==5) break;
}
}