✨C语言程序项目✨
代码的成长,就是在一次一次的刻意练习中不断成长。
对于C语言的应用包含很多很多应用,在这篇文章我对于其中的几种小项目进行一定的介绍和解析
😊1.猜数字
🐼2.三子棋
🦔3.本地保存的动态人员管理系统本文由爱吃苹果的清梦友情提供
前言
首先,我们要对文件进行区分即定义头文件:member.h、 测试文件:test.c、和具体实现文件:Contact.c
一、❤️初始设置通讯录的个体特征
首先,要明白描述一个人的具体特征中要包括姓名,年龄,性别,电话号码,和具体居住地址。所以我们在C语言中要建立一个具体描述一个人的成员结构体。
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<Windows.h>
enum MAX//使用常量来定义对应数组指标的最大值,便于维护
{
ARR_MAX = 3,//使用enum来定义常量可以避免变量污染即可以定义
NAME_MAX = 10,
SEX_MAX = 5,
ARESS_MAX = 20,
TEL_MAX = 13
};
typedef struct peo//定义成员结构体并重命名使得struct peo等同于peo便于书写使用
{
char name[NAME_MAX];
char sex[SEX_MAX];
int old;
char tel_num[TEL_MAX];
char aress[ARESS_MAX];
}peo;
定义完了成员结构体便需要将成员结构体集合成人员管理系统。首先我们要了解到,一个管理系统要包括几种特征标签:现有人员数量,最大容量,和人员存放:这人员的存放有两种实现方式,一(静态版本)利用数组,来建立即创建系统结构体。
typedef struct SqLinst
{
//因为实现的是静态所以要开辟成员数组
peo arr[ARR_MAX];
int size;
int sum_size;
}SL;
但是,这种静态版本会占用栈上太多空间,影响程序运行.而且刚开始用的时候并不会有那么多人的信息,一上来就有那么多空间.根本用不完导致空间的浪费,让运行受到妨碍.所以我们要使得内存能够随着使用,以及数据的增多,内存也不断地变化.故我们要对管理结构体进行调整.转换思路.
typedef struct SqLinst//重命名系统结构体,便于使用
{
//因为实现的是动态所以要创建指针指向动态开辟空间,后续利用动态开辟来将成员集合到指针指向的空间
peo* arrry;
int size;
int sum_size;
}SL;
二.🤞主体函数的实现
根据系统性质我们要创建系统结构体对象并且对其中内容进行初始化,利用memset进行重置并赋值.所以我们要编写初始化结构函数.
//test.c
#include "list.h"
int main()
{
SL my_new;//创建系统结构体
SqListInit(&my_new);//结构体传参一般只用传入创建的系统结构体的地址
}
//list.h头文件进行相应包含和声明
void SqListInit(SL* new);
//Contact.c
static void ImProve_SumSize(SL* new)//static修饰函数则会将函数函数的外链性
{
peo* ret = (peo*)realloc(new->arrry, (new->sum_size + ARR_MAX)*sizeof(peo));/*利用realloc对数组进行扩容进一
增加一个初始数组最大容量,先存在临 时指针变量中*/
if (ret)
{
new->arrry= ret;//将临时指针赋给系统成员data存放处
new->sum_size += ARR_MAX;//扩容成功后就要将最大容量进行更改
printf("扩容成功\n");
}
else
{
printf("扩容失败\n");
return;
}
}
void judge(SL* new)
{
while (new->size >= new->sum_size)//判断是否满,若现有数据等于最大容量
{
printf("内存已满,即将进行扩容\n");
ImProve_SumSize(new);//扩容函数
}
}
void reset(SL* new)
{
memset(new->arrry, 0, sizeof(peo)*new->size);//利用memset进行内容重置.
new->sum_size = ARR_MAX;//将最大容量设为预先在max枚举常量列举的数组初始最大值
new->size = 0;//将现有容量置为0
}
void SqListInit(SL* new)
{
new->size = 0;
printf("初始化中\n");
int i = 0;
Sleep(1000);
system("cls");
int j = 0;
for (i = 0; i < 20; i++)
{
printf("进度条:");
for (j = 0; j <= i; j++)
{
printf("*");
}
Sleep(1000);
system("cls");
} //实现进度条初始化界面,来体现进入系统的过程
assert(new); //判断系统结构体指针是不是空指针,防止解引用空指针的问题出现
peo* tem = (peo*)malloc(sizeof(peo) * ARR_MAX);//动态开辟成员连续存储内存块,得到人员信息存放地址将动态开辟的地址存 //放到临时指针变量若不为空,才能将tem赋给成员数组的指针
if (tem != NULL)//进行判空
{
new->arrry = tem;
}
else
{
printf("通讯录创建失败\n");
return;
}
reset(new);//封装重置函数,对系统内的内容进行重置
peo ret = { 0 };
FILE* pf = fopen("ContactList.txt", "r");//对保存在本地的通讯录文件进行打开
if (pf == NULL)//判断是否打开文件成功,若不成功则会返回空指针
{
perror("SqListInit");//若出现错误则将错误信息进行打印
return;
}
while (fread(&ret, sizeof(peo), 1, pf))//对文件数据进行二进制读取
{
judge(new);//判断读取过程中的内存情况,若不够进行扩容
new->arrry[new->size] = ret;
new->size++;
}
printf("初始化完成\n");
Sleep(1000);//界面完善
system("cls");
fclose(pf);
pf = NULL;
}
这之后就是对需求进行分析,这个系统要包含: 增 , 删 , 查 , 改 , 排序 , 插入 , 打印 , 插入 , 更改 ,退出后自动保存.所以在主体函数上我们要对这几种接口进行区别响应.即调用switch语句根据输入不同调用不同接口,来响应操作.
//list.c
enum movement//将操作枚举常量使得操作具有意义,更好的维护程序
{
EXIT,
ADD,
DEL,
SORT,
PRINT,
PUTIN,
CHANGE,
SEARCH,
RESET,
};
void SqListInit(SL* new);
void SqListAdd(SL* new);
void SqListPrint(SL* new);
void SqListDel(SL* new);
void SqList_Sort(SL* new);
void SqListSearch(SL* new);
void SqListPutin(SL*new);
void SqListChange(SL*new);
void SqListDistory(SL*new);
void SqList_Save(SL* new);
void judege(SL* new);
void reset(SL* new);//对接口进行声明使得能够在多文件使用
//test.c
do
{
menu();
printf("请输入操作:>\n");
scanf("%d", &judge);
switch (judge)
{
case ADD:
SqListAdd(&my_new);//增添数据
break;
case DEL:
SqListDel(&my_new);//删除数据
break;
case SORT:
SqList_Sort(&my_new);//排序数据
break;
case PRINT:
SqListPrint(&my_new);//打印数据
break;
case PUTIN:
SqListPutin(&my_new);//插入数据
break;
case CHANGE:
SqListChange(&my_new);//改变数据内容
break;
case EXIT:
SqList_Save(&my_new);//保存数据
SqListDistory(&my_new);//销毁即归还申请空间
break;
case SEARCH:
SqListSearch(&my_new);//查找数据
break;
case RESET:
reset(&my_new);//重置数据,清空
break;
default:
printf("输入错误\n");
break;
}
system("pause");
system("cls");
} while (judge);
return 0;
}
三.🫥接口函数实现
总体函数已经布置完成,那么就是底层对函数接口进行实现,那么我们就直接上代码,对应的解释就在代码注释中:
void SqListInput(SL* new,int size)
{
//对成员各个特征进行输入
printf("请输入姓名:>");
scanf("%s", new->arrry[size].name);
printf("请输入性别:>");
scanf("%s", new->arrry[size].sex);
printf("请输入年龄:>");
scanf("%d", &new->arrry[size].old);
printf("请输入手机号:>");
scanf("%s", new->arrry[size].tel_num);
printf("请输入地址:>");
scanf("%s", new->arrry[size].aress);
if (size == new->size)//因为是封装的插入函数也需要调用,但是当传入的是插入位置的时候在插入函数中size已经加加所以就不需要
//故进行判断是哪个函数使用.
{
new->size++;
}
}
void SqListPrint(SL* new)
{
int i = 0;
printf("%-10s\t%-5s\t%-5s\t%-13s\t%-20s\t",
"NAME", "SEX", "OLD","TELE", "ARESS");//将人员信息的表头进行打印,并加以对齐和格式控制
printf("\n");
for (i = 0; i < new->size; i++)//对结构体的成员元素
{
printf("%-10s\t", new->arrry[i].name);
printf("%-5s\t", new->arrry[i].sex);
printf("%-5d\t", new->arrry[i].old);
printf("%-13s\t", new->arrry[i].tel_num);
printf("%-5s\t", new->arrry[i].aress);
printf("\n");
}
}
void SqListAdd(SL* new)//对人员信息进行增加.
{
judge(new);//在增加前判断是否满了,并进行扩容
SqListInput(new,new->size);//输入人员信息
printf("成功增加\n");
}
int search_name(void* name1, SL* new)//根据名字进行寻找,进行遍历利用strcmp进行比较.
{
int i = 0;
for (i = 0; i < new->size; i++)
{
if (strcmp((char*)name1, new->arrry[i].name) == 0)
{
return i;//若相等则返回下标
}
}
return -1;
}
int search_tel(void *tel,SL* new)//原理同上,之所以对不同指标进行封装函数,是为了构建函数数组,进行回调
{
int i = 0;
for (i = 0; i < new->size; i++)
{
if (strcmp((char*)tel, new->arrry[i].name) == 0)
{
return i;
}
}
return -1;
}
int search_old(void* p, SL* new)
{
int i = 0;
for (i = 0; i < new->size; i++)
{
if (*(int*)p == new->arrry[i].old)
{
return i;
}
}
return -1;
}
int search_sex(void* sex, SL* new)
{
int i = 0;
for (i = 0; i < new->size; i++)
{
if (strcmp((char*)sex, new->arrry[i].name) == 0)
{
return i;
}
}
return -1;
}
int search_are(void* aress, SL* new)
{
int i = 0;
for (i = 0; i < new->size; i++)
{
if (strcmp((char*)aress, new->arrry[i].name) == 0)
{
return i;
}
}
return -1;
}
void SqListDel(SL* new)//对元素进行删除,基本原理:将要删除成员元素用后边的成员元素进行覆盖,即是删除
{
printf("请输入要删除的成员姓名:>");
char name1[NAME_MAX] = { 0 };
scanf("%s", name1);
int num = search_name(name1, new);//这里也可以回调函数对不同指标进行寻找删除元素,但是常用的搜寻指标就是名字.
if (num == -1)
{
printf("没有找到删除成员\n");
return;
}
int i = 0;
for (i = num; i < new->size - 1; i++)
{
new->arrry[i] = new->arrry[i + 1];//进行覆盖
}
new->size--;//现有成员元素数减一
}
void menu1()//封装指标菜单,在要选择指标的时候进行调用
{
printf("********指标*******\n");
printf("* 1.name 2.old *\n");
printf("* 3.sex 4.tel_num *\n");
printf("* 5.aress *\n");
}
//下面几个指标函数同样是要在cmp比较函数组成函数指针数组进行回调,在排序函数会调用
int name_cmp(const void* e1, const void* e2)
{
return strcmp(((peo*)e1)->name, ((peo*)e2)->name);
}
int sex_cmp(const void* e1, const void* e2)
{
return strcmp(((peo*)e1)->sex, ((peo*)e2)->sex);
}
int tel_cmp(const void* e1, const void* e2)
{
return strcmp(((peo*)e1)->tel_num, ((peo*)e2)->tel_num);
}
int are_cmp(const void* e1, const void* e2)
{
return strcmp(((peo*)e1)->aress, ((peo*)e2)->aress);
}
int old_cmp(const void* e1, const void* e2)
{
return ((peo*)e1)->old - ((peo*)e2)->old;
}
//在排序函数中会交换数据所以要写一个交换函数,但是如果是c++的话就可以用库里边的交换函数
void swap(char* e1, char* e2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char ch = *(e1 + i);
*(e1 + i) = *(e2 + i);
*(e2 + i) = ch;
}
}
//具体排序功能实现即利用冒泡思路实现万能排序
void my_sort(void* e1, int num, int width, int (*cmp)(const void*, const void*))
{
int i = 0;
int j = 0;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - 1 - i; j++)
{
if (cmp((char*)e1 + j * width, (char*)e1 + (j + 1) * width) > 0)//因为传入的并不是固有类型是构造类型
{ //所以要根据传入的函数指针不同则调用不 //同比较函数来比较
swap((char*)e1 + j * width, (char*)e1 + (j + 1) * width, width);
}
}
}
}
void SqList_Sort(SL* new)//调用排序时的排序函数总调用平台
{
menu1();
printf("请输入排序指标;>");
int judge = 0;
scanf("%d", &judge);
int (*cmp[5])(const void* e1, const void* e2) = { name_cmp,old_cmp,sex_cmp,tel_cmp,are_cmp };
//构造函数指针数组来容纳封装的比较函数.
my_sort(new->arrry, new->size, sizeof(new->arrry[0]), cmp[judge - 1]);
//根据输入不同传入不同比较指标.
SqListPrint(new);
}
void SqListSearch(SL* new)
{
int num = 0;
int n = 0;
//搜查函数的集成平台
int (*arr[5])(void*, SL * new) = { search_name,search_old,search_sex,search_tel,search_are };
menu1();//对指标选择菜单进行打印
printf("请选择搜查项目\n");
scanf("%d", &num);
char ch[20] = { 0 };
int m = 0;
char ch1 = 0;
printf("请输入搜查内容\n");
if (num != 2)
{
scanf("%s", ch);
m = arr[num - 1](ch, new);//因为搜查数据类型就只有两种所以根据不同输入,调用不同搜查函数,一种是字符串,一种是整数所以,用if进行判断区分
}
else
{
scanf("%d", &n);
m = arr[num-1](&n, new);
}
if (m != -1)
{
printf("为第%d个元素\n", m + 1);
printf("%-10s\t%-5s\t%-13s\t%-20s\t",
"NAME", "SEX", "TELE", "ARESS");
printf("\n");
printf("%-10s\t", new->arrry[m].name);
printf("%-5s\t", new->arrry[m].sex);
printf("%-13s\t", new->arrry[m].tel_num);
printf("%-5s\t", new->arrry[m].aress);
printf("\n");
}
else
{
printf("没有啊\n");
}
}
void new_ad(SL* new, int sz)
{
int i = sz;
new->size++;
for (i = sz; i < new->size; i++)
{
new->arrry[i + 1] = new->arrry[i];
}
SqListInput(new, sz);
}
void SqListPutin(SL* new)
{
judge(new);
int n = 0;
while (!n)
{
printf("请输入你要插入在第几个元素处\n");
scanf("%d", &n);
}
n--;
new_ad(new, n);
printf("插入成功\n");
}
void SqListChange(SL* new)
{
printf("请输入你要更改的人的名字\n");
char ch[NAME_MAX] = { 0 };
scanf("%s", ch);
int num = search_name(ch, new);
menu1();
printf("请输入你所要更改的指标。\n");
int n = 0;
scanf("%d", &n);//因为对搜查项目进行枚举常量定义可以加强程序可读性所以对输入进行判断调用不同输入
switch (n)
{
case NAME:
printf("请输入姓名:>");
scanf("%s", new->arrry[num].name);
break;
case SEX:
printf("请输入性别:>");
scanf("%s", new->arrry[num].sex);
break;
case OLD:
printf("请输入年龄:>");
scanf("%d", &new->arrry[num].old);
break;
case TEL:
printf("请输入手机号:>");
scanf("%s", new->arrry[num].tel_num);
break;
case ARESS:
printf("请输入地址:>");
scanf("%s", new->arrry[num].aress);
break;
default:
printf("哪有呀。");
break;
}
printf("更改成功\n");
}
void SqListDistory(SL* new)
{
free(new->arrry);
new->arrry = NULL;
new->size = 0;
new->sum_size = 0;
printf("通讯录已经销毁\n");
}
void SqList_Save(SL* new)//将结构体的数据写入本地数据
{
int i = 0;
FILE* pf = fopen("ContactList.txt", "w");//只读方式会如果不存在的话即新建文件
if (pf == NULL)//判断是否成功打开
{
perror("SqList_Save");
return;
}
for (i = 0; i < new->size; i++)
{
fwrite(new->arrry + i, sizeof(peo), 1, pf);//因为已知写入数据数所以直接用for循环进行写入
}
fclose(pf);//关闭文件进行置空
pf = NULL;
}
结语
😊如果本文对你用一定帮助,或者是感兴趣的话,请给我一个三连,您的支持对我真的很重要✨
要相信,所有的不美好都是为了迎接美好,所有的困难都会为努力让道。
——简蔓《巧克力色微凉青春》