趁着放假无事,开始用C语言开发一些小的项目,巩固基础知识的同时学习新的知识。
学生成绩管理系统实现的功能有:成绩录入、学生成绩查询、删除、修改、通过文件保存等。
开发这样一个系统需要具备的知识:线性表(链表)、文件操作、排序(如果需要成绩排序)。
开发环境为VS2015;在Linux下没有conio.h的头文件,需要修改与getch()函数相关的代码。
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
|
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <conio.h> /*学生信息结构体*/ typedef struct Node { char Name[10]; //学生姓名 char ID[15]; //学生学号 int Score[3]; //三科成绩(数学、英语、数据结构) float Ave_Sco; struct Node *next; }Lnode; void Display(); /*界面显示函数*/ void GetScore(Lnode *&h); /*成绩录入函数*/ void PrintScore(Lnode *h); /*成绩打印函数*/ void ModifyScore(Lnode *h); /*成绩修改函数*/ void FindInf(Lnode *h); /*查找信息*/ void Delete(Lnode *h); /*删除函数*/ void Quit(Lnode *h); /*退出函数*/ void SaveInf(Lnode *h); void LoadInf(Lnode *h); /*初始化链表*/ void InitList(Lnode *&head) { head = (Lnode *) malloc ( sizeof (Lnode)); if (head == NULL) { printf ( "error!" ); exit (1); } head->next = NULL; //使头节点指针域为空 } int main() { Lnode *ScoreList; //建立成绩链表,所有学生信息存放在此链表 int Function; char flag; int t = 0; InitList(ScoreList); LoadInf(ScoreList); while (1) { Display(); printf ( "请选择操作: " ); scanf ( "%d" , &Function); switch (Function) { case 1: while (1) { GetScore(ScoreList); printf ( "是否继续输入 (Y/N)" ); scanf ( "%s" , &flag); if (flag == 'N' || flag == 'n' ) break ; } system ( "cls" ); break ; case 2: PrintScore(ScoreList); _getch(); system ( "cls" ); break ; case 3: ModifyScore(ScoreList); system ( "cls" ); break ; case 4: FindInf(ScoreList); _getch(); system ( "cls" ); break ; case 5: Delete(ScoreList); _getch(); system ( "cls" ); break ; case 6: Quit(ScoreList); break ; default : printf ( "Error!!! 请重新输入:" ); break ; } //switch结束 } return 0; } /*系统界面显示*/ void Display() { printf ( "\t\t**********************************************\n" ); printf ( "\t\t*************欢迎使用成绩管理系统*************\n" ); printf ( "\t\t**********************************************\n" ); printf ( "\t\t\t\t1、录入成绩\n" ); printf ( "\t\t\t\t2、打印成绩\n" ); printf ( "\t\t\t\t3、修改成绩\n" ); printf ( "\t\t\t\t4、查找学生信息\n" ); printf ( "\t\t\t\t5、删除学生信息\n" ); printf ( "\t\t\t\t6、退出系统\n" ); printf ( "\n\n\n\n\n\n" ); } /*成绩录入*/ void GetScore(Lnode *&h) { Lnode *p, *q = h; char name[10], id[15]; int Math, English, Datastruct; p = (Lnode *) malloc ( sizeof (Lnode)); //为学生信息申请节点 printf ( "请依次输入学生信息:\n" ); printf ( "姓名 学号 数学 英语 数据结构\n" ); scanf ( "%s %s %d %d %d" , &name, &id, &Math, &English, &Datastruct); for (; q->next != NULL; q = q->next){;} //移动到尾节点 strcpy (p->Name, name); strcpy (p->ID, id); p->Score[0] = Math; p->Score[1] = English; p->Score[2] = Datastruct; p->Ave_Sco = (( float )((p->Score[0] + p->Score[1] + p->Score[2]) - 150)) / 30; p->next = NULL; q->next = p; q = p; } /*成绩打印*/ void PrintScore(Lnode *h) { Lnode *p = h->next; printf ( "%-14s%-8s%-8s%-8s%-8s%-8s\n" , "排名" , "学号" , "姓名" , "数学" , "英语" , "数据结构" , "平均绩点" ); while (p != NULL) { printf ( "%-14s%-8s%-8d%-8d%-8d%.2f\n" , p->ID, p->Name, p->Score[0], p->Score[1], p->Score[2], p->Ave_Sco); p = p->next; } } /*成绩修改*/ void ModifyScore(Lnode *h) { Lnode *p = h->next; char name[10], id[15]; int Math, English, Datastruct; printf ( "请输入学生姓名:" ); scanf ( "%s" , name); printf ( "请输入学生学号:" ); scanf ( "%s" , id); while (p) { if ( strcmp (p->Name, name)==0 && strcmp (p->ID, id)==0) { printf ( "当前学生信息:\n" ); printf ( "%-14s%-8s%-8s%-8s%-8s\n" , "学号" , "姓名" , "数学" , "英语" , "数据结构" ); printf ( "%-14s%-8s%-8d%-8d%-8d\n" , p->ID, p->Name, p->Score[0], p->Score[1], p->Score[2]); printf ( "请输入更正后的数学成绩:" ); scanf ( "%d" , &Math); printf ( "请输入更正后的英语成绩:" ); scanf ( "%d" , &English); printf ( "请输入更正后的数据结构成绩:" ); scanf ( "%d" , &Datastruct); p->Score[0] = Math; p->Score[1] = English; p->Score[2] = Datastruct; break ; } else { p = p->next; } } //while循环结束 } /*信息查找*/ void FindInf(Lnode *h) { Lnode *p = h->next; char name[10], id[15]; printf ( "请输入学生姓名:" ); scanf ( "%s" , name); printf ( "请输入学生学号:" ); scanf ( "%s" , id); while (p) { if ( strcmp (p->Name, name) == 0 && strcmp (p->ID, id) == 0) { printf ( "当前学生信息:\n" ); printf ( "%-14s%-8s%-8s%-8s%-8s\n" , "学号" , "姓名" , "数学" , "英语" , "数据结构" ); printf ( "%-14s%-8s%-8d%-8d%-8d\n" , p->ID, p->Name, p->Score[0], p->Score[1], p->Score[2]); break ; } else { p = p->next; } } //while循环结束 } /*删除*/ void Delete(Lnode *h) { Lnode *p = h, *q; q = p->next; char name[10], id[15]; printf ( "请输入学生姓名:" ); scanf ( "%s" , name); printf ( "请输入学生学号:" ); scanf ( "%s" , id); while (q) { if ( strcmp (q->Name, name) == 0 && strcmp (q->ID, id) == 0) { p->next = q->next; free (q); //删除p节点 printf ( "删除成功\n" ); break ; } else { p = p->next; q = q->next; } } //while循环结束 } /*退出系统*/ void Quit(Lnode *h) { SaveInf(h); //退出时保存信息 exit (0); } /*打开文件*/ void LoadInf(Lnode *h) { Lnode *p = h; Lnode *q; //临时变量 用于保存从文件中读取的信息 FILE * file = fopen ( "./Information.dat" , "rb" ); if (!file) { printf ( "文件打开失败!" ); return ; } /* 使用feof判断文件是否为结束要注意的问题: 当读取文件结束时,feof函数不会立即设置标志符为-1,而是 需要再读取一次后,才会设置。所以要先读一次。 */ q = (Lnode *) malloc ( sizeof (Lnode)); fread (q, sizeof (Lnode), 1, file); while (! feof (file)) //一直读到文件末尾 { p->next = q; p = q; q = (Lnode *) malloc ( sizeof (Lnode)); fread (q, sizeof (Lnode), 1, file); } //while循环结束 p->next = NULL; fclose (file); } /*保存信息到文件中*/ void SaveInf(Lnode *h) { Lnode *p = h->next; int flag; FILE * file = fopen ( "./Information.dat" , "wb" ); if (!file) { printf ( "文件打开失败!" ); return ; } while (p != NULL) { flag = fwrite (p, sizeof (Lnode), 1, file); //将p的内容写到文件中 if (flag != 1) { break ; } p = p->next; } fclose (file); } |
虽然是很简单的小项目,还是有很多问题。
一:链表相关
在写成绩录入和成绩打印功能时,发现始终只能保存(没加入文件保存)最后一个数据,确定链表的相关操作没有问题,仔细判断逻辑关系后,发现是每次在头节点传到GetScore()函数,为新节点申请内存后,直接将数据保存在了新申请的节点里面,没有将链表移动到尾节点,导致每次录入成绩,都会覆盖前一次输入的数据。解决办法是链表传到函数后,先移动到最后一个节点,将新申请的节点挂接在最后一个节点之后。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/*成绩录入*/ void GetScore(Lnode *&h) { Lnode *p, *q = h; char name[10], id[15]; int Math, English, Datastruct; p = (Lnode *) malloc ( sizeof (Lnode)); //为学生信息申请节点 printf ( "请依次输入学生信息:\n" ); printf ( "姓名 学号 数学 英语 数据结构\n" ); scanf ( "%s %s %d %d %d" , &name, &id, &Math, &English, &Datastruct); for (; q->next != NULL; q = q->next){;} //移动到尾节点 //保存数据 strcpy (p->Name, name); strcpy (p->ID, id); p->Score[0] = Math; p->Score[1] = English; p->Score[2] = Datastruct; p->Ave_Sco = (( float )((p->Score[0] + p->Score[1] + p->Score[2]) - 150)) / 30; //始终指向最后一个节点 p->next = NULL; q->next = p; q = p; } |
二、文件操作
用文件保存遇到的问题主要是每次打印数据时除正常数据外,始终多一行乱码。判断方法是while(!feof(file))。排除错误时确定了两种可能性:多保存了一行;多读取了一行。经过某度feof()与EOF的关系后,确定是多读取了一行数据。
用feof()函数进行文件尾判断时,当文件已经到达尾部后,还需要在读取一次后,feof()函数才会返回-1,所以会出现多读一次的情况;解决办法时,在循环读取之前先将第一个数据读取出来,然后在正常读取。即注意多读一次的问题。
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
|
/*打开文件*/ void LoadInf(Lnode *h) { Lnode *p = h; Lnode *q; //临时变量 用于保存从文件中读取的信息 FILE * file = fopen ( "./Information.dat" , "rb" ); if (!file) { printf ( "文件打开失败!" ); return ; } /* 使用feof判断文件是否为结束要注意的问题: 当读取文件结束时,feof函数不会立即设置标志符为-1,而是 需要再读取一次后,才会设置。所以要先读一次。 */ q = (Lnode *) malloc ( sizeof (Lnode)); fread (q, sizeof (Lnode), 1, file); while (! feof (file)) //一直读到文件末尾 { p->next = q; p = q; q = (Lnode *) malloc ( sizeof (Lnode)); fread (q, sizeof (Lnode), 1, file); } //while循环结束 p->next = NULL; fclose (file); } |