文章目錄
通訊錄
通訊錄對于我們來說都不陌生,在我們早些年使用老人機的時候就認識到了,隨著智能手機的普及通訊錄變得花里胡哨和更加好看了。但是功能還是不變的,通訊錄能夠保存人的信息,比如名字、年齡、性別、電話、住址。
那么今天讓我們來模擬實現通訊錄的一些功能吧。
1.菜單與主框架
我們首先要搞個菜單模塊,把我們想要實現的功能打印出來讓我們看到,然后我們再去做選擇。
框架: 我們定義三個文件,一個是頭文件 .h ,一個是.c,一個是test.c
在頭文件.h 中我們把定義信息結構體,聲明函數和引用庫函數的頭文件。
在源文件.c 中實現我們定義的函數功能。
在源文件test.c 中放主函數和搭建主要框架。
所以我們先寫test.c 中的main函數和框架,代碼如下:
#define _CRT_SECURE_NO_WARNINGS 1
//包含我們定義的頭文件
#include "Contact.h"
void menu() //菜單函數
{
printf("**********************************\n");
printf("*** 通訊錄管理系統 ***\n");
printf("**********************************\n");
printf("*** 0.退出通訊錄 ***\n");
printf("*** 1.增加聯系人信息 ***\n");
printf("*** 2.刪除聯系人信息 ***\n");
printf("*** 3.查找聯系人信息 ***\n");
printf("*** 4.修改聯系人信息 ***\n");
printf("*** 5.顯示聯系人信息 ***\n");
printf("*** 6.排序聯系人信息 ***\n");
printf("**********************************\n\n");
}
int main()
{
Contact con;
//因為要改變棧上變量con的內容,所以要傳地址實現
InitContact(&con);//初始化函數
LoadContact(&con);//加載保存聯系人函數
int input = 0;
//do while 循環,實現調用各種函數
do
{
menu();
printf("請輸入你的選擇:");
scanf("%d", &input);
switch (input)
{
case EXIT:
ConserveContact(&con);
DestoryContact(&con);
printf("退出通訊錄\n");
break;
case ADD:
AddContact(&con);
break;
case SUB:
SubContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
break;
case SHOW:
ShowContact(&con);
break;
case SORT:
SortContact(&con);
break;
default:
printf("選擇錯誤,請重新選擇\n");
break;
}
} while (input);
return 0;
}
2.定義信息結構體
當我們把框架寫好了,接下來就是怎么描述一個人的信息呢?要用整形、字符型還是型;這樣都不行,因為一個人的信息有很多,單獨靠一種類型是描述不了的。
所以我們要自定義類型,用到了結構體類型 來描述人的信息;在結構體中我們可以定義數組來保存名字、電話、住址等信息,定義整形來保存年齡等等。
而定義了描述一個人的結構體類型還不行,我們要存放若干個人的信息呀,所以我們還要創建描述一個人結構體的指針,然后再所以內存開辟函數,我們要保存多少人就開辟夠容納的空間。
但是怎么知道開辟的空間不夠了,要增容才行呢?所以我們再定義一個通訊錄結構體類型
,在通訊錄結構體中放描述人的結構體指針 * 和統計人總數與記錄開辟空間大小。
所以頭文件.h 就可以寫出來了,代碼如下:
#pragma once
#include
#include
#include
#include
// 定義枚舉類型,方便辨別case入口
enum p
{
EXIT,
ADD,
SUB,
SEARCH,
MODIFY,
SHOW,
SORT
};
// 名字、電話等數組元素最大個數
#define NAME_MAX 15
#define SEX_MAX 15
#define TELE_MAX 15
#define ADDR_MAX 15
// 把人的信息寫成一個結構體類型
typedef struct people
{
char name[NAME_MAX]; //名字
int age; //年齡
char sex[SEX_MAX]; //性別
char tele[TELE_MAX]; //電話
char addr[ADDR_MAX]; //住址
}people;
struct Contact
{
//創建一個信息結構體指針
struct people* arr;
int sz;
int capacity;
};
typedef struct Contact Contact;
//函數聲明如下
//初始化通訊錄
void InitContact(Contact* pc);
//顯示聯系人信息
void ShowContact(Contact* pc);
//增加聯系人信息
void AddContact(Contact* pc);
//刪除聯系人信息
void SubContact(Contact* pc);
//查找聯系人信息
void SearchContact(Contact* pc);
//修改聯系人信息
void ModifyContact(Contact* pc);
//排序聯系人信息
void SortContact(Contact* pc);
//保存聯系人信息
void ConserveContact(Contact* pc);
//加載聯系人信息
void LoadContact(Contact* pc);
//釋放內存函數
void DestoryContact(Contact* pc);
圖片分析如下:
3.初始化結構體
當我們創建好一個通訊錄變量con ,因為是在棧區上創建的,所以里面存放的是隨機值。那第一步就先進行初始化,把里面的信息結構體指針初始化為空指針,下標和容量都初始化為0。代碼如下:
void InitContact(Contact* pc)
{
pc->arr = NULL;
pc->capacity = 0;
pc->sz = 0;
}
4.增加聯系人信息
我們知道怎么實現描述一個人的信息,接下來就是主要的功能模塊實現了,
注意一點: 在增加聯系人的過程中,我們要注意下標與容量相等時要擴容才行。
代碼如下:
//擴容函數
people* BuyNewCapacity(Contact* pc)
{
//當容量為0時就為2,其它就擴2倍
pc->capacity = pc->capacity > 0 ? 2 * pc->capacity : 2;
people* tmp = (people*)realloc(pc->arr, pc->capacity * sizeof(people));
if (tmp == NULL)
{
printf("內存開辟失敗\n");
exit(-1);
}
//返回擴容的起始地址
return tmp;
}
void AddContact(Contact* pc)
{
if (pc->capacity == pc->sz)
{
people* tmp = BuyNewCapacity(pc);
pc->arr = tmp;
}
printf("請輸入名字:");
scanf("%s", pc->arr[pc->sz].name);
printf("請輸入年齡:");
scanf("%d", &pc->arr[pc->sz].age);
printf("請輸入性別:");
scanf("%s", pc->arr[pc->sz].sex);
printf("請輸入電話:");
scanf("%s", pc->arr[pc->sz].tele);
printf("請輸入地址:");
scanf("%s", pc->arr[pc->sz].addr);
pc->sz++; //增加一個信息,下標也要自增1
printf("添加成功\n");
}
5.刪除聯系人信息
刪除聯系人我們有三種情況c語言簡單通訊錄,第一是正常刪除,第二是通訊錄一個人也沒有就不能刪除了,第三是找不到這個信息也刪除不了。所以我們寫一個找聯系人的函數c語言簡單通訊錄,找到了就返回下標,找不到就返回 -1。
代碼如下:
int FindContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("請輸入名字:");
scanf("%s", name);
int i = 0;
//遍歷一遍
for (i = 0; i < pc->sz; i++)
{
//利用庫函數來判斷字符串是否相等
if (strcmp(name, pc->arr[i].name) == 0)
{
return i;
}
}
return -1;
}
void SubContact(Contact* pc)
{
if (pc->sz == 0)
{
printf("通訊錄為空,不可再刪除\n");
return;
}
// 通訊錄不為空就查找聯系人
int pos = FindContact(pc);
if (pos == -1)
{
printf("你要刪除的信息不存在\n");
return;
}
else
{
//找到了,就一個一個往前面挪
int i = 0;
for (i = pos; i < pc->sz - 1; i++)
{
pc->arr[i] = pc->arr[i + 1];
}
// 要把下標自減1
pc->sz--;
printf("刪除成功\n");
}
}
6.查找聯系人信息
如果通訊錄為空就直接返回不用找了,如果查找函數返回-1就表示沒有此人的信息,如果找到了就把這個人的信息打印出來即可。
代碼如下:
void SearchContact(Contact* pc)
{
if (pc->sz == 0)
{
printf("通訊錄為空,找不到\n");
return;
}
int pos = FindContact(pc);
if (pos == -1)
{
printf("此聯系人不存在\n");
return;
}
else
{
printf("%-15s %-15s %-15s %-15s %-15s\n",
"名字", "年齡", "性別", "電話", "地址");
printf("%-15s %-15d %-15s %-15s %-15s\n",
pc->arr[pos].name,
pc->arr[pos].age,
pc->arr[pos].sex,
pc->arr[pos].tele,
pc->arr[pos].addr);
}
}
7.修改聯系人信息
找到了就重新輸入一遍即可,代碼如下:
void ModifyContact(Contact* pc)
{
int pos = FindContact(pc);
if (pos == -1)
{
printf("此聯系人不存在\n");
return;
}
else
{
printf("請輸入名字:");
scanf("%s", pc->arr[pos].name);
printf("請輸入年齡:");
scanf("%d", &pc->arr[pos].age);
printf("請輸入性別:");
scanf("%s", pc->arr[pos].sex);
printf("請輸入電話:");
scanf("%s", pc->arr[pos].tele);
printf("請輸入地址:");
scanf("%s", pc->arr[pos].addr);
printf("修改成功\n");
}
}
8.顯示聯系人信息
這個也很簡單,顯示就是打印出來即可,代碼如下:
void ShowContact(Contact* pc)
{
int i = 0;
printf("%-15s %-15s %-15s %-15s %-15s\n",
"名字", "年齡", "性別", "電話", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-15s %-15d %-15s %-15s %-15s\n",
pc->arr[i].name,
pc->arr[i].age,
pc->arr[i].sex,
pc->arr[i].tele,
pc->arr[i].addr);
}
printf("\n");
}
9.排序聯系人信息
這個就要用到我們的qsort庫函數了,這個庫函數能排序很多種類型的信息,詳解請看這篇博文:qsort函數的解析 其中通訊錄我們針對的是對名字或者年齡的排序。有興趣的小伙伴也可以增加其他種類的排序,除了年齡外都是對字符串之間的排序。
代碼如下:
int compar1(const void* a, const void* b)
{
return strcmp(((people*)a)->name, ((people*)b)->name);
}
int compar2(const void* a, const void* b)
{
return ((people*)a)->age - ((people*)b)->age;
}
void SortContact(Contact* pc)
{
int input = 0;
do
{
printf("請選擇要排序的類型:\n");
printf("1.名字大小排序 2.年齡大小排序 0.退出排序\n");
scanf("%d", &input);
switch (input)
{
case 1:
qsort(pc->arr, pc->sz, sizeof(people), compar1);
printf("名字大小排序成功\n");
break;
case 2:
qsort(pc->arr, pc->sz, sizeof(people), compar2);
printf("年齡大小排序成功\n");
break;
case 0:
printf("退出排序\n");
break;
default:
printf("選擇錯誤,請重新選擇\n");
break;
}
} while (input);
}
10.保存和加載聯系人信息
當我們輸入聯系人信息結束通訊錄后,如果結束前不保存,那么下一次再執行程序就沒有上一次輸入的信息了,這樣不就是一次性的通訊錄么,這樣的通訊錄誰都不敢用呀。
所以我們要在程序結束前保存信息到文件里面,然后重新執行程序在初始化階段把文件里面信息加載到內存當中。文件使用可以參考這一篇博文:C語言中的文件操作
代碼如下:
//保存聯系人信息
void ConserveContact(Contact* pc)
{
//以二進制寫的方式打開文件
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("erron ");
return;
}
int i = 0;
//把信息保存到文件當中
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->arr + i, sizeof(people), 1, pf);
}
fclose(pf);
pf = NULL;
}
//加載聯系人信息
void LoadContact(Contact* pc)
{
FILE* pf = fopen("Contact.txt", "rb");
if (pf == NULL)
{
perror("erron ");
exit(-1);
}
// 因為初始化還沒有開辟空間
// 先開辟一點大小的空間
if (pc->capacity == pc->sz)
{
people* tmp = BuyNewCapacity(pc);
pc->arr = tmp;
}
// 從文件中把信息加載到變量當中
while (fread(pc->arr + pc->sz, sizeof(people), 1, pf))
{
pc->sz++;
//如果空間滿了就增容
if (pc->capacity == pc->sz)
{
people* tmp = BuyNewCapacity(pc);
pc->arr = tmp;
}
}
fclose(pf);
pf = NULL;
}
11.釋放內存空間
當我們結束通訊錄之前除了要保存聯系人的信息外,還要把在堆上開辟的內存空間還給操作系統。代碼如下:
void DestoryContact(Contact* pc)
{
free(pc->arr);
pc->arr = NULL;
pc->capacity = 0;
pc->sz = 0;
}
程序執行的部分結果:
以上就是我的通訊錄全部內容了,其中前面我只有把頭文件.h 和源文件test.c 放在同一段代碼塊中,實現函數功能的源文件.c 文件我拆開來分析了。如果想要方便拷貝.c 文件的內容,闊以移步到gitee上獲取。
三個源文件內容鏈接: