绪论:
本篇文章使结构体章节后的习题,如果你对C语言有问题,或者结构体有什么问题不妨看看我之前所写的文章(章回体),对于文件管理和动态内存分配问题(已补上直接改在源代码内,以及最后 有些解释)我将在后面补上,对于这个学生信息管理系统我用了多种方法和分源管理的形式来写可能内容偏多,但都有着重大意义,可以自行对我的源码进行copy和删减一些用了多种方法的地方,当然直接用也是没什么问题的。
附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要
思维导图
要XMind思维导图的话可以私信哈
逐步实现的目录
1.界面的创建、和功能的选择
1.1分源管理
1.1.1在主函数内
1.1.3在contact.h
1.1.2在contact.c
2.功能的实现
2.1分源管理
2.1.1在主函数内
2.1.2在contact.h
2.1.3在contact.c
3.文件的保存、和文件的调用
3.1分源管理
3.1.1在主函数内
3.1.2在contact.c
3.1.3在contact.h
补充:
对于分源管理:这样做的原因是可以将这个程序的实现,分开来实现,主函数放在了test.c中这里就像一个枢纽(主干)来选择功能,而contact.c 中放着的是这些主干后的分支(他代表着所要实现的功能的具体代码),而contact.h(头文件)这里存放着一些在test.c 和 contact.c 同时需要用到的东西,如头文件的应用、#define 定义的常量 、以及函数的声明 、这样只需在.c(源文件)中引用#include"contact" 即可包含该文件中的内容
下面先上源码:
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
enum function
{
Exit = 0,
Add,
Del,
Search,
Modeify,
Show,
Sort,
Refresh
};
void menu()
{
printf("********************************\n");
printf("********************************\n");
printf("***** 1. Add 2.Del *****\n");
printf("********************************\n");
printf("***** 3.Search 4.Modeify *****\n");
printf("********************************\n");
printf("***** 5.Show 6.Sort *****\n");
printf("********************************\n");
printf("***** 7.refresh 0.exit *****\n");
printf("********************************\n");
printf("********************************\n");
//1)添加学生信息
//2)删除学生信息
//3)查询学生信息
//4)修改学生信息
//5)展示所有学生信息
//6)重新排序学生信息
//7)刷新学生信息
}
int main()
{
int input = 0;
Info_System con ;
//初始化
InitCon(&con);
//初始化方法二:Info_System con = {0};直接将全部置成0
do
{
menu();
printf("请选择>:");
scanf("%d", &input);
switch(input)
{
case Add:
Add_Stu_message(&con);
break;
case Del:
Del_Stu_message(&con);
break;
case Search:
Search_Stu_message(&con);
break;
case Modeify:
Modeifyh_Stu_message(&con);
break;
case Show:
Show_Stu_message(&con);
break;
case Sort:
Sort_Stu_message(&con);
break;
case Refresh:
Refresh_Stu_message(&con);
break;
case Exit:
DestoryContact(&con);
printf("退出\n");
break;
default:
printf("选择错误,重新选择:\n");
break;
}
} while (input);
return 0;
}
contact.h:
#pragma once
#include
#include
#include
#include
#define Name_Max 20
#define Sex_Max 5
#define Class_Max 20
#define Room_Max 20
#define PeoMax 100
typedef struct PeoInfo
{
int ID;// 学号
char Name[Name_Max];// 姓名
char Sex[Sex_Max];// 性别
char Class[Class_Max];// 班级
char Room[Room_Max];// 宿舍号
int Score;// 成绩
}PeoInfo;
typedef struct management_System
{
PeoInfo* Contact;//存放人的信息
int sz;//已近存放了多少个人
int capacity;//容量大小
}Info_System;
void InitCon(Info_System *con);
void Add_Stu_message(Info_System* con);
void Show_Stu_message(const Info_System* con);
void Del_Stu_message (Info_System* con);
void Search_Stu_message(const Info_System* con);
void Modeifyh_Stu_message(Info_System* con);
void Sort_Stu_message(Info_System* con);
void Refresh_Stu_message(Info_System* con);
void DestroyContact(Info_System* con);
contact.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void AddCapacity(Info_System* con)
{
assert(con);
PeoInfo* ptr = (PeoInfo*)realloc(con->Contact,sizeof(PeoInfo) * (AddCap+ con->capacity));
if (ptr == NULL)
{
perror("realloc");
return ;
}
con->Contact = ptr;
con->capacity += AddCap;
ptr = NULL;
printf("增容成功\n");
}
//初始化
void InitCon(Info_System* con)
{
assert(con);
con->sz = 0;
memset(con->Contact, 0, sizeof(Info_System));//利用memset将一块连续的空间初始化成0最后要初始化多少个字节
}
int FindById(int Id, const Info_System* con)
{
int t = con->sz;//不要改变sz,用t来代替需要查找的人
while (t--)
{
if (Id == con->Contact[t].ID)
{
return t;
}
}
printf("找不到此人\n");
return -1 ;//返回-1是因为区别于返回0(返回0表示查找的人的下标为0)
}
int FindByName(char * name,const Info_System* con)
{
int t = con->sz;//不要改变sz,用t来代替需要查找的人
while (t--)
{
if (strcmp(name, con->Contact[t].Name) == 0)//--ret 是因为ret表示的是总人数,而总人数-1才能找到最后一个人
{
return t;
}
}
printf("找不到此人\n");
return -1;//返回-1是因为区别于返回0(返回0表示查找的人的下标为0)
}
//增加
void Add_Stu_message(Info_System* con)
{
assert(con);
if (con->sz == PeoMax)
{
AddCapacity(con);
}
printf("依次输入学号 名字 班级 性别 宿舍 计算机成绩\n并且以空格/回车分隔\n");
scanf("%d %s %s %s %s %d", &con->Contact[con->sz].ID,
con->Contact[con->sz].Name,
con->Contact[con->sz].Class,
con->Contact[con->sz].Sex,
con->Contact[con->sz].Room,
&con->Contact[con->sz].Score
);
con->sz += 1;
//con->sz++;
printf("添加完成\n");
}
void Show_Stu_message(const Info_System* con)
{
assert(con);
printf("%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩");
for (int i = 0; i < con->sz;i++)//解引用操作符的优先级都很(最)高
{
printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[i].ID,
con->Contact[i].Name,
con->Contact[i].Class,
con->Contact[i].Sex,
con->Contact[i].Room,
con->Contact[i].Score);
}
}
void Del_Stu_message(Info_System* con)
{
assert(con);
if (con->sz == 0)
{
printf("没有学生信息\n");
return;
}
int input = 0;
printf("输入学号进行删除;>");
scanf("%d", &input);
int ret = FindById(input,con);
if (ret != -1)
{
for (ret; ret < con-> sz - 1; ret++) //sz -1 也要转化成下标
{
con->Contact[ret] = con->Contact[ret+1]; //注意返回的就是所对应的下标
}
con->sz--;
printf("删除成功\n");
}
//if (ret!=-1)
//{
// memmove(con->Contact + ret, con->Contact + ret + 1, (con->sz)*sizeof(PeoInfo) - (ret) * sizeof(PeoInfo));
// con->sz--;
// printf("删除成功\n");
//}
}
void Search_Stu_message(const Info_System* con)
{
assert(con);
int i = 0;
printf("1.Id\n2.Name\n选择查找方法:>");
scanf("%d", &i);
if (i == 1)
{
int input = 0;
printf("输入学号进行查找:>");
scanf("%d", &input);
int ret = FindById(input, con);
if (ret != -1)
{
printf("%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩");
printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[ret].ID,
con->Contact[ret].Name,
con->Contact[ret].Class,
con->Contact[ret].Sex,
con->Contact[ret].Room,
con->Contact[ret].Score);
}
}
else if (i == 2)
{
char name[20] = { 0 };
printf("输入姓名进行查找:>");
scanf("%s",name);
int ret = FindByName(name, con);
if (ret != -1)
{
printf("%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩");
printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[ret].ID,
con->Contact[ret].Name,
con->Contact[ret].Class,
con->Contact[ret].Sex,
con->Contact[ret].Room,
con->Contact[ret].Score);
}
}
else
{
printf("选择错误\n");
}
}
void Modeifyh_Stu_message(Info_System* con)
{
assert(con);
int i = 0;
printf("1.Id\n2.Name\n选择查找方法:>");
scanf("%d", &i);
if (i == 1)
{
int input = 0;
printf("输入要修改的学生信息的学号:>");
scanf("%d", &input);
int ret = FindById(input, con);
if (ret != -1)
{
printf("修改:>\n");
printf("依次输入学号 名字 班级 性别 宿舍 计算机成绩\n并且以空格/回车分隔\n");
scanf("%d %s %s %s %s %d", &con->Contact[ret].ID,
con->Contact[ret].Name,
con->Contact[ret].Class,
con->Contact[ret].Sex,
con->Contact[ret].Room,
&con->Contact[ret].Score
);
printf("修改完成\n");
}
}
else if (i == 2)
{
char name[20] = { 0 };
printf("输入要修改的学生信息的姓名:>");
scanf("%s", name);
int ret = FindByName(name, con);
if (ret != -1)
{
printf("修改:>\n");
printf("依次输入学号 名字 班级 性别 宿舍 计算机成绩\n并且以空格/回车分隔\n");
scanf("%d %s %s %s %s %d", &con->Contact[ret].ID,
con->Contact[ret].Name,
con->Contact[ret].Class,
con->Contact[ret].Sex,
con->Contact[ret].Room,
&con->Contact[ret].Score
);
printf("修改完成\n");
}
}
else
{
printf("选择错误\n");
}
}
int Sort_Name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->Name, ((PeoInfo*)e2)->Name);
}
int Sort_age(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->ID - ((PeoInfo*)e2)->ID;
}
void Sort_Stu_message(Info_System* con)
{
assert(con);
int input = 0;
printf("1.以名字排序\n2.以学号排序\n");
scanf("%d", &input);
switch (input)
{
case 1:
qsort(con->Contact, con->sz, sizeof(con->Contact[0]), Sort_Name);
printf("排序成功\n");
break;
case 2:
qsort(con->Contact, con->sz, sizeof(con->Contact[0]), Sort_age);
printf("排序成功\n");
break;
default:
printf("输入错误\n");
break;
}
}
void Refresh_Stu_message(Info_System* con)
{
InitCon(con);
printf("刷新成功\n");
}
void DestroyContact(Info_System* con)
{
free(con->Contact);
con->Contact = NULL;
con->capacity = 0;
con->sz = 0;
con = NULL;
}
在main函数内创建一个目录函数
int main() { menu(); retrn 0; }
直接通过打印的方法,打印出自己所想要的目录(界面)
void menu() { printf("********************************\n"); printf("********************************\n"); printf("***** 1. Add 2.Del *****\n"); printf("********************************\n"); printf("***** 3.Search 4.Modeify *****\n"); printf("********************************\n"); printf("***** 5.Show 6.Sort *****\n"); printf("********************************\n"); printf("***** 7.refresh 0.exit *****\n"); printf("********************************\n"); printf("********************************\n"); //1)添加学生信息 //2)删除学生信息 //3)查询学生信息 //4)修改学生信息 //5)展示所有学生信息 //6)重新排序学生信息 //7)刷新学生信息 }
通过dowhile循环来不断的进行选择并且通过switch语句来控制所选择功能
int main() { int input = 0; do { printf("请选择>:"); scanf("%d", &input); switch(input) { case Add: Add_Stu_message(&con); break; case Del: Del_Stu_message(&con); break; case Search: Search_Stu_message(&con); break; case Modeify: Modeifyh_Stu_message(&con); break; case Show: Show_Stu_message(&con); break; case Sort: Sort_Stu_message(&con); break; case Refresh: Refresh_Stu_message(&con); break; case Exit: printf("退出\n"); break; default: printf("选择错误,重新选择:\n"); break; } } while (input); return 0; }
一个学生的信息可能有 学号、姓名、性别、班级、宿舍、成绩
所以我们可以用一个结构体来存,并且用typedef来将复杂的名字简化
typedef struct PeoInfo { int ID;// 学号 char Name[Name_Max];// 姓名 char Sex[Sex_Max];// 性别 char Class[Class_Max];// 班级 char Room[Room_Max];// 宿舍号 int Score;// 成绩 }PeoInfo;
同样用到typedef来简化
typedef struct management_System { PeoInfo Contact[PeoMax];//存放人的信息 int sz;//已近存放了多少个人 }Info_System;
1.1.2在contact.c
对于已经创建好的结构体,他们此时还是一些随机值需要初始化
所以写一个初始化函数来进行初始化
void InitCon(Info_System* con) { assert(con); con->sz = 0; memset(con->Contact, 0, sizeof(Info_System));//利用memset将一块连续的空间初始化成0最后要初始化多少个字节 }
第一个元素放目标地址,第二个元素表示要放东西的整形形式(如0 -> 0 ;而 'a' -> 97),第三个元素表示这个目标的大小(byte),具体用法可以看这篇博客:进阶C语言第三章-------《字符函数和内存函数》 完整思维导图+基本练习题+深入细节+通俗易懂+知识点+建议收藏_溟洵的博客-CSDN博客
附:首先,对于这些进行解释:他是通过枚举的办法实现的具体可以看看我写的一篇关于自定义类型的blog;其次,是对这个的解释
这是引用contact.h头文件,包含其里面的内容(函数的头文件,和函数声明)
通过目录的选择后进入功能内部
添加学生信息
对于下面的scanf操作我们需要引用并且存进结构体中 对于数组来说不需要加上&
因为其数组名表示其地址 而对于整形就需要&了
先从con这是Info_System结构体的变量所以通过->访问其里面的成员Contact而其又是PeoInfo结构体的数组变量所以就可以通过访问数组空间再用.操作符来找到学生的信息,
这里不用-> 是因为前面con->Contact[con->sz] 已经表示成Contact[PeoMax]结构体变量了不需要在*解引用了void Add_Stu_message(Info_System* con) { assert(con); if (con->sz == PeoMax) { printf("信息已满,请删除或刷新后再试\n"); return; } printf("依次输入%s %s %s %s %s %s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩"); scanf("%d %s %s %s %s %d", &con->Contact[con->sz].ID, con->Contact[con->sz].Name, con->Contact[con->sz].Class, con->Contact[con->sz].Sex, con->Contact[con->sz].Room, &con->Contact[con->sz].Score ); printf("添加完成\n"); }
展示学生信息
void Show_Stu_message(Info_System* con) { assert(con); printf("%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩"); for (int i = 0; i < con->sz;i++) { printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[i].ID, con->Contact[i].Name, con->Contact[i].Class, con->Contact[i].Sex, con->Contact[i].Room, con->Contact[i].Score ); } }
对于printf打印只需要满足所给条件(%d)和所写条件(con->Contact[i].ID)对应的类型一致即可。
删除学生信息
void Del_Stu_message(Info_System* con) { assert(con); if (con->sz == 0) { printf("没有学生信息\n"); return; } int input = 0; printf("输入学号进行删除;>"); scanf("%d", &input); int ret = FindById(input,con); if (ret != -1) { for (ret; ret < con-> sz - 1; ret++) //sz -1 也要转化成下标 { con->Contact[ret] = con->Contact[ret+1]; //注意返回的就是所对应的下标 } con->sz--; printf("删除成功\n"); } //if (ret!=-1) //{ // memmove(con->Contact + ret, con->Contact + ret + 1, (con->sz)*sizeof(PeoInfo) - (ret) * sizeof(PeoInfo)); // con->sz--; // printf("删除成功\n"); //} }
查找函数:
int FindByName(char * name,const Info_System* con)
{
int t = con->sz;//不要改变sz,用t来代替需要查找的人
while (t--)
{
if (strcmp(name, con->Contact[t].Name) == 0)//--ret 是因为ret表示的是总人数,而总人数-1才能找到最后一个人
{
return t;
}
}
printf("找不到此人\n");
return -1;//返回-1是因为区别于返回0(返回0表示查找的人的下标为0)
}
int FindById(int Id, const Info_System* con)
{
int t = con->sz;//不要改变sz,用t来代替需要查找的人
while (t--)
{
if (Id == con->Contact[t].ID)
{
return t;
}
}
printf("找不到此人\n");
return -1 ;//返回-1是因为区别于返回0(返回0表示查找的人的下标为0)
}
查找学生信息
void Search_Stu_message(const Info_System* con) { assert(con); int i = 0; printf("1.Id\n2.Name\n选择查找方法:>"); scanf("%d", &i); if (i == 1) { int input = 0; printf("输入学号进行查找:>"); scanf("%d", &input); int ret = FindById(input, con); if (ret != -1) { printf("%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩"); printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[ret].ID, con->Contact[ret].Name, con->Contact[ret].Class, con->Contact[ret].Sex, con->Contact[ret].Room, con->Contact[ret].Score); } } else if (i == 2) { char name[20] = { 0 }; printf("输入姓名进行查找:>"); scanf("%s",name); int ret = FindByName(name, con); if (ret != -1) { printf("%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩"); printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[ret].ID, con->Contact[ret].Name, con->Contact[ret].Class, con->Contact[ret].Sex, con->Contact[ret].Room, con->Contact[ret].Score); } } else { printf("选择错误\n"); } }
修改学生信息
void Modeifyh_Stu_message(Info_System* con) { int i = 0; printf("1.Id\n2.Name\n选择查找方法:>"); scanf("%d", &i); if (i == 1) { int input = 0; printf("输入要修改的学生信息的学号:>"); scanf("%d", &input); int ret = FindById(input, con); if (ret != -1) { printf("原信息:\n%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩"); printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[ret].ID, con->Contact[ret].Name, con->Contact[ret].Class, con->Contact[ret].Sex, con->Contact[ret].Room, con->Contact[ret].Score); printf("修改:>\n"); printf("依次输入学号 名字 班级 性别 宿舍 计算机成绩\n并且以空格/回车分隔\n"); scanf("%d %s %s %s %s %d", &con->Contact[ret].ID, con->Contact[ret].Name, con->Contact[ret].Class, con->Contact[ret].Sex, con->Contact[ret].Room, &con->Contact[ret].Score ); } } else if (i == 2) { char name[20] = { 0 }; printf("输入要修改的学生信息的姓名:>"); scanf("%s", name); int ret = FindByName(name, con); if (ret != -1) { printf("原信息:\n%-10s %-20s %-20s %-5s %-20s %-10s\n", "学号", "名字", "班级", "性别", "宿舍", "计算机成绩"); printf("%-10d %-20s %-20s %-5s %-20s %-10d\n", con->Contact[ret].ID, con->Contact[ret].Name, con->Contact[ret].Class, con->Contact[ret].Sex, con->Contact[ret].Room, con->Contact[ret].Score); printf("修改:>\n"); printf("依次输入学号 名字 班级 性别 宿舍 计算机成绩\n并且以空格/回车分隔\n"); scanf("%d %s %s %s %s %d", &con->Contact[ret].ID, con->Contact[ret].Name, con->Contact[ret].Class, con->Contact[ret].Sex, con->Contact[ret].Room, &con->Contact[ret].Score ); } } else { printf("选择错误\n"); } }
排序学生信息
int Sort_Name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->Name, ((PeoInfo*)e2)->Name);
//注意强转要写成 PeoInfo* 结构体类型
//返回大于0的数才交换,即是升序形式
}
int Sort_age(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->ID - ((PeoInfo*)e2)->ID;
}
void Sort_Stu_message(Info_System* con)
{
assert(con);
int input = 0;
printf("1.以名字排序\n2.以学号排序\n");
scanf("%d", &input);
switch (input)
{
case 1:
//ASCII升序
qsort(con->Contact, con->sz, sizeof(con->Contact[0]), Sort_Name);//头文件stdlib,比较函数int compare(const void *elem1, const void *elem2)
//此处con->Contact 直接找到了数组名传进去,所以qsort内所接受到的结构体时PeoInfo类型的
printf("排序成功\n");
break;
case 2:
//升序
qsort(con->Contact, con->sz, sizeof(con->Contact[0]), Sort_age);
printf("排序成功\n");
break;
default:
printf("输入错误\n");
break;
}
}
刷新学生信息
void Refresh_Stu_message(Info_System* con)
{
InitCon(con);//直接再次调用初始化函数
printf("刷新成功\n");
}
动态内存管理:
对于动态内存来说,他所要修改的地方是空间的开辟问题,对此我们
首先需要重新定义一下结构体:来知道容量的大小确保容量的够用
//静态的 //typedef struct management_System //{ // // PeoInfo Contact[PeoMax];//存放人的信息 // int sz;//已近存放了多少个人 //}Info_System; //动态的 typedef struct management_System { PeoInfo* Contact;//存放人的信息 int sz;//已近存放了多少个人 int capacity;//容量大小 }Info_System;
对此还需要再定义两个常量,来定义起始人数和后面容量不够时每次增加的容量
#define InitPeo 3 #define AddCap 2
(用#define定义是因为这样当你需要对程序进行修改时就可以直接在此处修改,而不要跑到代码内一处处的修改)
其次,因为是动态的所以我们还要修改一下Add函数,当进行增加人数时应该判断一下所开辟的空间是否够
void AddCapacity(Info_System* con) { assert(con); PeoInfo* ptr = (PeoInfo*)realloc(con->Contact,sizeof(PeoInfo) * (AddCap+ con->capacity)); if (ptr == NULL) { perror("realloc"); return ; } con->Contact = ptr; con->capacity += AddCap; ptr = NULL; } void Add_Stu_message(Info_System* con) { assert(con); if (con->sz == con->capacity) { AddCapacity(con); } printf("依次输入学号 名字 班级 性别 宿舍 计算机成绩\n并且以空格/回车分隔\n"); scanf("%d %s %s %s %s %d", &con->Contact[con->sz].ID, con->Contact[con->sz].Name, con->Contact[con->sz].Class, con->Contact[con->sz].Sex, con->Contact[con->sz].Room, &con->Contact[con->sz].Score ); con->sz += 1; //con->sz++; printf("添加完成\n"); }
最后,当我们退出程序时还应该将所借的空间归还给操作系统
//contact.c void DestroyContact(Info_System* con) { free(con->Contact); con->Contact = NULL; con->capacity = 0; con->sz = 0; con = NULL; } //contact.h void DestroyContact(Info_System* con); //test.c ... case Exit: DestroyContact(&con); printf("退出\n"); break; ...
敬请期待!
持续更新大量C语言知识,关注喔老铁!