C語言代碼實(shí)現(xiàn)通訊錄管理系統(tǒng)
本文實(shí)例為大家分享了C語言實(shí)現(xiàn)通訊錄管理系統(tǒng),供大家參考,具體內(nèi)容如下
一、需求分析
運(yùn)用C語言實(shí)現(xiàn)一個簡單的通訊錄管理系統(tǒng),要求對數(shù)據(jù)有 增刪改查清排顯 等功能的實(shí)現(xiàn)(這里由于還沒學(xué)到文件,所以下面所有的存儲都是在內(nèi)存中,也就是當(dāng)程序結(jié)束的時候添加的信息都會清空掉 - 后續(xù)會加以改進(jìn)的)
二、程序結(jié)構(gòu)
在基本的程序設(shè)計中,都會將項目分為三個文件:
1、test.c - 主函數(shù)(負(fù)責(zé)調(diào)用各個功能的接口)
2、contact.c - 實(shí)現(xiàn)各個模塊的功能
3、contact.h - 用于存放頭文件、宏定義、函數(shù)聲明等
三、頭文件內(nèi)容的介紹
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#define MAX 1000
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 50
#define TELE_MAX 12
#define DEFAULT_SZ 3
// 數(shù)組版本
//typedef struct PeoInfo
//{
//?? ?char name[NAME_MAX];
//?? ?int age;
//?? ?char sex[SEX_MAX];
//?? ?char addr[ADDR_MAX];
//?? ?char tele[TELE_MAX];
//}PeoInfo;
//
//typedef struct Contact
//{
//?? ?//PeoInfo data[MAX]; ?// 存放信息
//?? ?PeoInfo data[MAX];
//?? ?int sz; // 存放通訊錄有幾個人的信息
//}Contact;
// 每個人的基本信息
typedef struct PeoInfo
{
?? ?char name[NAME_MAX];
?? ?int age;
?? ?char sex[SEX_MAX];
?? ?char addr[ADDR_MAX];
?? ?char tele[TELE_MAX];
}PeoInfo;
typedef struct Contact
{
?? ?//PeoInfo data[MAX]; ?// 存放信息
?? ?PeoInfo* data;
?? ?int sz; // 存放通訊錄有幾個人的信息
?? ?int capacity; // 記錄當(dāng)前通訊錄的最大容量
}Contact;
void DestoryContact(Contact* pc);
void InitContact(Contact* pc);
void AddContact(Contact* pc);
void ShowContact(const Contact* pc);
void DeleteContact(Contact* pc);
void SearchContact(const Contact* pc);
void ModifyContact(Contact* pc);
void SortContact(Contact* pc);里面被注釋的代碼則是之前的數(shù)組版本,規(guī)定死了通訊錄大??;而下面我們會用動態(tài)增容來進(jìn)行實(shí)現(xiàn)。頭文件里面用宏定義主要為了之后我們更改對應(yīng)的大小的時候更加的方便;結(jié)構(gòu)體主要存放的就是三大部分:
1. 一個指向每個人基本信息的結(jié)構(gòu)體指針;2. sz記錄著通訊錄的記錄條數(shù);3. capacity 存放通訊錄的容量
四、模塊化實(shí)現(xiàn)各個功能
(1)主函數(shù)實(shí)現(xiàn)
int main()
{
?? ?int input = 0;
?? ?Contact con; // 創(chuàng)建通訊錄
?? ?InitContact(&con);
?? ?do
?? ?{
?? ??? ?menu();
?? ??? ?printf("請選擇:->");
?? ??? ?scanf("%d", &input);
?? ??? ?switch (input)
?? ??? ?{
?? ??? ?case ADD:
?? ??? ??? ?AddContact(&con);
?? ??? ??? ?break;
?? ??? ?case DEL:
?? ??? ??? ?DeleteContact(&con);
?? ??? ??? ?break;
?? ??? ?case SEARCH:
?? ??? ??? ?SearchContact(&con);
?? ??? ??? ?break;
?? ??? ?case MODIFY:
?? ??? ??? ?ModifyContact(&con);
?? ??? ??? ?break;
?? ??? ?case SORT:
?? ??? ??? ?SortContact(&con);
?? ??? ??? ?break;
?? ??? ?case SHOW:
?? ??? ??? ?ShowContact(&con);
?? ??? ??? ?break;
?? ??? ?case EXIT:
?? ??? ??? ?DestoryContact(&con);
?? ??? ??? ?printf("退出\n");
?? ??? ??? ?break;
?? ??? ?default:
?? ??? ??? ?printf("輸入錯誤,請重新輸入\n");
?? ??? ??? ?break;
?? ??? ?}
?? ?} while (input);
?? ?return 0;
}一般函數(shù)的主函數(shù)里面都可以用do ……while結(jié)構(gòu)來控制程序的執(zhí)行順序,在case后面用的則是 枚舉常量 ,這樣能做到更好的見名知意。
(2)初始化通訊錄
void InitContact(Contact* pc)
{
?? ?assert(pc);
?? ?pc->sz = 0;
?? ?PeoInfo* tmp = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
?? ?if (tmp != NULL)
?? ?{
?? ??? ?pc->data = tmp;
?? ?}
?? ?else
?? ?{
?? ??? ?printf("InitContact()::%s\n", strerror(errno));
?? ?}
?? ?pc->capacity = DEFAULT_SZ;
}這里設(shè)定了通訊錄的起始大小,后面可以通過動態(tài)增容來擴(kuò)大(而數(shù)組版本的話就沒有這么靈活了)
(3)添加聯(lián)系人信息
void check_capacity(Contact* pc)
{
?? ?assert(pc);
?? ?if (pc->sz == pc->capacity)
?? ?{
?? ??? ?// 增容
?? ??? ?PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2)*sizeof(PeoInfo));
?? ??? ?if (tmp != NULL)
?? ??? ?{
?? ??? ??? ?pc->data = tmp;
?? ??? ??? ?pc->capacity += 2;
?? ??? ??? ?printf("增容成功\n");
?? ??? ?}
?? ??? ?else
?? ??? ?{
?? ??? ??? ?printf("InitContact()::%s\n", strerror(errno));
?? ??? ?}
?? ?}
}
void AddContact(Contact* pc)
{
?? ?assert(pc);
?? ?check_capacity(pc);
?? ?// 添加內(nèi)容
?? ?printf("名字:");
?? ?scanf("%s", pc->data[pc->sz].name);
?? ?printf("年齡:");
?? ?scanf("%d", &(pc->data[pc->sz].age));
?? ?printf("性別:");
?? ?scanf("%s", pc->data[pc->sz].sex);
?? ?printf("地址:");
?? ?scanf("%s", pc->data[pc->sz].addr);
?? ?printf("電話:");
?? ?scanf("%s", pc->data[pc->sz].tele);
?? ?
?? ?pc->sz++;
?? ?printf("添加成功\n");
}這里實(shí)現(xiàn)了一個check_capacity 的接口,用來判斷當(dāng)前通訊錄是否存滿了;若滿了則進(jìn)行擴(kuò)容(一般按2倍進(jìn)行擴(kuò)容),注意:添加成功后需要對 sz++
(4)刪除聯(lián)系人信息
int FindByName(const Contact* pc, char* name)
{
?? ?int i = 0;
?? ?for (i = 0; i < pc->sz; i++)
?? ?{
?? ??? ?if (strcmp(pc->data[i].name, name) == 0)
?? ??? ?{
?? ??? ??? ?return i;
?? ??? ?}
?? ?}
?? ?return -1;
}
void DeleteContact(Contact* pc)
{
?? ?char name[NAME_MAX] = { 0 };
?? ?assert(pc);
?? ?if (pc->sz == 0)
?? ?{
?? ??? ?printf("通訊錄為空\n");
?? ??? ?return;
?? ?}
?? ?// 根據(jù)輸入的人名進(jìn)行刪除
?? ?printf("請輸入要刪除的人名:");
?? ?scanf("%s", name);
?? ?int pos = FindByName(pc, name);
?? ?if (pos == -1)
?? ?{
?? ??? ?printf("要刪除的人不存在\n");
?? ?}
?? ?else
?? ?{
?? ??? ?// 刪除
?? ??? ?//int j = 0;
?? ??? ?//for (j = pos; j < pc->sz-1; j++) ?// sz-1為了防止j+1非法訪問
?? ??? ?//{
?? ??? ?//?? ?pc->data[j] = pc->data[j + 1];
?? ??? ?//}
?? ??? ?//pc->sz--;
?? ??? ?//printf("刪除成功\n");
?? ??? ?//if (pc->sz>1)
?? ??? ?memmove((pc->data) + pos, (pc->data) + pos + 1, (pc->sz - pos)*sizeof(PeoInfo));
?? ??? ?pc->sz--;
?? ?}
}這里我就只做了按照名字進(jìn)行刪除,你們也可以都嘗試一下喲。下面寫了兩種刪除方法(這里的按名字刪除前需要進(jìn)行查找,因此我們也可以直接使用我們下面要實(shí)現(xiàn)的查找函數(shù),但如果查找里面進(jìn)行了其他功能,那時不能直接使用的喲)
(5)查找聯(lián)系人信息
void SearchContact(const Contact* pc)
{
?? ?char name[NAME_MAX] = { 0 };
?? ?assert(pc);
?? ?if (pc->sz == 0)
?? ?{
?? ??? ?printf("通訊錄為空\n");
?? ??? ?return;
?? ?}
?? ?printf("請輸入要查找的姓名:");
?? ?scanf("%s", name);
?? ?int i = FindByName(pc, name);
?? ?if(-1 == i)
?? ?{
?? ??? ?// 沒有找到
?? ??? ?printf("你要查找的人不存在\n");
?? ?}
?? ?else
?? ?{
?? ??? ?// 找到了
?? ??? ?printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n",?
?? ??? ??? ??? ?"姓名", "年齡", "性別", "地址", "電話");
?? ??? ?printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
?? ??? ??? ?pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
?? ?}
}這里如果查找到了后,我打印顯示了一下
(6)更改聯(lián)系人信息
void ModifyContact(Contact* pc)
{
?? ?char name[NAME_MAX] = { 0 };
?? ?assert(pc);
?? ?if (pc->sz == 0)
?? ?{
?? ??? ?printf("通訊錄為空\n");
?? ??? ?return;
?? ?}
?? ?printf("請輸入要修改信息的姓名:");
?? ?scanf("%s", name);
?? ?int i = FindByName(pc, name);
?? ?// 修改內(nèi)容
?? ?printf("新的名字:");
?? ?scanf("%s", pc->data[i].name);
?? ?printf("新的年齡:");
?? ?scanf("%d", &(pc->data[i].age));
?? ?printf("新的性別:");
?? ?scanf("%s", pc->data[i].sex);
?? ?printf("新的地址:");
?? ?scanf("%s", pc->data[i].addr);
?? ?printf("新的電話:");
?? ?scanf("%s", pc->data[i].tele);
?? ?printf("修改后的信息為:\n");
?? ?printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", "姓名", "年齡", "性別", "地址", "電話");
?? ?printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
?? ??? ?pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
}(7)顯示所有聯(lián)系人信息
void ShowContact(const Contact* pc)
{
?? ?assert(pc);
?? ?// 打印標(biāo)題
?? ?printf("%-10s\t%-5s\t%-5s\t%-20s\t%-13s\n", "姓名", "年齡", "性別", "地址", "電話");
?? ?int i = 0;
?? ?for (i = 0; i < pc->sz; i++)
?? ?{
?? ??? ?printf("%-10s\t%-5d\t%-5s\t%-20s\t%-13s\n",
?? ??? ??? ?pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele);
?? ?}
}直接遍歷打印即可
(8)對聯(lián)系人信息進(jìn)行排序
int cmp_by_name(const void* e1, const void* e2)
{
?? ?return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_by_age(const void* e1, const void* e2)
{
?? ?return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
?? ?// return strcmp(, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
?? ?int choice = 0;
?? ?printf("請選擇:(1、按姓名;2、按年齡)");
?? ?scanf("%d", &choice);
?? ?if (1 == choice)
?? ?{
?? ??? ?qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);
?? ??? ?printf("排序后的數(shù)據(jù):\n");
?? ??? ?ShowContact(pc);
?? ?}
?? ?else if (2 == choice)
?? ?{
?? ??? ?qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_age);
?? ??? ?printf("排序后的數(shù)據(jù):\n");
?? ??? ?ShowContact(pc);
?? ?}
?? ?else
?? ?{
?? ??? ?printf("輸入有誤\n");
?? ?}
}這里使用qsort實(shí)現(xiàn)的排序,可以對 姓名 或 年齡進(jìn)行排序的
(9)退出時銷毀通訊錄
void DestoryContact(Contact* pc)
{
?? ?assert(pc);
?? ?free(pc->data);
?? ?pc->data = NULL;
}直接將里面指向有效內(nèi)容的 pc->data 進(jìn)行free釋放就行,因為它本來就是動態(tài)開辟的,后面要有個好習(xí)慣,free之后將其置為NULL,不然就為野指針了。
總結(jié):
以上就是動態(tài)增容版本通訊錄的全部代碼,盡管用了動態(tài)增容,里面仍然會存在很多的空間浪費(fèi),后面等博主學(xué)了鏈表后,在進(jìn)行改進(jìn)。
希望這篇文章對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C++ Strassen算法代碼的實(shí)現(xiàn)
這篇文章主要介紹了C++ Strassen算法代碼的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
ubuntu20.04中vscode使用ROS的詳細(xì)方法
這篇文章主要介紹了ubuntu20.04?vscode使用ROS的詳細(xì)方法,主要包括在vscode安裝擴(kuò)展創(chuàng)建工作文件夾的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10
C++實(shí)現(xiàn)圖片轉(zhuǎn)base64的示例代碼
Base64就是一種 基于64個可打印字符來表示二進(jìn)制數(shù)據(jù)的表示方法,本文主要為大家詳細(xì)介紹了如何使用C++實(shí)現(xiàn)圖片轉(zhuǎn)base64,需要的可以參考下2024-04-04
C語言數(shù)據(jù)結(jié)構(gòu)單鏈表接口函數(shù)全面講解教程
這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)單鏈表所有接口函數(shù)的全面講解教程,有需要朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-10-10

