Article directory
1 Basic requirements
Single linked list, file operation, structure, secondary pointer
Idea: Use the previous blog - singly linked list to simply modify the singly linked list and apply it to the address book.
2 Address book function
The address book can only store 100 people.
User information: name, gender, age, phone number, address. Address
book display, add, delete, modify, search, search.
Contact information storage
2.1 Files that introduce singly linked lists
In order to show the difference, the author backed up the file of the singly linked list and renamed it to and. SeqList_copy.h
The header files SeqList_copy.c
that create Contact.h
and Contact.c
and test code functions test.c
test.c
and Contact.c
reference are all Contact.h
andSeqList_copy.h
2.2 Define contact data structure
In Contact.h
the header file,
define the maximum length of the name, the maximum length of the gender, the maximum length of the phone number, and the maximum length of the address.
#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50
//前置声明
typedef struct SListNode contact;
//定义结构体
typedef struct ContactInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}ConInfo;
2.3 Open the address book
void ContactInit(contact** pcon)
{
FILE* pf = fopen("Contact_Info.txt", "rb"); //二进制方式打开文件
if (pf == NULL)
{
perror("fopen error.\n"); //主动报错,打开失败
return;
}
ConInfo info;
//回顾一下fread
//size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
//从流中读取一个由count元素组成的数组,每个元素的大小为size字节,并将它们存储在ptr指定的内存块中。
//流的位置指示器按读取的总字节数前进。
//如果成功读取的总字节数为(size * count)
while (fread(&info, sizeof(info), 1, pf))
{
SLPushBack(pcon, info);
}
printf("历史数据已导入通讯录\n");
}
2.4 Destroy the address book after saving the data
Be sure to save the data before destroying the address book, otherwise the function of opening the address book with stored contacts in 2.3 will be lost.
void ContactSave(contact* con)
{
FILE* pf = fopen("Contact_Info.txt", "wb");
if (pf == NULL)
{
perror("fopen error\n");
return;
}
contact* cur = con;
while (cur)
{
fwrite(&(cur->data), sizeof(cur->data), 1, pf);
cur = cur->next;
}
printf("成功保存通讯录数据\n");
}
void ContactDestory(contact** pcon)
{
ContactSave(*pcon);
SLDestory(pcon);
}
2.5 Add contacts
Add data using tail interpolation in a singly linked list
void ContactAdd(contact** pcon)
{
ConInfo info;
printf("请输入添加联系人姓名:\n");
scanf("%s", &info.name);
printf("请输入添加联系人性别:\n");
scanf("%s", &info.sex);
printf("请输入添加联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入添加联系人电话:\n");
scanf("%s", &info.tel);
printf("请输入添加联系人地址:\n");
scanf("%s", &info.addr);
SLPushBack(pcon, info);
printf("已添加联系人数据\n");
}
2.6 Delete contacts
With the help of the FindByName() function, the index is found. However, the flaw is obvious. The index found is the entered name that appears for the first time. Similarly, it can only be searched by name.
//我们这里通过姓名删除联系人
contact* FindByName(contact* con, char name[])
{
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name,name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void ContactDel(contact** pcon)
{
char name[NAME_MAX];
printf("请输入你要删除的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(*pcon, name);
if (pos == NULL)
{
printf("该联系人不存在\n");
return;
}
SLErase(pcon, pos);
printf("已删除该联系人\n");
}
2.7 Modify contacts
Note that
age
age isint
an integer, and the other four data structures are all arrays.scanf
When used, the array here is equivalent to the address of the first element, so no need to use it&
.
void ContactModify(contact* con)
{
char name[NAME_MAX];
printf("请输入你要修改的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(con, name);
if (pos == NULL)
{
printf("该联系人不存在\n");
return;
}
printf("该联系人姓名修改为:\n");
scanf("%s", pos->data.name);
printf("该联系人性别修改为:\n");
scanf("%s", pos->data.sex);
printf("该联系人年龄修改为:\n");
scanf("%d", &pos->data.age);
printf("该联系人电话修改为:\n");
scanf("%s", pos->data.tel);
printf("该联系人地址修改为:\n");
scanf("%s", pos->data.addr);
printf("修改成功\n");
}
2.8 Find contacts
Also view the address book information of detailed contacts based on name
void ContactFind(contact* con)
{
char name[NAME_MAX];
printf("请输入你要查找的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(con, name);
if (pos == NULL)
{
printf("该联系人不存在\n");
return;
}
printf("找到了\n");
printf("%s %s %d %s %s\n",
pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr
);
}
2.9 View address book
The first time I wrote it myself,
cur=cur->next
I placedwhile
it outside the loop. QAQ
void ContactShow(contact* con)
{
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
contact* cur = con;
while (cur)
{
printf("%s %s %d %s %s\n",
cur->data.name,
cur->data.sex,
cur->data.age,
cur->data.tel,
cur->data.addr);
cur = cur->next;
}
}
3 Address book code display
3.1 SeqList_copy.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"Contact.h"
//定义链表的结构
//typedef int SLDataType
typedef struct ContactInfo SLDataType; //更改SLDataType的类型
typedef struct SListNode
{
SLDataType data; //保存的数据
struct SListNode* next; //指针变量存放下一个节点的地址
}SLNode;
//1 打印链表
void SLPrint(SLNode* phead);
//2 尾插
void SLPushBack(SLNode** pphead, SLDataType x);
//3 头插
void SLPushFront(SLNode** pphead, SLDataType x);
//4 尾删
void SLPopBack(SLNode** pphead);
//5 头删
void SLPopFront(SLNode** pphead);
//SLFind()找到要查找数据的下标
//找节点的函数这里传一级实际上就可以了,因为不改变头结点
//但是这里要写二级指针,因为要保持接口一致性
//缺点:这个函数有一定的局限性,他找到数据x的下标是第一次出现的x的下标后,不会找后续的x的下标
SLNode* SLFind(SLNode** pphead, SLDataType x);
//6 定点前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x);
//7 定点后插入数据
void SLInsertAfter(SLNode* pos, SLDataType x);
//8 删除pos节点
void SLErase(SLNode** pphead, SLNode* pos);
//9 删除pos后的节点
void SLEraseAfter(SLNode* pos);
//10 销毁链表
void SLDestory(SLNode** pphead);
3.2 SeqList_copy.c
#include"SList_copy.h"
//1 创建新的节点的函数
SLNode* SLBuyNode(SLDataType x)
{
SLNode* node = (SLNode*)malloc(sizeof(SLNode));
node->data = x;
node->next = NULL;
return node;
}
//2 尾插
void SLPushBack(SLNode** pphead, SLDataType x) //保存第一个节点的指针的地址
{
assert(pphead);
//创建一个节点
SLNode* node = SLBuyNode(x);
if (*pphead == NULL)
{
*pphead = node;
return;
}
//链表不为空,找尾
SLNode* pcur = *pphead;
while (pcur->next)//相当于pcur->next!=NULL
{
pcur = pcur->next;
}
pcur->next = node;
}
//3 头插
void SLPushFront(SLNode** pphead, SLDataType x)
{
assert(pphead);
SLNode* node = SLBuyNode(x);
//新结点跟头结点连接起来
node->next = *pphead;
//让新节点成为头结点
*pphead = node;
}
//4 尾删
void SLPopBack(SLNode** pphead)
{
assert(pphead);
assert(*pphead); //第一个节点不能为空
//判断只有一个结点的情况
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
return;
}
//找到尾节点和尾节点的前一个节点
SLNode* pre = NULL;
SLNode* ptail = *pphead;
while (ptail->next)
{
pre = ptail;
ptail = ptail->next;
}
//pre的next指针不指向ptail,而是指向ptail的下一个节点
//pre->next = NULL;
pre->next = ptail->next;
//因为在SLPrint(SLNode* phead)函数中有判断节点是否为空while (pcur),所以置空
free(ptail);
ptail = NULL;
}
//5 头插
void SLPopFront(SLNode** pphead)
{
assert(pphead);
assert(*pphead); //第一个节点不能为空
SLNode* del = *pphead;
*pphead = (*pphead)->next;
free(del);
del = NULL;
}
//6 定点前插入数据
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead);
assert(pos); //pos不能为空
assert(*pphead); //NULL前插入数据是荒谬的,所以assert第一个节点
SLNode* node = SLBuyNode(x);
//当只有一个结点(pos就是第一个节点)
//if (((*pphead)->next) == NULL||pos==*pphead),可以简化成以下代码
if (pos == *pphead) {
node->next = *pphead;
*pphead = node;
return;
}
//找到pos的前一个节点
SLNode* pre = *pphead;
while (pre->next != pos)
{
pre = pre->next;
}
//处理pre node pos的位置
node->next = pos;
pre->next = node;
}
//7 定点后插入数据
void SLInsertAfter(SLNode* pos, SLDataType x)
{
assert(pos);
SLNode* node = SLBuyNode(x);
node->next = pos->next;
pos->next = node;
}
//8 删除pos节点
void SLErase(SLNode** pphead, SLNode* pos)
{
assert(*pphead);
assert(pphead);
assert(pos);
//pos是头结点
if (pos == *pphead)
{
*pphead = (*pphead)->next;
free(pos);
return;
}
//pos不是头结点,找pos的前一个节点
SLNode* pre = *pphead;
while (pre->next != pos)
{
pre = pre->next;
}
pre->next = pos->next;
free(pos);
pos = NULL; //代码规范
}
//9 删除pos后的节点
void SLEraseAfter(SLNode* pos)
{
//删除pos之后的节点也不能空
assert(pos && pos->next);
SLNode* del = pos->next;
pos->next = del->next;
free(del);
}
void SLDestory(SLNode** pphead)
{
assert(pphead);
SLNode* pcur = *pphead;
while (pcur)
{
SLNode* next = pcur->next;
free(pcur);
pcur = next;
}
//头结点记得置空
*pphead = NULL;
}
3.3 Contact.h
#pragma once
//创建保存联系人数据的结构
#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50
typedef struct ContactInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}ConInfo;
//前置声明
typedef struct SListNode contact;
//1.打开通讯录
void ContactInit(contact** pcon);
//2.保存数据后销毁通讯录
void ContactDestory(contact** pcon);
//3.添加联系人
void ContactAdd(contact** pcon);
//4.删除联系人
void ContactDel(contact** pcon);
//5.修改联系人
void ContactModify(contact* con);
//6.查找联系人
void ContactFind(contact* pcon);
//7.查看通讯录
void ContactShow(contact* pcon);
3.4 Contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"Contact.h"
#include"SList_copy.h"
//1.打开通讯录
void ContactInit(contact** pcon)
{
FILE* pf = fopen("Contact_Info.txt", "rb");
if (pf == NULL)
{
perror("fopen error.\n"); //主动报错,打开失败
return;
}
ConInfo info;
//回顾一下fread
//size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
//从流中读取一个由count元素组成的数组,每个元素的大小为size字节,并将它们存储在ptr指定的内存块中。
//流的位置指示器按读取的总字节数前进。
//如果成功读取的总字节数为(size * count)
while (fread(&info, sizeof(info), 1, pf))
{
SLPushBack(pcon, info);
}
printf("历史数据已导入通讯录\n");
}
void ContactSave(contact* con)
{
FILE* pf = fopen("Contact_Info.txt", "wb");
if (pf == NULL)
{
perror("fopen error\n");
return;
}
contact* cur = con;
while (cur)
{
fwrite(&(cur->data), sizeof(cur->data), 1, pf);
cur = cur->next;
}
printf("成功保存通讯录数据\n");
}
void ContactDestory(contact** pcon)
{
ContactSave(*pcon);
SLDestory(pcon);
}
void ContactAdd(contact** pcon)
{
ConInfo info;
printf("请输入添加联系人姓名:\n");
scanf("%s", &info.name);
printf("请输入添加联系人性别:\n");
scanf("%s", &info.sex);
printf("请输入添加联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入添加联系人电话:\n");
scanf("%s", &info.tel);
printf("请输入添加联系人地址:\n");
scanf("%s", &info.addr);
SLPushBack(pcon, info);
printf("已添加联系人数据\n");
}
//我们这里通过姓名删除联系人
contact* FindByName(contact* con, char name[])
{
contact* cur = con;
while (cur)
{
if (strcmp(cur->data.name,name) == 0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void ContactDel(contact** pcon)
{
char name[NAME_MAX];
printf("请输入你要删除的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(*pcon, name);
if (pos == NULL)
{
printf("该联系人不存在\n");
return;
}
SLErase(pcon, pos);
printf("已删除该联系人\n");
}
void ContactModify(contact* con)
{
char name[NAME_MAX];
printf("请输入你要修改的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(con, name);
if (pos == NULL)
{
printf("该联系人不存在\n");
return 1;
}
printf("该联系人姓名修改为:\n");
scanf("%s", pos->data.name);
printf("该联系人性别修改为:\n");
scanf("%s", pos->data.sex);
printf("该联系人年龄修改为:\n");
scanf("%d", &pos->data.age);
printf("该联系人电话修改为:\n");
scanf("%s", pos->data.tel);
printf("该联系人地址修改为:\n");
scanf("%s", pos->data.addr);
printf("修改成功\n");
}
void ContactFind(contact* con)
{
char name[NAME_MAX];
printf("请输入你要查找的联系人姓名:\n");
scanf("%s", name);
contact* pos = FindByName(con, name);
if (pos == NULL)
{
printf("该联系人不存在\n");
return;
}
printf("找到了\n");
printf("%s %s %d %s %s\n",
pos->data.name,
pos->data.sex,
pos->data.age,
pos->data.tel,
pos->data.addr
);
}
void ContactShow(contact* con)
{
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
contact* cur = con;
while (cur)
{
printf("%s %s %d %s %s\n",
cur->data.name,
cur->data.sex,
cur->data.age,
cur->data.tel,
cur->data.addr);
cur = cur->next;
}
}
3.5 test.c
test()
I encapsulated five functions, all of which are used to test the address book function. After debugging, I encapsulated the functions together and wrote a menu to complete the address book function.
#define _CRT_SECURE_NO_WARNINGS
#include"Contact.h"
#include"SList_copy.h"
void test1()
{
contact *pcontact=NULL;
ContactInit(&pcontact);
}
void test2()
{
contact* pcontact = NULL;
ContactInit(&pcontact);
ContactAdd(&pcontact);
ContactDel(&pcontact);
ContactShow(pcontact);
}
void test3()
{
contact* pcontact = NULL;
ContactInit(&pcontact);
ContactShow(pcontact);
ContactAdd(&pcontact);
ContactShow(pcontact);
ContactDel(&pcontact);
ContactShow(pcontact);
}
void test4()
{
contact* pcontact = NULL;
ContactInit(&pcontact);
ContactAdd(&pcontact);
ContactAdd(&pcontact);
ContactShow(pcontact);
ContactFind(pcontact);
ContactShow(pcontact);
}
void test5()
{
contact* pcontact = NULL;
ContactInit(&pcontact);
ContactAdd(&pcontact);
ContactShow(pcontact);
ContactModify(pcontact);
ContactShow(pcontact);
ContactDestory(&pcontact);
}
void menu()
{
printf("\n");
printf("****************************\n");
printf("****************************\n");
printf("************通讯录***********\n");
printf("*********1.添加联系人********\n");
printf("*********2.删除联系人********\n");
printf("*********3.修改联系人********\n");
printf("*********4.查找联系人********\n");
printf("*********5.查看通讯录********\n");
printf("*********0.退 出************\n");
printf("****************************\n");
printf("****************************\n");
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
int op = 0;
contact* con=NULL;
ContactInit(&con);
do {
menu();
printf("请选择:\n");
scanf("%d", &op);
switch (op)
{
case 1:
ContactAdd(&con);
break;
case 2:
ContactDel(&con);
break;
case 3:
ContactModify(con);
break;
case 4:
ContactFind(con);
break;
case 5:
ContactShow(con);
break;
case 0:
printf("退出了哈\n");
break;
default:
printf("输入错误\n");
break;
}
} while (op!=0);
ContactDestory(&con);
return 0;
}
3.6 Debugging console screenshot
What problem did I forget here? I forgot about it in the blink of an eye.
After many syntax errors, endless loops, garbled output, and code crashes, I finally completed the function of the address book simply!