文章目錄
- 1 基礎要求
- 2 通訊錄功能
- 2.1 引入單鏈表的文件
- 2.2 定義聯系人數據結構
- 2.3 打開通訊錄
- 2.4 保存數據后銷毀通訊錄
- 2.5 添加聯系人
- 2.6 刪除聯系人
- 2.7 修改聯系人
- 2.8 查找聯系人
- 2.9 查看通訊錄
- 3 通訊錄代碼展示
- 3.1 SeqList_copy.h
- 3.2 SeqList_copy.c
- 3.3 Contact.h
- 3.4 Contact.c
- 3.5 test.c
- 3.6 調試控制臺截圖
1 基礎要求
單鏈表,文件操作,結構體,二級指針
思路:利用上一篇博客-單鏈表,對單鏈表簡易修改,應用到通訊錄.
2 通訊錄功能
通訊錄只能能存100個人
用戶信息:名字,性別,年齡,電話,地址
通訊錄的展示,增,刪,改,查,找.
聯系人信息存儲
2.1 引入單鏈表的文件
為了顯現區別,筆者備份單鏈表的文件并且更名為SeqList_copy.h
和SeqList_copy.c
創建Contact.h
和Contact.c
以及測試代碼功能的test.c
test.c
和Contact.c
引用的頭文件都是Contact.h
和SeqList_copy.h
2.2 定義聯系人數據結構
在Contact.h
頭文件中
定義名字最大長度,性別最大長度,電話最大長度,地址最大長度
#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 打開通訊錄
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 保存數據后銷毀通訊錄
銷毀通訊錄之前一定要保存數據,不然2.3打開有存儲聯系人的通訊錄的作用就沒有了
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 添加聯系人
使用單鏈表一節中尾插法添加數據
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 刪除聯系人
借助FindByName()函數,找到索引.然而缺陷明顯,找到的索引是第一次出現的輸入的姓名,同樣,只能通過姓名查找
//我們這里通過姓名刪除聯系人
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 修改聯系人
注意
age
年齡是int
整型,其他四個數據結構都是數組,用scanf
時,這里的數組相當于首元素地址,不用&
.
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 查找聯系人
同樣根據姓名查看詳細聯系人的通訊錄信息
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 查看通訊錄
第一次自己寫的時候,
cur=cur->next
放在while
循環外面了.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 通訊錄代碼展示
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 50typedef 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()
我封裝了五個函數,都是用來測試通訊錄功能的,調試結束后,就將函數封裝在一起,寫一個菜單完成通訊錄功能.
#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 調試控制臺截圖
這里忘記啥問題了,一轉眼就忘了
在多次語法錯誤,死循環,輸出亂碼,代碼崩潰后,終于簡單完成通訊錄的功能!