今天开设了新专栏✨
在这个专栏中,我会写一些小项目,类似于学校里要求做的课程设计,请大家支持啊!
好的,废话不多说,我们开始正文✨
用C语言编写一个通讯录程序,要求能够实现对联系人信息的增删查改、排序、清空,将联系人信息保存到文件中,下一次使用时可以读取文件中信息。
要实现通讯录的各种功能,就必须要定义一系列与之相关的函数,在这个项目中,我用到了13个函数,下面我们来一一介绍
首先我们来定义两个结构体
typedef struct PeoInfo
{
//创建联系人信息结构体
char name[NAME_MAX];
char tele[TEL_MAX];
char addr[ADDR_MAX];
char sex[SEX_MAX];
int age;
}PeoInfo;
第一个结构体包含了联系人的基本信息(名字,电话,地址,性别,年龄)
对于char类型数组的大小,我提前将其定义为宏,这样做的好处是以后改变数组大小时,只需要改变对应的宏即可
上面的最后两个宏分别表示通讯录初始容量和每次需要增容时的增量
为了节省内存,通讯录每次达到已满的状态时,程序就会对其进行增容,每次增加两个联系人信息大小的空间
第二个结构体包含了第一个结构体变量的位置,通讯录容量和通讯录中已存的联系人个数
typedef struct
{
//将联系人信息结构体地址和通讯录容量、当前联系人数量存入结构体
PeoInfo* data;
int capacity;
int size;
}contact;
众所周知,对结构体传参时,传址要比传整个结构体好得多,因为不用另外开辟一个很大的空间,这里将第一个结构体的位置存入第二个结构体中也有这个作用
第二个结构体中capacity和size分别表示通讯录容量和已存联系人个数
好的,下面就开始介绍函数了!!!
函数一:init_contact(对通讯录进行初始化)
当刚启动程序的时候,我们需要对通讯录进行初始化,在这个过程中,会为通讯录申请三个联系人信息大小的内存,并将已存联系人个数设定为0
void init_contact(contact* p)
{
assert(p);//断言,p不为空指针
p->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CAPACITY);//为通讯录开辟三个结构体大小的空间
if (p == NULL)
{
printf("初始化失败\n");
return;
}
p->size = 0;
p->capacity = CAPACITY;
}
函数二:check_contact(检查通讯录是否已满,若满,则进行增容)
int check_contact(contact* p)//检查通讯录是否已满,如果满了就进行增容
{
assert(p);
if (p->size == p->capacity)
{
printf("通讯录已满,进行增容\n");
//每次新增两个联系人信息大小的空间
PeoInfo* tmp = (contact*)realloc(p->data, (p->size + ZENG) * sizeof(PeoInfo));
if (tmp == NULL)
{
printf("增容失败\n");
return 0;
}
p->data = tmp;
p->capacity += ZENG;
return 1;
}
}
记得增容后要对capacity+ZENG
函数三:locate(对要进行查找、删除、修改的联系人进行定位,找到其在通讯录中的位置)
int locate(char* name,contact* p)//找到需要进行操作的联系人在结构体数组中的位置
{
assert(p);
int i = 0;
for (i = 0; i < p->size; i++)
{
if ((strcmp(name, p->data[i].name)) == 0)
{
return i;
}
}
if (i == p->size)
{
return 0;
}
}
上面的代码对通讯录信息进行遍历,直到找到联系人为止,找到就返回该联系人在结构体数组中的下标,找不到就返回0,将这个过程写成一个独立的函数是为了方便其他函数的调用,一次封装,多次调用
函数四:add_contact(增加联系人信息)
void add_contact(contact* p)//添加联系人
{
assert(p);
if (check_contact(p) == 0)
{
//判断通讯录已满的条件下是否增容失败
printf("通讯录已满,增容失败\n");
return;
}
printf("请输入姓名:>");
scanf("%s", p->data[p->size].name);
printf("请输入年龄:>");
scanf("%d", &p->data[p->size].age);
printf("请输入性别:>");
scanf("%s", p->data[p->size].sex);
printf("请输入电话:>");
scanf("%s", p->data[p->size].tele);
printf("请输入地址:>");
scanf("%s", p->data[p->size].addr);
printf("添加成功\n");
p->size++;
}
要增加联系人,首先就要判断通讯录是否已满,若已满就进行增容,最后千万不要忘记要把size++
void dele_contact(contact* p)//删除联系人
{
assert(p);
char name[NAME_MAX];
printf("请输入要删除人的名字:>");
scanf("%s", name);
int pos = locate(name, p);
if (pos == 0)
{
printf("该联系人不存在!\n");
return;
}
else
{
//将要删除的联系人后面的联系人位置依次向前挪动,覆盖该联系人
for (int i = pos; i < p->size; i++)
{
p->data[i] = p->data[i + 1];
}
printf("删除联系人成功!\n");
p->size--;
}
}
函数六:search_contact(查找联系人)
void search_contact(contact* p)//查找联系人
{
assert(p);
char name[NAME_MAX];
printf("请输入要查找人的名字:>");
scanf("%s", name);
int pos = locate(name, p);
if (pos == 0)
{
printf("该联系人不存在!\n");
return;
}
else
{
printf("%10s %10s %10s %20s %20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%10s %10d %10s %20s %20s\n", p->data[pos].name, p->data[pos].age,
p->data[pos].sex, p->data[pos].tele, p->data[pos].addr);
}
}
函数七:multify_contact(修改指定联系人信息)
void multify_contact(contact* p)//修改联系人信息
{
assert(p);
char name[NAME_MAX];
printf("请输入要修改人的名字:>");
scanf("%s", name);
int pos = locate(name, p);
if (pos == 0)
{
printf("该联系人不存在!\n");
return;
}
else
{
printf("请输入修改后的姓名:>");
scanf("%s", p->data[pos].name);
printf("请输入修改后的年龄:>");
scanf("%d", &(p->data[pos].age));
printf("请输入修改后的性别:>");
scanf("%s", p->data[pos].sex);
printf("请输入修改后的电话:>");
scanf("%s", p->data[pos].tele);
printf("请输入修改后的地址:>");
scanf("%s", p->data[pos].addr);
printf("修改成功!\n");
}
}
运行效果:
函数八:show_contact(显示通讯录所有联系人信息)
void show_contact(contact* p)//显示通讯录信息
{
assert(p);
printf("%10s %10s %10s %20s %20s\n", "姓名", "年龄", "性别", "电话", "地址");
for (int i = 0; i < p->size; i++)
{
printf("%10s %10d %10s %20s %20s\n", p->data[i].name, p->data[i].age,
p->data[i].sex, p->data[i].tele, p->data[i].addr);
}
}
运行效果在前面函数的运行效果中已经展示过了,这里就不再单独展示了!
函数九:sort_contact(对联系人进行排序)
void sort_contact(contact* p)
{
//将联系人按名字首字母进行排序
assert(p);
int flag = 1;
PeoInfo temp = { 0 };
for (int i = p->size; i > 0 && flag; i--)
{
flag = 0;
for (int j = 1; j < p->size; j++)
{
if (strcmp(p->data[j].name, p->data[j - 1].name) < 0)
{
temp = p->data[j];
p->data[j] = p->data[j - 1];
p->data[j - 1] = temp;
flag = 1;
}
}
}
printf("排序成功!\n");
}
注意,所谓的排序是指按名字首字母的先后位置进行排序,小伙伴们也可以稍加改动达到自己想要的排序效果哦
这几行英语讲的就是strcmp返回值代表的含义,在上面的代码中,返回值小于0,就是指两者名字首字母顺序和通讯录所在位置顺序相反
运行效果:
函数十:save_contact(保存通讯录信息到文件里)
void save_contact(contact* p)//将输入的信息保存到文件中
{
assert(p);
FILE* pf = fopen("contact.dat", "w");
if (pf == NULL)
{
printf("保存失败!\n");
return;
}
else
{
for (int i = 0; i < p->size; i++)
{
//将联系人信息依次写入文件
fwrite(&(p->data[i]), sizeof(PeoInfo), 1, pf);
}
printf("保存成功!\n");
fclose(pf);
pf = NULL;
}
}
运行程序时,在本项目的同一路径下会出现一个contact.dat文件,里面就保存着已经输入的联系人信息
函数十一:load_contact(讲文件中保存的信息加载出来,方便下次使用)
void load_contact(contact* p)//将文件中信息加载出来
{
assert(p);
FILE* pf = fopen("contact.dat", "r");
if (pf == NULL)
{
printf("加载失败!\n");
return;
}
else
{
while (fread(&(p->data[p->size]), sizeof(PeoInfo), 1, pf))
{
if (check_contact(p))
{
p->size++;
}
}
printf("加载成功!\n");
fclose(pf);
pf = NULL;
}
}
运行效果:
函数十二:empty_contact(清空通讯录所有内容)
void empty_contact(contact* p)//清空通讯录
{
assert(p);
if (p->size == 0)
{
printf("通讯录为空!\n");
return;
}
else
{
free(p->data);
p->data = NULL;
p->capacity = 0;
p->size = 0;
printf("清空成功!\n");
}
}
void end_contact(contact* p)//退出程序
{
//释放使用过的空间
assert(p);
free(p->data);
p->data = NULL;
p->capacity = 0;
p->size = 0;
exit(0);
}
由于0直接退出程序,所有就不用展示运行效果了
一个项目一般用到三个文件----一个.h文件用于进行函数的声明和宏的定义以及头文件的涵盖
一个.c文件用于对各个函数进行实现
另一个.c文件用于测试前两个文件所含代码
contact.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#define NAME_MAX 20
#define SEX_MAX 4
#define TEL_MAX 14
#define ADDR_MAX 30
#define CAPACITY 3
#define ZENG 2
typedef struct PeoInfo
{
//创建联系人信息结构体
char name[NAME_MAX];
char tele[TEL_MAX];
char addr[ADDR_MAX];
char sex[SEX_MAX];
int age;
}PeoInfo;
typedef struct
{
//将联系人信息结构体地址和通讯录容量、当前联系人数量存入结构体
PeoInfo* data;
int capacity;
int size;
}contact;
void add_contact(contact* p);
int check_contact(contact* p);
void dele_contact(contact* p);
void search_contact(contact* p);
int locate(char name, contact* p);
void multify_contact(contact* p);
void show_contact(contact* p);
void end_contact(contact* p);
void load_contact(contact* p);
void save_contact(contact* p);
void empty_contact(contact* p);
void sort_contact(contact* p);
void init_contact(contact* p);
contact.c
#define _CRT_SECURE_NO_WARNINGS
#include "contact.h"
void init_contact(contact* p)
{
assert(p);//断言,p不为空指针
p->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CAPACITY);//为通讯录开辟三个结构体大小的空间
if (p == NULL)
{
printf("初始化失败\n");
return;
}
p->size = 0;
p->capacity = CAPACITY;
}
void add_contact(contact* p)//添加联系人
{
assert(p);
if (check_contact(p) == 0)
{
//判断通讯录已满的条件下是否增容失败
printf("通讯录已满,增容失败\n");
return;
}
printf("请输入姓名:>");
scanf("%s", p->data[p->size].name);
printf("请输入年龄:>");
scanf("%d", &p->data[p->size].age);
printf("请输入性别:>");
scanf("%s", p->data[p->size].sex);
printf("请输入电话:>");
scanf("%s", p->data[p->size].tele);
printf("请输入地址:>");
scanf("%s", p->data[p->size].addr);
printf("添加成功\n");
p->size++;
}
int check_contact(contact* p)//检查通讯录是否已满,如果满了就进行增容
{
assert(p);
if (p->size == p->capacity)
{
printf("通讯录已满,进行增容\n");
//每次新增两个联系人信息大小的空间
PeoInfo* tmp = (contact*)realloc(p->data, (p->size + ZENG) * sizeof(PeoInfo));
if (tmp == NULL)
{
printf("增容失败\n");
return 0;
}
p->data = tmp;
p->capacity += ZENG;
return 1;
}
}
void dele_contact(contact* p)//删除联系人
{
assert(p);
char name[NAME_MAX];
printf("请输入要删除人的名字:>");
scanf("%s", name);
int pos = locate(name, p);
if (pos == -1)
{
printf("该联系人不存在!\n");
return;
}
else
{
//将要删除的联系人后面的联系人位置依次向前挪动,覆盖该联系人
for (int i = pos; i < p->size; i++)
{
p->data[i] = p->data[i + 1];
}
printf("删除联系人成功!\n");
p->size--;
}
}
void search_contact(contact* p)//查找联系人
{
assert(p);
char name[NAME_MAX];
printf("请输入要查找人的名字:>");
scanf("%s", name);
int pos = locate(name, p);
if (pos == -1)
{
printf("该联系人不存在!\n");
return;
}
else
{
printf("%10s %10s %10s %20s %20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%10s %10d %10s %20s %20s\n", p->data[pos].name, p->data[pos].age,
p->data[pos].sex, p->data[pos].tele, p->data[pos].addr);
}
}
int locate(char* name,contact* p)//找到需要进行操作的联系人在结构体数组中的位置
{
assert(p);
int i = 0;
for (i = 0; i < p->size; i++)
{
if ((strcmp(name, p->data[i].name)) == 0)
{
return i;
}
}
if (i == p->size)
{
return -1;
}
}
void multify_contact(contact* p)//修改联系人信息
{
assert(p);
char name[NAME_MAX];
printf("请输入要修改人的名字:>");
scanf("%s", name);
int pos = locate(name, p);
if (pos == -1)
{
printf("该联系人不存在!\n");
return;
}
else
{
printf("请输入修改后的姓名:>");
scanf("%s", p->data[pos].name);
printf("请输入修改后的年龄:>");
scanf("%d", &(p->data[pos].age));
printf("请输入修改后的性别:>");
scanf("%s", p->data[pos].sex);
printf("请输入修改后的电话:>");
scanf("%s", p->data[pos].tele);
printf("请输入修改后的地址:>");
scanf("%s", p->data[pos].addr);
printf("修改成功!\n");
}
}
void show_contact(contact* p)//显示通讯录信息
{
assert(p);
printf("%10s %10s %10s %20s %20s\n", "姓名", "年龄", "性别", "电话", "地址");
for (int i = 0; i < p->size; i++)
{
printf("%10s %10d %10s %20s %20s\n", p->data[i].name, p->data[i].age,
p->data[i].sex, p->data[i].tele, p->data[i].addr);
}
}
void end_contact(contact* p)//退出程序
{
//释放使用过的空间
assert(p);
free(p->data);
p->data = NULL;
p->capacity = 0;
p->size = 0;
exit(0);
}
void save_contact(contact* p)//将输入的信息保存到文件中
{
assert(p);
FILE* pf = fopen("contact.dat", "w");
if (pf == NULL)
{
printf("保存失败!\n");
return;
}
else
{
for (int i = 0; i < p->size; i++)
{
//将联系人信息依次写入文件
fwrite(&(p->data[i]), sizeof(PeoInfo), 1, pf);
}
printf("保存成功!\n");
fclose(pf);
pf = NULL;
}
}
void load_contact(contact* p)//将文件中信息加载出来
{
assert(p);
FILE* pf = fopen("contact.dat", "r");
if (pf == NULL)
{
printf("加载失败!\n");
return;
}
else
{
while (fread(&(p->data[p->size]), sizeof(PeoInfo), 1, pf))
{
if (check_contact(p))
{
p->size++;
}
}
printf("加载成功!\n");
fclose(pf);
pf = NULL;
}
}
void empty_contact(contact* p)//清空通讯录
{
assert(p);
if (p->size == 0)
{
printf("通讯录为空!\n");
return;
}
else
{
free(p->data);
p->data = NULL;
p->capacity = 0;
p->size = 0;
printf("清空成功!\n");
}
}
void sort_contact(contact* p)
{
//将联系人按名字首字母进行排序
assert(p);
int flag = 1;
PeoInfo temp = { 0 };
for (int i = p->size; i > 0 && flag; i--)
{
flag = 0;
for (int j = 1; j < p->size; j++)
{
if (strcmp(p->data[j].name, p->data[j - 1].name) < 0)
{
temp = p->data[j];
p->data[j] = p->data[j - 1];
p->data[j - 1] = temp;
flag = 1;
}
}
}
printf("排序成功!\n");
}
test.c
#include "contact.h"
void menu()
{
printf("***********************************\n");
printf("****** 0.退出 1.添加 ******\n");
printf("****** 2.删除 3.查找 ******\n");
printf("****** 4.修改 5.显示 ******\n");
printf("****** 6.排序 7.清空 ******\n");
printf("****** 8.加载 9.保存 ******\n");
printf("***********************************\n");
}
void Work()
{
int choice;
contact con;
void(*p[10])(contact* p) =
{
end_contact,
add_contact,
dele_contact,
search_contact,
multify_contact,
show_contact,
sort_contact,
empty_contact,
load_contact,
save_contact
};//定义函数指针数组,减少代码冗余
init_contact(&con);
do
{
menu();
printf("请选择操作:>");
scanf("%d", &choice);
if (choice >= 0 && choice <= 9)
{
//根据输入的数字调用对应的函数
(*p[choice])(&con);
}
else
{
printf("选择错误!\n");
return;
}
} while (choice>=0);
}
int main()
{
Work();
return 0;
}
在最后一个文件中用到了函数指针数组
其实是将每一个函数的地址当做一个元素,通过指针找到它,进行相应操作
第一次写关于小项目的博客,如有错误,请大家不吝赐教,我积极改正
上面所有代码用到了动态内存管理,文件操作,指针等重点内容,希望对大家有帮助!!!
好的,我们下次再见