preconf.h
头文件。作用:被核心头文件所包含,从而调用相关已定义的数据Boolean
、Status
两种返回值类型,本质上是int
类型SqList
、ElemType
了两个结构体,实则属于独立结构的Struct嵌套SysConfig
结构体根据学生管理系统的功能特性与线性表的ADT描述,确定并编写相关的基本操作,便于后续的文件调用。
GitHub:https://github.com/ITchujian/StudentManagementSystem_2022_C
注:为方便分享本次开发的经历,我把分析过程以及代码书写过程,以文字、图片形式合计放于开发记录中,但是一些非常基础的行为动作我将不会讲解或者阐述。
当前位置:【开发细节】用C语言基础写学生管理系统(三)
可跳转:
ADT——Abstract Data Type,意为抽象数据类型
,我的理解:
描述数据结构的数学模型,以及该模型所能做出的操作
请注意,我们所使用的是线性表的顺序存储结构,而非链式结构,这一点已经在1.1文章的代码中有所体现。
因此,对于学生管理系统的线性表ADT如下:
序号 | 函数名 | 操作结果 |
---|---|---|
1 | InitList | 初始化,构造空的线性表 |
2 | DestroyList | 销毁线性表 |
3 | ClearList | 清空线性表 |
4 | ListEmpty | 判断空的线性表 |
5 | ListLength | 返回线性表长度 |
6 | GetElem | 获取线性表的数据元素 |
7 | LocateElem | 返回满足一定条件的数据元素的位序 |
8 | SearchElem | 通过数据项搜索线性表 |
9 | ListInsert | 向线性表插入元素 |
10 | ListDelete | 向线性表删除元素 |
11 | ListTraverse | 遍历线性表的数据元素,执行同类型操作 |
12 | SwapElem | 交换两个数据元素的位置 |
13 | ListSort | 排序整个线性表 |
以上表格中,绝大部分的操作我们都将可能用到,拭目以待吧。 |
创建kernel_list.h
(代表这是一个与表相关的核心文件,后续的其他文件都需要调用该头文件中的操作)
此时VS2022已经给我们的头文件顶行加上了如下代码:
#pragma once
看来微软官方的IDE也比较推荐我们使用这种
引入自定义的头文件preconf.h
。
#include "preconf.h"
这一步,我们将实现基本操作表
中的所有操作,函数的命名和表中呈现一样,使用大驼峰命名法(根据开发规范,此为核心文件,函数命名即为如此)。
初始化,构造空的线性表
通过malloc函数申请一片内存,然后判断内存是否分配成功;
赋予SqList结构体中的length初始值0,即现在0个学生;
初始分配空间值为128,单位为sizeof(ElemType)。
Status InitList(SqList* L)
{
L->elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType));
if (!(L->elem))
exit(OVERFLOWED);
L->length = 0;
L->list_size = LIST_INIT_SIZE;
return OK;
}
销毁线性表
释放动态内存;
长度为0;
分配空间也为0;
Status DestroyList(SqList* L)
{
free(L->elem);
L->elem = NULL;
L->length = 0;
L->list_size = 0;
return OK;
}
清空线性表
Status ClearList(SqList* L)
{
L->length = 0;
return OK;
}
判断空的线性表
Boolean ListEmpty(SqList* L)
{
if (L->length == 0)
return TRUE;
else
return FALSE;
}
返回线性表长度
这里可能我们会用不到,因为涉及长度的话,我们直接引用L->length就行了,但是写写也无妨,强迫症作祟
int ListLength(SqList* L)
{
return L->length;
}
获取线性表的数据元素
将第i个数据元素的值传给同类型的e,如果i不合法,退出并返回ERROR。
Status GetElem(SqList* L, int i, ElemType* e)
{
if (i < 1 || i > ListLength(L))
exit(ERROR);
*e = *((L->elem) + i - 1);
return OK;
}
返回满足一定条件的数据元素的位序
我记得在上一章中写了,C++在兼容C的有些地方是有差异的,在学习Python、Java的同时,也比较浅显的了解了部分的C++语法。其中比较有意思的是,在C中,如果这样写
int Test(float(*tt)())
{
return 0;
}
你是否会认为参数中的函数是无参数的函数呢?
其实并不是的,如果无参数,那其实我们应该这样写
int Test(float(*tt)(void))
{
return 0;
}
加上void,才是函数无参的最好证明,而为我们在C++中,这种做法是不可取的,我们的项目是基于C++的,尽管是C编写的,所以明白这一点,我们才能避免一些错误和大坑。
以下的函数指针Status(*compare)(ElemType, ElemType)表示一个返回类型是Status且拥有两个参数的函数,参数类型是ElemType。
int LocateElem(SqList* L, ElemType* e, Status(*compare)(ElemType, ElemType))
{
ElemType* p;
int i = 1;
p = L->elem;
while (i <= (L->length) && !(*compare)(*e, *(p++)))
++i;
if (i <= (L->length))
return i;
else
return 0;
}
通过数据项搜索线性表
参数cur_e是搜索项,在之前的定义结构中,我注明了学号是所有学生表中的唯一值,它是不可重复的,因此,我们查询学生信息时,必须利用好这一点;
参数mode_manner表示搜索的偏差,如果为-1,即寻找某学号对应同学的前驱,0则为查询该同学本身,1则为查询某学号对应同学的后继。
最终将搜索出来的同学,传给参数e。
Status SearchElem(SqList* L, int cur_e, ElemType* e, int mode_manner)
{
ElemType* r = L->elem;
int i = 1;
while (i <= L->length && r->num != cur_e)
{
i++;
r++;
}
if (i > L->length)
return INFEASIBLE;
switch (mode_manner)
{
case -1:
*e = *(--r);
return OK;
case 0:
*e = *r;
return OK;
case 1:
*e = *(++r);
return OK;
default:
return ERROR;
}
return OK;
}
向线性表插入元素
在第i(i∈[1, n])个元素之前插入一个元素,需将i→n个元素向后移动一个位置。
Status ListInsert(SqList* L, int i, ElemType e)
{
if (i < 1 || i >(L->length) + 1)
return ERROR;
if (L->length >= L->list_size)
{
ElemType* new_base = (ElemType*)realloc(L->elem, (L->length + LIST_INCREMENT) * sizeof(ElemType));
if (!new_base)
exit(OVERFLOWED);
L->elem = new_base;
L->list_size += LIST_INCREMENT;
}
ElemType* q = &(L->elem[i - 1]);
ElemType* p = &(L->elem[(L->length) - 1]);
for (p = &(L->elem[(L->length) - 1]); p >= q; --p) /* 插入位置及之后的元素右移 */
*(p + 1) = *p;
*q = e;
++(L->length);
return OK;
}
向线性表删除元素
删除第i(i∈[1, n])个元素,需将i+1→n个元素向前移动一个位置。
Status ListDelete(SqList* L, int i, ElemType* e)
{
if (i < 1 || i > L->length)
return ERROR;
ElemType* p = L->elem + i - 1;
*e = *p;
ElemType* q = L->elem + L->length - 1;
for (++p; p <= q; ++p)
*(p - 1) = *p;
--(L->length);
return OK;
}
遍历线性表的数据元素,执行同类型操作
Status ListTraverse(SqList* L, void(*visit)(ElemType*))
{
int i;
ElemType* p = L->elem;
for (i = 1; i <= L->length; ++i)
visit(p);
return OK;
}
交换两个数据元素的位置
Status SwapElem(ElemType* x, ElemType* y)
{
ElemType* z = (ElemType*)malloc(sizeof(ElemType*)+1);
if (z == NULL)
return OVERFLOWED;
*z = *x;
*x = *y;
*y = *z;
return OK;
}
排序整个线性表
因为目前只学过冒泡排序,所以这里就用它吧
sort_manner≤0时,表示降序排序;
sort_manner>1时,表示升序排序;
key为排序的主键,根据项目情况,它只能排序的项有:
Status ListSort(SqList* L, int sort_manner, char key)
{
int i, j;
Boolean flag = TRUE;
for (i = 0; i < (L->length) - 1 && flag == TRUE; i++)
{
flag = FALSE;
for (j = 0; j < (L->length) - i - 1; j++)
{
if (sort_manner <= 0)
{
switch (key)
{
case 'a':
if ((L->elem)[j].average_score < (L->elem)[j + 1].average_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 's':
if ((L->elem)[j].sum_score < (L->elem)[j + 1].sum_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'l':
if ((L->elem)[j].score_literature < (L->elem)[j + 1].score_literature)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'm':
if ((L->elem)[j].score_math < (L->elem)[j + 1].score_math)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'e':
if ((L->elem)[j].score_english < (L->elem)[j + 1].score_english)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'n':
if ((L->elem)[j].num < (L->elem)[j + 1].num)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
default:
printf("该主键不支持排序\n");
return ERROR;
}
}
else
{
switch (key)
{
case 'a':
if ((L->elem)[j].average_score > (L->elem)[j + 1].average_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 's':
if ((L->elem)[j].sum_score > (L->elem)[j + 1].sum_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'l':
if ((L->elem)[j].score_literature > (L->elem)[j + 1].score_literature)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'm':
if ((L->elem)[j].score_math > (L->elem)[j + 1].score_math)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'e':
if ((L->elem)[j].score_english > (L->elem)[j + 1].score_english)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'n':
if ((L->elem)[j].num > (L->elem)[j + 1].num)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
default:
printf("该主键不支持排序\n");
return ERROR;
}
}
}
}
return OK;
}
kernel_list.h
の的完整代码:#pragma once // 解决宏名引发的各种奇怪问题/Solve various strange problems caused by macro names
/*********************************************************************
* 转载请注明来源/Reprint please indicate the source
* @FileName kernellist.h
* @Description 核心线性表/core linear table
* @History
* version author data introduction and operations
* 1.0 初见 2022-01-23 Create
* *** *** ****-**-** *******
*/
#include "preconf.h"
/*********************************************************************
* @chujian(cn) 引入自定义头文件
* @chujian(en) Import custom header files
*/
Status InitList(SqList* L)
{
L->elem = (ElemType*)malloc(LIST_INIT_SIZE * sizeof(ElemType));
if (!(L->elem))
exit(OVERFLOWED);
L->length = 0;
L->list_size = LIST_INIT_SIZE;
return OK;
}
/*********************************************************************
* @chujian(cn) 初始化
* @chujian(en) Initialize
*/
Status DestroyList(SqList* L)
{
free(L->elem);
L->elem = NULL;
L->length = 0;
L->list_size = 0;
return OK;
}
/*********************************************************************
* @chujian(cn) 销毁
* @chujian(en) Destroy
*/
Status ClearList(SqList* L)
{
L->length = 0;
return OK;
}
/*********************************************************************
* @chujian(cn) 清空
* @chujian(en) Reset
*/
Boolean ListEmpty(SqList* L)
{
if (L->length == 0)
return TRUE;
else
return FALSE;
}
/*********************************************************************
* @chujian(cn) 判断空表
* @chujian(en) Judge the empty table
*/
int ListLength(SqList* L)
{
return L->length;
}
/*********************************************************************
* @chujian(cn) 返回长度
* @chujian(en) Retrun length of the table
*/
Status GetElem(SqList* L, int i, ElemType* e)
{
if (i < 1 || i > ListLength(L))
exit(ERROR);
*e = *((L->elem) + i - 1);
return OK;
}
/*********************************************************************
* @chujian(cn) 获取第i个数据元素
* @chujian(en) Get the i-th data element
*/
int LocateElem(SqList* L, ElemType* e, Status(*compare)(ElemType, ElemType))
{
ElemType* p;
int i = 1;
p = L->elem;
while (i <= (L->length) && !(*compare)(*e, *(p++)))
++i;
if (i <= (L->length))
return i;
else
return 0;
}
/*********************************************************************
* @chujian(cn) 返回满足特定关系的数据元素位序
* @chujian(en) Returns the bit order of data elements that satisfy a specified relationship
*/
Status SearchElem(SqList* L, int cur_e, ElemType* e, int mode_manner)
{
ElemType* r = L->elem;
int i = 1;
while (i <= L->length && r->num != cur_e)
{
i++;
r++;
}
if (i > L->length)
return INFEASIBLE;
switch (mode_manner)
{
case -1:
*e = *(--r);
return OK;
case 0:
*e = *r;
return OK;
case 1:
*e = *(++r);
return OK;
default:
return ERROR;
}
return OK;
}
/*********************************************************************
* @chujian(cn) 学号搜索引擎
* @chujian(en) Student ID Search Engine
*/
Status ListInsert(SqList* L, int i, ElemType e)
{
if (i < 1 || i >(L->length) + 1)
return ERROR;
if (L->length >= L->list_size)
{
ElemType* new_base = (ElemType*)realloc(L->elem, (L->length + LIST_INCREMENT) * sizeof(ElemType));
if (!new_base)
exit(OVERFLOWED);
L->elem = new_base;
L->list_size += LIST_INCREMENT;
}
ElemType* q = &(L->elem[i - 1]);
ElemType* p = &(L->elem[(L->length) - 1]);
for (p = &(L->elem[(L->length) - 1]); p >= q; --p) /* 插入位置及之后的元素右移 */
*(p + 1) = *p;
*q = e;
++(L->length);
return OK;
}
/*********************************************************************
* @chujian(cn) 插入
* @chujian(en) insert
*/
Status ListDelete(SqList* L, int i, ElemType* e)
{
if (i < 1 || i > L->length)
return ERROR;
ElemType* p = L->elem + i - 1;
*e = *p;
ElemType* q = L->elem + L->length - 1;
for (++p; p <= q; ++p)
*(p - 1) = *p;
--(L->length);
return OK;
}
/*********************************************************************
* @chujian(cn) 删除
* @chujian(en) delete
*/
Status ListTraverse(SqList* L, void(*visit)(ElemType*))
{
int i;
ElemType* p = L->elem;
for (i = 1; i <= L->length; ++i)
visit(p);
return OK;
}
/*********************************************************************
* @chujian(cn) 遍历所有数据进行同等操作
* @chujian(en) Iterate over all data for the same operation
*/
Status SwapElem(ElemType* x, ElemType* y)
{
ElemType* z = (ElemType*)malloc(sizeof(ElemType*)+1);
if (z == NULL)
return OVERFLOWED;
*z = *x;
*x = *y;
*y = *z;
return OK;
}
/*********************************************************************
* @chujian(cn) 交换两个数据元素的位置
* @chujian(en) Swap the positions of two data elements
*/
Status ListSort(SqList* L, int sort_manner, char key)
{
int i, j;
Boolean flag = TRUE;
for (i = 0; i < (L->length) - 1 && flag == TRUE; i++)
{
flag = FALSE;
for (j = 0; j < (L->length) - i - 1; j++)
{
if (sort_manner <= 0)
{
switch (key)
{
case 'a':
if ((L->elem)[j].average_score < (L->elem)[j + 1].average_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 's':
if ((L->elem)[j].sum_score < (L->elem)[j + 1].sum_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'l':
if ((L->elem)[j].score_literature < (L->elem)[j + 1].score_literature)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'm':
if ((L->elem)[j].score_math < (L->elem)[j + 1].score_math)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'e':
if ((L->elem)[j].score_english < (L->elem)[j + 1].score_english)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'n':
if ((L->elem)[j].num < (L->elem)[j + 1].num)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
default:
printf("该主键不支持排序\n");
return ERROR;
}
}
else
{
switch (key)
{
case 'a':
if ((L->elem)[j].average_score > (L->elem)[j + 1].average_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 's':
if ((L->elem)[j].sum_score > (L->elem)[j + 1].sum_score)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'l':
if ((L->elem)[j].score_literature > (L->elem)[j + 1].score_literature)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'm':
if ((L->elem)[j].score_math > (L->elem)[j + 1].score_math)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'e':
if ((L->elem)[j].score_english > (L->elem)[j + 1].score_english)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
case 'n':
if ((L->elem)[j].num > (L->elem)[j + 1].num)
{
SwapElem(&((L->elem)[j]), &((L->elem)[j + 1]));
flag = TRUE;
}
break;
default:
printf("该主键不支持排序\n");
return ERROR;
}
}
}
}
return OK;
}
/*********************************************************************
* @chujian(cn) 冒泡排序(看起来比较繁琐,因为多个不同的排序项)
* @chujian(en) Bubble Sor(Seems cumbersome here because of multiple different sort items)
*/