C语言小项目之“究极无敌螺旋丸极爆炸狂拽炫酷五彩棒的”通讯录之派小猩作品
项目概述
基于C语言开发一个多功能的通讯录
功能目录
1.增加
2.删除
3.查找
4.修改
5.显示
6.排序
7.保存
8.清空
设计思路
废话不多说直接开整代码
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Contact.h"//引用自己创建的头文件要用双引号
void menu()
{
printf("++ 0.Exit ++\n");
printf("++ 1.Add 2.Del ++\n");
printf("++ 3.Search 4.Modify ++\n");
printf("++ 5.Show 6.Sort ++\n");
printf("++ 7.Save 8.Empty ++\n");
printf("++ ++\n");
}
int main()
{
Contact con;//创建通讯录
InitContact(&con);//初始化(动态)
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case Add:
AddContact(&con);
break;
case Del:
DelContact(&con);
break;
case Modify:
ModifyContact(&con);
break;
case Search:
SearchContact(&con);
break;
case Show:
ShowContact(&con);
break;
case Sort:
SortContact(&con);
break;
case Save:
SaveContact(&con);
break;
case Empty:
EmptyContact(&con);
break;
case Exit:
SaveContact(&con);//退出的时候先保存再释放动态内存
FreeContact(&con);
printf("退出通讯录\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}
Contact.c
#define _CRT_SECURE_NO_WARNINGS
#include "Contact.h"
static void CheckCapacity(Contact* ps);
void LoadContact(Contact* ps)
{
assert(ps);
PeoInfo tmp = { 0 };
FILE* pfRead = fopen("Contact.dat", "rb");//以二进制方式打开读取
if (!pfRead)//如果NULL
{
printf("LoadContact::%s", strerror(errno));
return;
}
//读取文件,加载到通讯录种
//fread(读取的放到哪个地址,读的元素大小每个,个数,从哪读)返回的时读取到的实际个数
while (fread(&tmp, sizeof(PeoInfo), 1, pfRead))//返回0代表全部读完了
{
//先判断当前容量够不够存
CheckCapacity(ps);
//再写入
ps->data[ps->size] = tmp;
ps->size++;//计数
}
fclose(pfRead);
pfRead = NULL;
}
void InitContact(Contact* ps)
{
assert(ps);
//初始化
//刚开始默认存放3个人的信息
ps->data =(PeoInfo*) malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (!ps->data)//->和.操作符优先级高先结合!!!
{
return;//开辟失败
}
//初始化计数
ps->size = 0;
ps->capacity = DEFAULT_SZ;
//将本地文件存放的信息加载到通讯录中
LoadContact(ps);
}
void ShowContact(const Contact* ps)
{
assert(ps);
//先判断能不能显示出来(空or不空)
if (0 == ps->size)
{
printf("通讯录内容为空\n");
}
else
{
//显示标题 内容
//标题都是字符串不能%d
int i = 0;
printf("%-20s\t%-5s\t%-4s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "住址");
for (i = 0; i < ps->size; i++)//遍历一个一个打印(左对齐-)
{
//要与标题栏对齐
printf("%-20s\t%-5s\t%-4d\t%-12s\t%-30s\n",
ps->data[i].Name,
ps->data[i].Sex,
ps->data[i].Age,
ps->data[i].Tel,
ps->data[i].Address);
}
}
}
static void CheckCapacity(Contact* ps)
{
assert(ps);
//如果满了->增容
if (ps->capacity == ps->size)
{
PeoInfo* ptr=realloc(ps->data, (ps->capacity + 2) * sizeof(PeoInfo));//先交给临时的ptr再去判断是否为空指针不为空才能赋给data
if (ptr)
{
ps->data = ptr;
ps->capacity += 2;//每次加完都要计数
printf("扩容成功\n");
}
else
{
printf("增容失败\n");
}
}
//没满就出去增加数据
}
void DelContact(Contact* ps);//引用声明
void AddContact(Contact* ps)
{
assert(ps);
//1.检查容量判断是否超出容量
int input = 0;
int i = 0;
CheckCapacity(ps);
printf("请输入要添加加联系人的个数:>");
scanf("%d", &input);
if (input < 0)
{
DelContact(ps);
}
//增加数据
else if(input>0)
{
for (i = 0; i < input; i++)
{
printf("请输入姓名:>");
scanf("%s", ps->data[ps->size].Name);
printf("请输入性别:>");
scanf("%s", ps->data[ps->size].Sex);
printf("请输入年龄:>");
scanf("%d", &(ps->data[ps->size].Age));
printf("请输入电话:>");
scanf("%s", ps->data[ps->size].Tel);
printf("请输入住址:>");
scanf("%s", ps->data[ps->size].Address);
ps->size++;
printf("添加成功!\n");
CheckCapacity(ps);
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
//FindByName只能在这函数定义里使用用Static修饰
static int FindByName(const Contact* ps, const char name[Name_max])
{
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (0 == strcmp(name, ps->data[i].Name))
{
return i;
}
}
return -1;
}
void DelContact(Contact* ps)
{
assert(ps);
//输入要删除的信息的人的名字
char name[Name_max];
printf("请输入你所要删除的人的姓名:>");
scanf("%s", &name);
//再查找此人的信息(遍历)
int pos = FindByName(ps, name);
if (pos == ps->size)
{
printf("要删除的人不存在");
}
//再删除(把要删的数据后面的数据往前面覆盖)
else
{
int j = 0;
for (j = pos; j < ps->size - 1; j++)
{
ps->data[j] = ps->data[j + 1];
}
//计数
ps->size--;
printf("删除成功\n");
}
}
void ModifyContact(Contact* ps)
{
assert(ps);
char name[Name_max];
int i = 0;
printf("请输入要修改的人的名字:>");
scanf("%s", &name);
//遍历查找(出现了2次要封装成函数)
int pos=FindByName(ps, name);//找到就返回下标
if (-1==pos)
{
printf("要修改的人不存在\n");
}
else
{
//找到了先显示给用户看show
printf("%-20s\t%-5s\t%-4s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-20s\t%-5s\t%-4d\t%-12s\t%-30s\n",
ps->data[pos].Name,
ps->data[pos].Sex,
ps->data[pos].Age,
ps->data[pos].Tel,
ps->data[pos].Address);
//修改
printf("请输入姓名:>");
scanf("%s", ps->data[ps->size].Name);
printf("请输入性别:>");
scanf("%s", ps->data[ps->size].Sex);
printf("请输入年龄:>");
scanf("%d", &(ps->data[ps->size].Age));
printf("请输入电话:>");
scanf("%s", ps->data[ps->size].Tel);
printf("请输入住址:>");
scanf("%s", ps->data[ps->size].Address);
printf("修改成功!\n");
}
}
void SearchContact(const Contact* ps)
{
assert(ps);
char name[Name_max];
printf("请输入要查找人的姓名:>");
scanf("%s", & name);
int pos=FindByName(ps, name);//找到返回下标,否则返回-1
if (-1 == pos)//先排除不存在的情况剩下的情况就是找到了
{
printf("要查找的人不存在\n");
}
else
{
printf("%-20s\t%-5s\t%-4s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "住址");
printf("%-20s\t%-5s\t%-4d\t%-12s\t%-30s\n",
ps->data[pos].Name,
ps->data[pos].Sex,
ps->data[pos].Age,
ps->data[pos].Tel,
ps->data[pos].Address);
}
}
void SortContact(Contact* ps)
{
//按升序排列
assert(ps);
int i = 0;//趟数
int j = 0;//每一趟在do what?!
for (i = 0; i <ps->size-1; i++)
{
for (j = 0; j < ps->size - 1-i; j++)
{
int ret = strcmp(ps->data[j].Name, ps->data[j + 1].Name);
if (ret>0)
{
//交换
//strcpy(ps->data[j + 1].Name, ps->data[j].Name);//字符交换
//交换结构体要用结构体变量且类型必须是要交换的结构体的类型
PeoInfo tmp = ps->data[j];
ps->data[j] = ps->data[j+1];
ps->data[j + 1] = tmp;
}
}
}
printf("排序成功\n");
}
void SaveContact(Contact* ps)
{
FILE* pfWrite = fopen("Contact.dat", "wb");//以二进制形式写入本地文件
//判断是否打开成功
if (!pfWrite)
{
printf("SaveContact::%s", strerror(errno));//前面加上函数名和其他的报错作出区分
return;//失败就出去
}
//打开成功开始写入(遍历)一个一个写进去
else
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
fwrite(&(ps->data[i]),sizeof(PeoInfo),1,pfWrite);
//写入的源头地址,data[i]下标为i的元素,每个元素的大小,一次写入的个数,写到哪个流
}
printf("保存成功!\n");
}
fclose(pfWrite);//打开之后就关闭文件
pfWrite = NULL;
}
static void Destroy(Contact* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
PeoInfo tmp = { 0 };
ps->data[i] = tmp;//把空的人物信息赋给原数据就是全部清空
}
ps->size = 0;
ps->capacity = 0;
printf("删除成功!\n");
return;
}
void EmptyContact(Contact* ps)
{
int input = 0;
printf("请确定是否要删除通讯录的所有信息\n");
printf(" 1.Ensure 0.Cancel \n ");
scanf("%d", &input);
switch (input)
{
case 1:
Destroy(ps);
break;
case 0:
return;
}
}
void FreeContact(Contact* ps)
{
assert(ps);
free(ps->data);
ps->data = NULL;
}
Contact.h 头文件
#pragma once
#define Name_max 5
#define Sex_max 5
//#define Age_max 4
#define Tel_max 12
#define Address_max 50
#define DEFAULT_SZ 3
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<errno.h>
//声明类型
typedef struct PeoInfo
{
//姓名 性别 年龄 电话号码 住址
char Name[Name_max];
char Sex[Sex_max];
char Tel[Tel_max];
char Address[Address_max];
int Age;
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;//结构体指针指向动态开辟的空间
int size;//记录当前已有信息数量
int capacity;//当前最大容量 size==capacity时要进行动态扩容
}Contact;
enum Options//枚举选项可能的取值
{
Exit,//0
Add,//1
Del,//2
Modify,//3
Search,//4
Show,//5
Sort,//6
Save,//7
Empty//8
};
//初始化
void InitContact(Contact* ps);
//增加信息
void AddContact(Contact* ps);
//删除信息
void DelContact(Contact* ps);
//修改信息
void ModifyContact(Contact* ps);
//查找信息
void SearchContact(const Contact* ps);
//排序
void SortContact(Contact* ps);
//展示信息
void ShowContact(const Contact* ps);
//保存通讯录
void SaveContact(Contact* ps);
//清空通讯录信息
void EmptyContact(Contact* ps);
//释放动态内存
void FreeContact(Contact* ps);//free作动词有释放的意思
//加载通讯录信息
void LoadContact(Contact* ps);
总结
这次的小项目在实现过程中我也遇到了一些问题还有待改善,同时也注意到了很多细节,下面我将一 一总结出来
1.比如查找特定联系人时如果有重名情况该怎么处理
2.结构体内让占用内存小的变量聚集在一起,更节省空间
3.结构体指针类型的活用
3.动态开辟的内存记得释放
4.打开文件后最后记得关闭及赋成空指针
5.分配动态内存及文件操作都要进行判断减少Debug次数
5.能不能采用MySQL数据库提高项目的安全性
6.……
好了,这次的小项目分享到这里,这是我第一次写博客,如果有任何问题欢迎大佬指正,我也特别希望大家能够喜欢我创作的内容!!
by_派小猩