前言:
上篇主要是用顺序表实现通讯录,本篇主要是用单链表实现通讯录。
分析:
单链表在这里是指单向不带头不循环链表。
如下图所示,通讯录是一个结构体,里面存有数据和下一个结点的地址。数据在这里是联系人信息的结构体。(当然也可以不建两个结构体,只建一个通讯录结构体)
联系人信息 有名字、性别、年龄、电话和地址。姓名性别年龄地址都是数组,因为它们可能用到汉字,汉字是宽字符,一个汉字占两个字节。而char类型的变量只占一个字节。这是不够的。年龄一个整型就足够了,不必要给数组。
数组的长度可以用宏定义,比较方便,也防止在后面再次用的时候记错数字。
建立三个文件,slcontact.h,slcontact.c,test.c。分别用来(主要是)声明函数,定义函数和测试功能等。
slcontact.h:
1.创建联系人结构体和通讯录结构体
2.声明通讯录想要实现的五个功能,封装成五个函数。
3.声明菜单。
4.声明初始化和销毁通讯录。
说明:给函数传的参数,形参用一级指针还是二级指针接收?这个取决于是否要改变当前所传的指针的地址。如果要改变当前指针的地址,就用二级指针接收,如果不需要改变,就可以用一级指针接收。
有时为了保持接口一致性,可以全部都用二级指针(接口中存在二级指针)。
头文件中声明的函数,是测试文件中需要调用的函数。如果测试文件中不需要调用,但在函数实现中需要用到,可以写在函数定义的slcontact.c文件(亦即可以不声明)。比如下面的Addlistback函数。
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#pragma once //防止头文件被重复引用
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
#include
#include
#include
#include
//创建联系人结构体
typedef struct PersonInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}PeoInfo;//把这个结构体重命名为PeoInfo
//创建一个通讯录,这个通讯录是一个链表
typedef struct contact
{
//这个链表包含联系人数据和下一个结点的数据
PeoInfo info;//联系人数据是一个结构体
struct contact* next;//下一个结点的数据是一个通讯录指针,指针名字是next,储存的是下一个联系人的数据。
}contact;//把这个通讯录结构体重命名为contact
//写下待会要完成的内容,进行声明,然后在slcontact.c文件里实现,在test.c文件里测试
//声明一下菜单
void Menu();
//创建新结点
contact* BuyNode();
//初始化通讯录
void InitContact(contact** con);
//尾插的函数
void Addlistback(contact** con, contact* newnode);
//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);
slcontact.c:
实现这些通讯录函数有一些细节需要注意。
包含头文件。
添加联系人时,需要一个创建新结点的函数以及一个尾插的函数。
删除联系人时,需要查找联系人(也可以封装一个函数),找到以后再删除,删除完了再展示一下当前通讯录(这一点可不写)
销毁通讯录时,要写一个循环,逐个释放每一个结点的内存。
实现打印的内容时,格式可以自己调。
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include "slcontact.h"
void Menu()
{
printf("\n");
printf("**************通讯录***************\n");
printf("****1.添加联系人****2.删除联系人***\n");
printf("****3.查看联系人****4.展示通讯录***\n");
printf("****5.修改联系人****0.退出通讯录***\n");
printf("***********************************\n");
printf("\n\n");
}
//创建新结点
contact* BuyNode()
{
contact* newnode = (contact*)malloc(sizeof(contact));
if (newnode == NULL)
{
assert("malloc failed");
exit(1);
}
newnode->next = NULL;
strcpy(newnode->info.name, " ");
return newnode;
}
//初始化通讯录
void InitContact(contact** con)//头指针是*con
{
assert(con);
*con = NULL;
}
//添加通讯录数据之前要有一个尾插的函数
//尾插
void Addlistback(contact** con,contact* newnode)
{
assert(con);
contact* pcur = *con;//创建一个新的指针指向头结点
if (pcur == NULL)
{
*con = newnode;
}
else
{
while (pcur->next != NULL)//找尾结点
{
pcur = pcur->next;
}
pcur->next = newnode;//找到以后把这个新结点放在尾结点的下一个结点,实现尾插
}
}
//添加联系人
void AddContact(contact** con)
{
assert(con);
contact* newnode = BuyNode();//创建联系人结构体,把数据存在临时的联系人中
printf("请输入您要添加的联系人姓名:\n");
scanf("%s", newnode->info.name);//数组名就是首元素地址,name是数组名
printf("请输入您要添加的联系人性别:\n");
scanf("%s", newnode->info.sex);
printf("请输入您要添加的联系人年龄:\n");
scanf("%d", &newnode->info.age);//这里要取地址,因为age不是数组
printf("请输入您要添加的联系人电话:\n");
scanf("%s", newnode->info.tel);
printf("请输入您要添加的联系人地址:\n");
scanf("%s", newnode->info.addr);
Addlistback(con,newnode);//把头指针的指针和临时信息传过去
printf("联系人添加成功!\n");
ShowContact(*con);
}
//删除通讯录数据之前要查找联系人
void DelContact(contact** con)
{
assert(con);
assert(*con);
printf("请输入您要删除的联系人:\n");
char name[NAME_MAX];
scanf("%s",name);
contact* del = *con;
contact* prev = NULL;
while (del != NULL && strcmp(del->info.name, name) != 0)//这个指针有数,且不是我要删的数
{
/* del = del->next;
prev = del;(顺序不能乱!!!!!)*/
prev = del;
del = del->next;
}
if (del == NULL)//跳出循环后,del还是空,说明不存在
{
printf("您要删除的联系人不存在!\n");
return;//(这里不存在以后,要及时return,不能再继续后面的操作了,不然会出错!!!!!
}
else if (prev == NULL)//del!=NULL,但是第一次就没进入循环因为第一个数就是del
{
*con = (*con)->next;
}
else//del!=NULL,del是除第一个数之外的其他数
{
prev->next = del->next;
}
free(del);
del = NULL;
printf("联系人删除成功!\n");
ShowContact(*con);
}
//展示通讯录数据
void ShowContact(contact* con)
{
if (con == NULL)
{
return;
}
printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址" );
contact* pcur = con;
while (pcur)
{
printf("%-4s ", pcur->info.name);
printf("%-4s ", pcur->info.sex);
printf("%-4d ", pcur->info.age);
printf("%-4s ", pcur->info.tel);
printf("%-4s ", pcur->info.addr);
printf("\n");
pcur = pcur->next;
}
}
//查找通讯录数据
void FindContact(contact* con)
{
assert(con);
char name[NAME_MAX];
printf("请输入您要查找的联系人姓名:\n");
scanf("%s", name);
contact* pcur = con;
while (pcur != NULL && strcmp(pcur->info.name, name) != 0)
{
pcur = pcur->next;
}
if (pcur == NULL)
{
printf("您要查找的联系人不存在!\n");
return;
}
else
{
printf("%-4s %-4s %-4s %-4s %-4s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-4s ", pcur->info.name);
printf("%-4s ", pcur->info.sex);
printf("%-4d ", pcur->info.age);
printf("%-4s ", pcur->info.tel);
printf("%-4s ", pcur->info.addr);
printf("\n");
}
}
void ModifyContact(contact** con)
{
assert(con);
assert(*con);
printf("请输入您要修改的联系人姓名:\n");
char name[NAME_MAX];
scanf("%s", name);
contact* pcur = *con;
while (pcur != NULL && strcmp(pcur->info.name, name) != 0)
{
pcur = pcur->next;
}
if (pcur == NULL)
{
printf("您要修改的联系人不存在!\n");
return;
}
else
{
printf("请输入修改后的联系人姓名:\n");
scanf("%s", pcur->info.name);
printf("请输入修改后的联系人性别:\n");
scanf("%s", pcur->info.sex);
printf("请输入修改后的联系人年龄:\n");
scanf("%d", &pcur->info.age);
printf("请输入修改后的联系人电话:\n");
scanf("%s", pcur->info.tel);
printf("请输入修改后的联系人地址:\n");
scanf("%s", pcur->info.addr);
printf("\n");
printf("联系人修改成功!\n");
}
}
void DestroyContact(contact** con)
{
contact* prev = NULL;
contact* pcur = *con;
while (pcur!= NULL)
{
prev = pcur;
pcur = pcur->next;
free(prev);
prev = NULL;
}
free(prev);
prev = NULL;
}
test.c:
测试文件相对来说代码比较少。
需要注意,顺序表是直接创建的结构体,这里是创建一个结构体指针。
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:6031)
#include "slcontact.h"
int main()
{
contact* s1;//这里要用指针
InitContact(&s1);//初始化通讯录
int num = 0;
do
{
Menu();
printf("请选择您要进行的操作:\n");
scanf("%d", &num);
switch (num)
{
case 0: DestroyContact(&s1);
break;
case 1: AddContact(&s1);
break;
case 2: DelContact(&s1);
break;
case 3: FindContact(s1);
break;
case 4: ShowContact(s1);
break;
case 5: ModifyContact(&s1);
break;
default: printf("输入错误,请重新输入\n");
break;
}
} while (num);
return 0;
}
这样,一个通讯录就写完了。这里没有添加文件操作,感兴趣可以自行扩展。