通讯录是C语言数据结构中的经典项目,今天我们来实现一个简易的通讯录。主要需要实现的功能,就是增加、删除、修改和查找联系人。
为了使项目条理更加清晰,我们将分为多个源文件和头文件进行编写,分别是Contest.c、Contact.c、Contact.h和顺序表的实现部分:Seqlist.h、Seqlist.c。
首先是主函数部分,用一个菜单和Switch来调控整个程序的进行:
Contest.c
#define _CRT_SECURE_NO_WARNINGS
#include"Seqlist.h"
void menu()
{
printf("*****************通讯录******************\n");
printf("******1、添加联系人***2、删除联系人******\n");
printf("******3、修改联系人***4、查找联系人******\n");
printf("******5、查看通讯录***0、退出************\n");
printf("*****************************************\n");
}
int main()
{
int input;
contact con;
InitContact(&con);//通讯录的创建
do {//do--while循环使可以多次操作
menu();
printf("请输入你的选择:");
scanf("%d", &input);
switch (input)
{
case 1:AddContact(&con); break;
case 2:DelContact(&con); break;
case 3:ModifyContact(&con); break;
case 4:FindContact(&con); break;
case 5:ShowContact(&con); break;
case 0:printf("已退出"); break;
default:printf("输入错误!\n"); break;
}
} while (input != 0);
DestroyContact(&con);//销毁通讯录
return 0;
}
由此奠定我们需要分别实现的功能,这里我将顺序表的部分再单独分了出来,实际上也可以在通讯录的三个文件中编写。下一步列出顺序表和通讯录的头文件:
Contact.h
#pragma once
#include<stdio.h>
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
struct Seqlist;
typedef struct SeqList contact;
//用户数据
typedef struct PersonInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//初始化通讯录
void InitContact(contact* con);
//添加通讯录数
void AddContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);
Seqlist.h
#pragma once
#include"Contact.h"
#define INIT_CAPACITY 4
typedef PeoInfo SLDataType;
//这一步定义同样可以是运用顺序表储存其他类型数据
//动态顺序表
typedef struct SeqList
{
SLDataType* a;
int size; // 有效数据个数
int capacity; // 空间容量
}SL;
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLCheckCapacity(SL* ps);
void SLPushBack(SL* ps, SLDataType x);
void SLErase(SL* ps, int pos);
//这里并不包括全部的顺序表基本操作文件,只有通讯录函数中调用到了的
接下来在顺序表的文件中实现相关函数,这些基本可以视为有关数组的简单操作:
Seqlist.c
#include"Seqlist.h"
#include<assert.h>
#include <stdlib.h>
//创建顺序表
void SLInit(SL* ps) {
assert(ps);
ps->a = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
if (ps->a == NULL) {
perror("malloc error!\n");
exit(1);
}
ps->size = 0;
ps->capacity = INIT_CAPACITY;
}
//销毁顺序表
void SLDestroy(SL* ps) {
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps) {//空间大小检查
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//如果空间不够,就需要倍乘capacity
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
void SLPushBack(SL* ps, SLDataType x)
{
//尾插新的数据
assert(ps);
SLCheckCapacity(ps);
ps->a[ps->size++] = x;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i < ps->size; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
最后是通讯录的主要函数的实现:
Contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"Contact.h"
#include"Seqlist.h"
#include<string.h>
void InitContact(contact* con)
//对于创建和之后的销毁函数,都可以直接调用
{
SLInit(con);
}
void AddContact(contact* con)
{
PeoInfo info;
printf("请输入姓名:");
scanf("%s", info.name);
printf("请输入年龄:");
scanf("%d", &info.age);
printf("请输入性别:");
scanf("%s", info.sex);
printf("请输入电话号码:");
scanf("%s", info.tel);
printf("请输入住址:");
scanf("%s", info.addr);
SLPushBack(con, info);
}
int FindByName(contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->a[i].name, name) == 0)
return i;
}
return -1;
}
//对于删除和查找联系人的函数,都需要上面的按姓名查找的函数
void DelContact(contact* con)
{
printf("请输入要删除的联系人姓名:");
char name[NAME_MAX];
scanf("%s", name);
int findIndex = FindByName(con, name);
if (findIndex < 0)
{
printf("未找到!\n");
return;
}
SLErase(con, findIndex);
printf("删除成功!\n");
}
void ShowContact(contact* con)
{
for (int i = 0; i < con->size; i++)
{
printf("联系人%d:\n", i + 1);
printf("姓名:%s\t年龄:%d\t性别:%s\t电话号码:%s\t住址:%s\n",
con->a[i].name,con->a[i].age,con->a[i].sex,con->a[i].tel,con->a[i].addr);
}
//展示通讯录,这里用\t空白制表符可以使数据打印更加美观
}
void FindContact(contact* con)
{
printf("请输入要查找的联系人姓名:");
char name[NAME_MAX];
scanf("%s", name);
int findIndex = FindByName(con, name);
if (findIndex < 0)
{
printf("未找到!\n");
return;
}
printf("姓名:%s\t年龄:%d\t性别:%s\t电话号码:%s\t住址:%s\n",
con->a[findIndex].name, con->a[findIndex].age,
con->a[findIndex].sex, con->a[findIndex].tel, con->a[findIndex].addr);
}
void ModifyContact(contact* con)
{
printf("请输入要修改的联系人姓名:");
char name[NAME_MAX];
scanf("%s", name);
int findIndex = FindByName(con, name);
if (findIndex < 0)
{
printf("未找到!\n");
return;
}
printf("请输入姓名:");
scanf("%s", con->a[findIndex].name);
printf("请输入年龄:");
scanf("%d", &con->a[findIndex].age);
printf("请输入性别:");
scanf("%s", con->a[findIndex].sex);
printf("请输入电话号码:");
scanf("%s", con->a[findIndex].tel);
printf("请输入住址:");
scanf("%s", con->a[findIndex].addr);
printf("修改成功!\n");
}
void DestroyContact(contact* con)
{
SLDestroy(con);
}
实际上这些文件的编写顺序并不是像展示的这样一个顺序下来,需要互相之间来回调整和建立链接,所以文件也是同时完成的。
另外我们还可以用文件操作的一些方法实现联系人数据在电脑中的存储,使通讯录更加完整。主要就是用fopen和fread循环读取历史数据,这里不再详细演示。