目录
1.前言:
Hello everybody! 😄小邓儿,好久没更新了。今天重出江湖,带大家来唠唠顺序表。
2.顺序表是啥?
2.1定义:
顺序表:是线性表的一种物理存储结构,即顺序存储结构。指的是用一段地址连续的存储单元依此存储线性表的数据元素。
2.2存储方式:
顺序存储结构:在内存中找一块地儿,通过占位的形式,把一定内存空间给占了。然后,把相同数据类型的数据元素依次存放在这块空地上。
那到这里儿,咱们想一下,在C语言中,一个一维数组不就可以来实现相似的功能?所以,一维数组也是一种顺序存储结构,也就是顺序表。
3.顺序表的具体函数功能:
3.1定义一个顺序表(以int型为例)

这里,为什么咱们要用SLDateType来代替int给arr定义元素类型,直接用它不香么?相信很多小伙伴儿会有这个疑问。其实,也可以直接使用int,当如果你想存储的是其它类型的数据元素,如char等,这时你要将整个代码进行修改。这时SLDateType好处就体现出来了,只要修改修改typedef int SLDateType为typedef char SLDateType即可。是不是很方便( ̄▽ ̄)"
3.2初始化顺序表:

3.3扩容:
3.4插入:
3.4.1:头插:
咱们的思路就是把顺序表中元素像蓝色箭头所指,依次向后移动一个位置。然后,把要插入的数据放入第一个位置。

代码如下,不过,只要涉及插入的,都要先检查一下空间是否足够,是否要扩容哦。

3.4.2尾插:
尾插的思路就很简单了,直接在最后插入即可。

3.4.3任意位置插入:
这个听起来是不是很难,且听小邓儿来分析。假如,我们想将x插入到3这个位置,我们的思路不就是将3及后面的元素依次移动到下一个位置,再将x放到3位置上吗?
没错,思路正确。不过,元素要从后向前遍历。咱们想一下,如是从前向后会怎么样?结果是后面的数据会被覆盖,就没法实现想要的功能了。


3.5删除:
3.5.1头删:
咱们把第一个元素删除,可以直接依次把第二个元素向前移动,最后--ps->size。


3.5.2尾删:

3.5.3任意位置删除:
假设我们要删除3位置上的元素,只要把第4个及以后的元素依次向前移动一个位置就好了。


3.6查找:

3.7销毁:
在使用完顺序表后,不要忘记销毁顺序表哦。这样,可以避免一些不必要的空间浪费。

4.学生信息系统的设计
首先,让咱们来看看具体要实现的功能有哪些(●'◡'●)

4.1录入学生记录:
此功能小邓儿用了二进制文件来录入。具体代码如下,感兴趣的小伙伴儿可以去了解一下二进制文件的相关概念。
//文件存储
int SaveList(SL* L, const char* filename)
{
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
perror("文件打开失败");
return -1;
}
size_t written = fwrite(L->data,sizeof(Stu), L->length, fp);
fclose(fp);
return (written == L->length) ? 0 : -1;
}
// 写入可读文件
int SaveText(SL* list, const char* filename) {
FILE* fp = fopen(filename, "w");
if (!fp) return -1;
for (int i = 0; i < list->length; i++) {
fprintf(fp, "%s,%s,%.1f\n",
list->data[i].id,
list->data[i].name,
list->data[i].score);
}
fclose(fp);
return 0;
}
// 读取文件函数
int LoadList(SL* list, const char* filename)
{
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
return -1;
}
// 获取文件大小并计算记录数
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
rewind(fp);
int num_records = file_size / sizeof(Stu);
if (file_size % sizeof(Stu) != 0) {
fclose(fp);
return -1; // 文件内容不完整
}
// 检查容量是否足够
if (num_records > MaxSize) {
fclose(fp);
return -1; // 超出存储容量
}
// 读取数据
size_t read = fread(list->data, sizeof(Stu), num_records, fp);
fclose(fp);
if (read != num_records) {
return -1; // 读取不完整
}
list->length = num_records;
return 0;
}
//异常处理
bool Input_compliant(char* stunum, char* stuname, float stuscore)
{// 进一步判断输入的学号、姓名和成绩是否合理
// 遍历每个字符进行校验
for (; *stunum != '\0'; stunum++) {//学号只能由数字构成
if (!isdigit((unsigned char)*stunum)) {
return false; // 发现非数字字符立即返回失败
}
}
for (; *stuname != '\0'; stuname++) {
unsigned char c = (unsigned char)*stuname;
if (!isalpha(c)) { // 允许空格和连字符
return false;
}
}
if (stuscore < 0 || stuscore>100) { //成绩应在合理区间[0,100]
return false;
}
return true;
}
void Input_stuinfo(SL* L)
{//读取用户输入并写入线性表
int info_num = L->length;
char inputinfo[100];
printf("请按照以下格式录入学生信息(8052 zhangsan 89),以'#'结束!\n");
while (1)
{
char stunum[10] = { 0 };
char stuname[20] = { 0 };
float stuscore;
fgets(inputinfo, sizeof(inputinfo), stdin);
inputinfo[strcspn(inputinfo, "\n")] = '\0'; // 去除末尾换行符
// 检查是否输入终止符 `#`
if (strcmp(inputinfo, "#") == 0) {
printf("停止录入,正在返回主界面...\n");
break;
}
// 解析输入的Num_Data_Items个字段
if (sscanf(inputinfo, "%s %s %f", stunum, stuname, &stuscore) != Num_Data_Items)
{
printf("信息未通过形式审查,建议检查并删除多余空格,然后重新录入...\n");
continue;
}
else
{
if (!Input_compliant(stunum, stuname, stuscore))
{
printf("错误!学号由数字构成,姓名由字母构成,成绩要在[1,100]之间,请重新录入...\n");
continue;
}
printf("输入的学号、姓名、成绩符合规定,正在录入顺序表...\n");
strcpy(L->data[info_num].id, stunum);
strcpy(L->data[info_num].name, stuname);
L->data[info_num].score = stuscore;
info_num++;
printf("录入成功!请继续录入下一条学生信息,以'#'结束!...\n");
}
}
L->length = info_num;
}
4.2 添加学生信息记录:
这里,咱们用的就是刚刚任意位置插入的方法。


4.3 按学号删除记录 :
同理,这里,咱们用的就是刚刚任意位置删除的方法。


4.4 按学号修改记录:


这里的Change函数,小邓儿用的是strcmp函数 。
4.5按姓名查找记录 :


这里的Search函数,其实就是在刚刚的 SLFind函数上做了一个小小的变动,大体思路都是遍历元素,看看是否相同。
4.6显示所有记录 (即打印顺序表):

4.7 按照成绩排序 :


这里,小邓儿用的是冒泡排序法,如果大家还有更好的方法,欢迎评论留言哦。
4.8 清理控制面板:
咱们直接使用system("cls")这条语句就可以实现啦。
5.总代码:
#define _CRT_SECURE_NO_WARNINGS
#define MaxSize 200 // 顺序表最大元素数量
#define Num_Data_Items 3 // 每个数据元素的数量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <ctype.h>
#include<assert.h>
#include<stdbool.h>
typedef struct Student
{
char id[10];
char name[20];
float score;
}Stu;
typedef struct SeqList
{
Stu* data;
int length;
int capacity;
}SL;
//初始化顺序表
void InitList(SL** L)
{
*L = (SL*)malloc(sizeof(SL));
(*L)->data = (Stu*)malloc(4 * sizeof(Stu));
(*L)->length = 0;
(*L)->capacity = 4;
}
//扩容:当空间不足时自动扩容(每次增加5个位置)
void CheckCapacity(SL* L)
{
if (L->length >=L->capacity)
{
int newCapacity = (L->capacity == 0) ? 4 : L->capacity + 5;
Stu* tmp = realloc(L->data,newCapacity * sizeof(Stu));
if (!tmp)
{
printf("扩容失败!\n");
exit(1);
}
L->data= tmp;
L->capacity = newCapacity;
}
}
//插入记录
void Insert(SL* L, int pos, Stu s) {
assert(L);
assert(pos >= 0 && pos <= L->length);
CheckCapacity(L);
// 从后往前移动元素(包含pos位置)
for (int i = L->length; i > pos; i--) {
L->data[i] = L->data[i - 1];
}
L->data[pos] = s;
L->length++;
}
//查找功能:按姓名查找,返回第一个匹配的位置
int Search(SL*L, char* name)
{
for (int i = 0; i < L->length; i++)
{
if (strcmp(L->data[i].name,name)==0)
{
return i;
}
} return -1;
}
//删除:按学号查找并删除
void Delete(SL* L, char* id)
{
for (int i = 0; i < L->length; i++)
{
if (strcmp(L->data[i].id,id) == 0)
{
for (int j = i; j < L->length-1; j++)
{
L->data[j] = L->data[j + 1];
}
}
}
L->length--;
}
//修改学生信息
void Change(SL*L,char* id)
{
int found = 0;
for (int i = 0; i < L->length; i++) {
if (strcmp(L->data[i].id, id) == 0) {
printf("找到学生,请输入新信息(学号 姓名 成绩):\n");
scanf("%s%s%f", L->data[i].id, L->data[i].name, &L->data[i].score);
found = 1;
break; // 修改后退出循环
}
}
if (!found) {
printf("未找到学号为 %s 的学生!\n", id);
}
}
//输出
void DispList(SL*L)
{
for (int i = 0; i < L->length; i++)
{
printf("学号:%s 姓名:%s 成绩:%f\n",L->data[i].id, L->data[i].name,L->data[i].score);
}
}
//销毁
void Destroy(SL* L)
{
assert(L);
if (L->data)
free(L->data);
L->data = NULL;
L->length = L->capacity = 0;
}
//创新功能:按学生成绩排序(从高到低)
void score_sort(SL* L)
{
for (int i = 0; i < L->length-1; i++)
{
for (int j = 0; j < L->length - i - 1; j++)
{
if (L->data[j].score<L->data[j + 1].score)
{
Stu temp = L->data[j];
L->data[j] = L->data[j + 1];
L->data[j + 1] = temp;
}
}
}
}
//文件存储
int SaveList(SL* L, const char* filename)
{
FILE* fp = fopen(filename, "wb");
if (fp == NULL)
{
perror("文件打开失败");
return -1;
}
size_t written = fwrite(L->data,sizeof(Stu), L->length, fp);
fclose(fp);
return (written == L->length) ? 0 : -1;
}
// 写入可读文件
int SaveText(SL* list, const char* filename) {
FILE* fp = fopen(filename, "w");
if (!fp) return -1;
for (int i = 0; i < list->length; i++) {
fprintf(fp, "%s,%s,%.1f\n",
list->data[i].id,
list->data[i].name,
list->data[i].score);
}
fclose(fp);
return 0;
}
// 读取文件函数
int LoadList(SL* list, const char* filename)
{
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
return -1;
}
// 获取文件大小并计算记录数
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
rewind(fp);
int num_records = file_size / sizeof(Stu);
if (file_size % sizeof(Stu) != 0) {
fclose(fp);
return -1; // 文件内容不完整
}
// 检查容量是否足够
if (num_records > MaxSize) {
fclose(fp);
return -1; // 超出存储容量
}
// 读取数据
size_t read = fread(list->data, sizeof(Stu), num_records, fp);
fclose(fp);
if (read != num_records) {
return -1; // 读取不完整
}
list->length = num_records;
return 0;
}
//异常处理
bool Input_compliant(char* stunum, char* stuname, float stuscore)
{// 进一步判断输入的学号、姓名和成绩是否合理
// 遍历每个字符进行校验
for (; *stunum != '\0'; stunum++) {//学号只能由数字构成
if (!isdigit((unsigned char)*stunum)) {
return false; // 发现非数字字符立即返回失败
}
}
for (; *stuname != '\0'; stuname++) {
unsigned char c = (unsigned char)*stuname;
if (!isalpha(c)) { // 允许空格和连字符
return false;
}
}
if (stuscore < 0 || stuscore>100) { //成绩应在合理区间[0,100]
return false;
}
return true;
}
void Input_stuinfo(SL* L)
{//读取用户输入并写入线性表
int info_num = L->length;
char inputinfo[100];
printf("请按照以下格式录入学生信息(8052 zhangsan 89),以'#'结束!\n");
while (1)
{
char stunum[10] = { 0 };
char stuname[20] = { 0 };
float stuscore;
fgets(inputinfo, sizeof(inputinfo), stdin);
inputinfo[strcspn(inputinfo, "\n")] = '\0'; // 去除末尾换行符
// 检查是否输入终止符 `#`
if (strcmp(inputinfo, "#") == 0) {
printf("停止录入,正在返回主界面...\n");
break;
}
// 解析输入的Num_Data_Items个字段
if (sscanf(inputinfo, "%s %s %f", stunum, stuname, &stuscore) != Num_Data_Items)
{
printf("信息未通过形式审查,建议检查并删除多余空格,然后重新录入...\n");
continue;
}
else
{
if (!Input_compliant(stunum, stuname, stuscore))
{
printf("错误!学号由数字构成,姓名由字母构成,成绩要在[1,100]之间,请重新录入...\n");
continue;
}
printf("输入的学号、姓名、成绩符合规定,正在录入顺序表...\n");
strcpy(L->data[info_num].id, stunum);
strcpy(L->data[info_num].name, stuname);
L->data[info_num].score = stuscore;
info_num++;
printf("录入成功!请继续录入下一条学生信息,以'#'结束!...\n");
}
}
L->length = info_num;
}
void show_screen()
{
printf("*************** 功能选择 *****************\n");
printf("*************** 1:录入学生记录 ***************\n");
printf("*************** 2: 添加学生信息记录 ***************\n");
printf("*************** 3: 按学号删除记录 ***************\n");
printf("*************** 4: 按学号修改记录 ***************\n");
printf("*************** 5:按姓名查找记录 ***************\n");
printf("*************** 6:显示所有记录 ***************\n");
printf("*************** 7:按照成绩排序 ***************\n");
printf("*************** 8:清理控制面板 ***************\n");
printf("*************** 9:退出管理系统 ***************\n");
}
int main()
{
int choose; int i;
char filename_dat[] = "student_info.dat";
char filename_text[] = "student_info.text";
SL* stulist; //定义一个指向线性表的指针
InitList(&stulist); //初始化一个学生元素的线性表
int info_num = 0;
if (LoadList(stulist, filename_dat) == 0) {
printf("加载成功!共%d条记录\n", stulist->length);
}
while (1)
{
show_screen();
printf("请选择:\n");
scanf("%d", &choose);
int c;
Stu s = { 0,0,0 };
while ((c = getchar()) != '\n' && c != EOF);//清空scanf缓冲区
switch (choose)
{
case 1://录入学生信息
Input_stuinfo(stulist);
SaveList(stulist, filename_dat);
SaveText(stulist, filename_text); // 可读文本备份
break;
case 2:
//在指定序号i处增加学生信息,并将原表受影响的学生后移
printf("要修改的地址:\n");
scanf("%d", &i);
while ((c = getchar()) != '\n' && c != EOF); // 清理缓冲区
printf("请输入学号、姓名、成绩:\n");
scanf("%s%s%f", s.id, s.name, &s.score);
Insert(stulist, i, s);
printf("添加成功!请继续。\n");
break;
case 3://按学号删除学生信息,并将原表中后面的学生前移
printf("请输入要删除学生的学号:\n");
scanf("%s", s.id);
Delete(stulist, s.id);
printf("删除成功!请继续。\n");
break;
case 4://按学号修改学生信息
printf("要修改的学生学号:\n");
scanf("%s",s.id);
while ((c = getchar()) != '\n' && c != EOF);
Change(stulist, s.id);
printf("修改成功!请继续。\n");
break;
case 5://按照姓名查询学生信息
printf("要查找的学生姓名:\n");
scanf("%s", s.name);
int index = Search(stulist,s.name);
if (index != -1)
{
printf("找到学生,信息如下:\n");
printf("学号:%s 姓名:%s 成绩:%.1f\n",
stulist->data[index].id,
stulist->data[index].name,
stulist->data[index].score);
}
else {
printf("未找到姓名为 %s 的学生!\n", s.name);
}
break;
case 6://显示所有记录
DispList(stulist);
break;
case 7://按照成绩排序
score_sort(stulist);
printf("已按成绩从高到低排序,并保存到文件。\n");
SaveList(stulist, filename_dat);
SaveText(stulist, filename_text);
break;
case 8://清屏
system("cls");
break;
case 9://退出管理系统
printf("退出系统,拜拜!");
return;
default:
printf("功能尚未开放!\n");
break;
}
if (choose == 8&&choose==9)//程序终止
{
Destroy(stulist);
break;
}
}
return 0;
}
今天,小邓儿带大家用顺序表,实现了学生信息系统的功能。希望以后咱们一起努力,成为厉害的程序猴(●'◡'●)


4155

被折叠的 条评论
为什么被折叠?



