目錄
前言
一、整體邏輯分析
二、實現步驟
1、創建菜單和多次操作問題
2、創建通訊錄
3、初始化通訊錄
4、添加聯系人
5、顯示聯系人
6、刪除指定聯系人
?7、查找指定聯系人
8、修改聯系人信息
9、排序聯系人信息
三、全部源碼
前言
我們上期已經詳細的介紹了自定義類型,本期我們來用學過的知識來做個小項目來練習練習。本期我們將用C語言實現通訊錄靜態版!后期介紹了動態內存管理和文件操作我們將會在此版本的基礎上進行優化改進!
一、整體邏輯分析
我們還和以前實現三子棋和掃雷小項目一樣,先對整體工程的邏輯盡可能詳細的梳理,否則一開就是一頓狂敲,最后一走發現全是Bug這樣就不好了!我們一開始盡可能的分析清楚,后面即使出現了問題也能快速確定!本期還是和往期一樣采用多文件編程!OK,我們下面一起來梳理一下這個小項目的基本實現邏輯:
1、創建菜單和多次操作問題
2、創建通訊錄
3、通訊錄的初始化以及增刪查改等操作
二、實現步驟
1、創建菜單和多次操作問題
我們期望當我們進行對通訊錄操作時可以給我們在進行操作前給一個菜單功能我們操作!考慮到后面還要多次操作通訊錄(增刪查改等)我們結合起來很快想到用do..while循環處理最為合理,這里以前寫過兩三遍了我們直接上代碼:
#include"contact.h"void menu()
{printf("*****************************************\n");printf("********* 1. add 2. del **********\n");printf("********* 3. search 4. modify **********\n");printf("********* 5. show 6. sort **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}void test()
{int input = 0;do{menu();printf("請選擇操作數:> ");scanf("%d", &input);switch (input){case 1://addbreak;case 2://delbreak;case 3://searchbreak;case 4://modifybreak;case 5://showbreak;case 6://sortbreak;case 0:printf("退出成功!\n");break;default:printf("選擇數非法!\n");break;}} while (input);
}int main()
{test();return 0;
}
看效果:
2、創建通訊錄
我們在對通訊錄進行各種操作前先得有個通訊錄吧!通訊錄的本質還是描述一個人的屬性例如:姓名、年齡、性別、電話、住址等!這些都是不同的類型,我們很容易想到用結構體就能解決!但這只是描述一個人,你的通訊錄不可能只有一個人吧應該是有很多個!而這些聯系人又是同一類型,因此采用結構體數組來創建通訊錄!
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[15];char addr[20];
}PeoInfo;
這就是描述一個人基本屬性的結構體!當創建好了這個結構體后,我們就可以在test.c這個文件中do..while循環那里可以創建結構體數組來作為通訊錄了!
但這樣寫會有個問題就是,當你存入或刪除聯系人后想打印出來看看是此時通訊錄中的聯系人時應該打印多少?我們還想不知道。所以這里我們還得再定義一個變量sz來記錄通訊錄的大小!
其實我們發現,sz不僅是可以記錄通訊錄的大小,而且增加聯系人的時候只需要增加到sz位置即可,增加一個sz++,當刪除一個sz--;這樣其實?sz和通訊錄是綁定在一起的,我們不妨在對通訊錄進行一層封裝,把他也封裝成一個結構體,一個成員是PeoInfo結構體數組,一個是記錄通訊錄大小的sz:
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[15];char addr[20];
}PeoInfo;typedef struct Contact
{PeoInfo data[100];int sz;
}Contact;
然后再在test.c文件中創建即可!
OK!這個創建通訊錄記好了!其實這里還可已在稍微優化一下:我們發現兩個結構體中都出現了大量的常數,我們萬一后期需求有變,這些都得改!因此為了后期修改方便,我們把這些都改成#define的常量,后期修改起來容易!這里你可能會說上期不是介紹了枚舉嗎,這里正好是多個常量枚舉不是更好嗎?這里枚舉確實可以達到目的,但枚舉是在沒開始前就能列舉出來所有可能,我們這個可不是!所以,這里不用枚舉!
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 20
#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;
這樣才是當前邏輯下的最優代碼!
3、初始化通訊錄
當我們創建好了通訊錄后我們先對其進行一個初始化!把結構體數組的每個元素都初始化為0,通訊錄大小也置為0!因為我們要改變結構體的內容所以應該傳地址!我們這里采用的是memset這個函數直接初始化為0,也可以用個循環來初始化!不了解memset的點擊memset
void InitContact(Contact* pc)
{assert(pc);memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}
4、添加聯系人
我們前面已經說過了,只需要在sz的位置增加即可!但要考慮通訊錄滿的情況!
void AddContact(Contact* pc)
{assert(pc);//通訊錄已滿if (pc->sz == MAX){printf("通訊錄已滿,無法添加!\n");return;}//添加聯系人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].tele);printf("請輸入聯系人住址:> ");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功!\n");
}
OK,我們下面先來寫一個顯示聯系人,來驗證一下是否添加成功!
5、顯示聯系人
顯示聯系人就是把通訊錄中的聯系人打印在控制臺!
void ShowContact(Contact* pc)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年齡", "性別", "電話", "住址");for (int i = 0; i < pc->sz; i++){printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}
OK,我們來看看是否添加成功以及顯示聯系人!
OK,添加聯系人和顯示聯系人都成功了!說明我們上面邏輯沒有問題!
6、刪除指定聯系人
我們輸入要刪除的人的姓名,進行查找,找到了刪除,否則輸出沒有此聯系人!注意考慮通訊錄為空的情況!這里提到了查找,這個查找和通訊錄的那個查找有點不一樣,這個是查找后返回下標即可,那個是還要打印聯系人信息功能不一樣。因此我們單獨在封裝一個函數!
//查找要刪除的人并返回下標
int FindContact(const Contact* pc, const char* name)
{for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//沒找到
}//刪除聯系人
void DelContact(Contact* pc)
{assert(pc);//通訊錄為空if (pc->sz == 0){printf("通訊錄為空,無法刪除!\n");return;}char name[NAME_MAX] = { 0 };printf("請輸入要刪除人的姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){//刪除for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功刪除聯系人!\n");}else{printf("沒有此聯系人!\n");}
}
這里要注意的是比較字符串要用strcmp,不能用 == 這個我們在字符串函數和內存函數那一期詳細介紹過!另外注意刪除的時候那里下標越界問題!
看效果:
?7、查找指定聯系人
查找指定聯系人還是先按指定方式進行查找, 找到了打印,否則輸出沒有此聯系人!這里顯示一個聯系人的這個里后面修改聯系人也可能要用所以我們為了方便把他封裝成一個函數后面直接調用即可!
//顯示單個聯系人信息
void Print(const Contact* pc, int pos)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年齡", "性別", "電話", "住址");printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}//查找指定聯系人
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要查找聯系人姓名:> ");scanf("%s", name);int pos = -1;//初始化為-1表示沒有此聯系人for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i;//找到了break;}}if (pos != -1){Print(pc, pos);}else{printf("沒有此聯系人!\n");}
}
其實還可以在簡潔一點,上面已經實現了FindContact我們可以用一下:
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要查找聯系人姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){Print(pc, pos);}else{printf("沒有此聯系人!\n");}
}
看效果:
8、修改聯系人信息
修改聯系人信息這塊就有很多可能,比如只修改一項例如名字或性別等,也有可能修改多項例如名字、年齡等,也有可能得全部修改這幾種情況。當我們要修改之前群毆我們能還是期望有一個菜單供我們選擇,當修改完一項后我們可能還要繼續修改。基于此我們采用和test.c文件中得do...while循環+菜單!
//修改聯系人信息菜單
void menu1()
{system("cls");printf("*****************************************\n");printf("********* 1. name 2. age **********\n");printf("********* 3. sex 4. tele **********\n");printf("********* 5. addr 6. all **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}//修改名字
void ModName(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人姓名:> ");scanf("%s", pc->data[ret].name);printf("修改成功!\n");
}//修改年齡
void ModAge(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人年齡:> ");scanf("%d", &pc->data[ret].age);printf("修改成功!\n");
}//修改性別
void ModSex(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人性別:> ");scanf("%s", &pc->data[ret].sex);printf("修改成功!\n");
}//修改電話
void ModTele(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人電話:> ");scanf("%s", &pc->data[ret].tele);printf("修改成功!\n");
}//修改住址
void ModAddr(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人住址:> ");scanf("%s", &pc->data[ret].addr);printf("修改成功!\n");
}void ModAll(Contact* pc, int ret)
{assert(pc);printf("請輸入聯系人姓名:> ");scanf("%s", pc->data[ret].name);printf("請輸入聯系人年齡:> ");scanf("%d", &pc->data[ret].age);printf("請輸入聯系人性別:> ");scanf("%s", pc->data[ret].sex);printf("請輸入聯系人電話:> ");scanf("%s", pc->data[ret].tele);printf("請輸入聯系人住址:> ");scanf("%s", pc->data[ret].addr);printf("修改成功!\n");
}//修改聯系人
void ModContact(Contact* pc)
{assert(pc);//判斷是否為空if (pc->sz == 0){printf("通訊錄為空,無法修改!\n");return;}char name[NAME_MAX] = { 0 };printf("請輸入要修改系人姓名:> ");scanf("%s", name);//判斷是否有該聯系人int ret = FindContact(pc, name);//有該聯系人if (ret != -1){int n = 0;do{menu1();printf("請選擇修改內容:> ");scanf("%d", &n);//修改switch (n){case 1:ModName(pc,ret);break;case 2:ModAge(pc, ret);break;case 3:ModSex(pc, ret);break;case 4:ModTele(pc, ret);break;case 5:ModAddr(pc, ret);break;case 6:ModAll(pc, ret);break;case 0:printf("修改結束!\n");break;default:printf("選擇數非法!\n");break;}} while (n);}else{printf("沒有此聯系人!\n");}
}
看效果:(小編做了清屏)
修改前:
?修改后:
OK,還是比較成功的!我們再來進行對他實現一下排序!
9、排序聯系人信息
我們上面已經實現了通訊錄的增刪查改的基本功能!我們想讓他再有一個排序功能,比如按名字排序,或年齡排序!我們在指針進階那塊介紹過回調函數,這里我們用qsort進行對通訊錄排序!我們期望還是和上面一樣一開始有個菜單選擇!
//排序菜單
void menu2()
{system("cls");printf("**********************************\n");printf("***** 1. name 2. age **********\n");printf("**********************************\n");
}//名字比較函數
int cmp_name(const void* str1, const void* str2)
{//return strcmp(((PeoInfo*)str1)->name, ((PeoInfo*)str2)->name);return strcmp((((Contact*)str1)->data)->name, (((Contact*)str2)->data)->name);
}//名字排序
void SortName(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
}//年齡比較函數
int cmp_age(const void* str1, const void* str2)
{//return ((PeoInfo*)str1)->age - ((PeoInfo*)str2)->age;return (((Contact*)str1)->data)->age - (((Contact*)str2)->data)->age;
}
//年齡排序
void SortAge(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_age);
}//排序聯系人信息
void SortContact(Contact* pc)
{assert(pc);//判斷為空if (pc->sz == 0){printf("通訊錄為空,無法排序!\n");Sleep(3000);system("cls");return;}menu2();int n = 0;printf("請選擇排序方式:> ");scanf("%d", &n);//排序switch (n){case 1:SortName(pc);system("cls");printf("排序成功!\n");break;case 2:SortAge(pc);system("cls");printf("排序成功!\n");break;default:printf("選擇數非法!\n");Sleep(3000);system("cls");break;}
}
看效果:
排序前(年齡):
?排序后(年齡):
排序前(名字):
?排序后(名字):
OK,實現了排序!
三、全部源碼
?contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 20
#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;//初始化通訊錄
void InitContact(Contact* pc);//添加聯系人
void AddContact(Contact* pc);//顯示聯系人
void ShowContact(const Contact* pc);//刪除聯系人
void DelContact(Contact* pc);//查找指定聯系人
void Search(const Contact* pc);//修改聯系人
void ModContact(Contact* pc);//排序聯系人信息
void SortContact(Contact* pc);
contact.c
#include"contact.h"//初始化通訊錄
void InitContact(Contact* pc)
{assert(pc);memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}//增加聯系人
void AddContact(Contact* pc)
{assert(pc);//通訊錄已滿if (pc->sz == MAX){printf("通訊錄已滿,無法添加!\n");Sleep(2000);return;}//添加聯系人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].tele);printf("請輸入聯系人住址:> ");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功!\n");Sleep(1000);
}//顯示聯系人
void ShowContact(const Contact* pc)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年齡", "性別", "電話", "住址");for (int i = 0; i < pc->sz; i++){printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}//查找要刪除的人并返回下標
int FindContact(const Contact* pc, const char* name)
{for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//沒找到
}//刪除聯系人
void DelContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通訊錄為空,無法刪除!\n");Sleep(2000);return;}char name[NAME_MAX] = { 0 };printf("請輸入要刪除人的姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){//刪除for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功刪除聯系人!\n");Sleep(1000);}else{printf("沒有此聯系人!\n");Sleep(2000);}
}//顯示單個聯系人信息
void Print(const Contact* pc, int pos)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年齡", "性別", "電話", "住址");printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}//查找指定聯系人
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要查找聯系人姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){Print(pc, pos);}else{printf("沒有此聯系人!\n");Sleep(2000);system("cls");}
}//修改聯系人信息菜單
void menu1()
{system("cls");printf("*****************************************\n");printf("********* 1. name 2. age **********\n");printf("********* 3. sex 4. tele **********\n");printf("********* 5. addr 6. all **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}//修改名字
void ModName(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人姓名:> ");scanf("%s", pc->data[ret].name);
}//修改年齡
void ModAge(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人年齡:> ");scanf("%d", &pc->data[ret].age);
}//修改性別
void ModSex(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人性別:> ");scanf("%s", &pc->data[ret].sex);
}//修改電話
void ModTele(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人電話:> ");scanf("%s", &pc->data[ret].tele);
}//修改住址
void ModAddr(Contact* pc, int ret)
{assert(pc);printf("請輸入修改后聯系人住址:> ");scanf("%s", &pc->data[ret].addr);
}void ModAll(Contact* pc, int ret)
{assert(pc);printf("請輸入聯系人姓名:> ");scanf("%s", pc->data[ret].name);printf("請輸入聯系人年齡:> ");scanf("%d", &pc->data[ret].age);printf("請輸入聯系人性別:> ");scanf("%s", pc->data[ret].sex);printf("請輸入聯系人電話:> ");scanf("%s", pc->data[ret].tele);printf("請輸入聯系人住址:> ");scanf("%s", pc->data[ret].addr);
}//修改聯系人
void ModContact(Contact* pc)
{assert(pc);//判斷是否為空if (pc->sz == 0){printf("通訊錄為空,無法修改!\n");Sleep(2000);system("cls");return;}char name[NAME_MAX] = { 0 };printf("請輸入要修改系人姓名:> ");scanf("%s", name);//判斷是否有該聯系人int ret = FindContact(pc, name);//有該聯系人if (ret != -1){int n = 0;do{menu1();Print(pc, ret);printf("請選擇修改內容:> ");scanf("%d", &n);//修改switch (n){case 1:ModName(pc,ret);break;case 2:ModAge(pc, ret);break;case 3:ModSex(pc, ret);break;case 4:ModTele(pc, ret);break;case 5:ModAddr(pc, ret);break;case 6:ModAll(pc, ret);break;case 0:printf("修改結束!\n");Sleep(2000);system("cls");break;default:printf("選擇數非法!\n");Sleep(2000);system("cls");break;}} while (n);}else{printf("沒有此聯系人!\n");Sleep(2000);system("cls");}
}//排序菜單
void menu2()
{system("cls");printf("**********************************\n");printf("***** 1. name 2. age **********\n");printf("**********************************\n");
}//名字比較函數
int cmp_name(const void* str1, const void* str2)
{//return strcmp(((PeoInfo*)str1)->name, ((PeoInfo*)str2)->name);return strcmp((((Contact*)str1)->data)->name, (((Contact*)str2)->data)->name);
}//名字排序
void SortName(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
}//年齡比較函數
int cmp_age(const void* str1, const void* str2)
{//return ((PeoInfo*)str1)->age - ((PeoInfo*)str2)->age;return (((Contact*)str1)->data)->age - (((Contact*)str2)->data)->age;
}
//年齡排序
void SortAge(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_age);
}//排序聯系人信息
void SortContact(Contact* pc)
{assert(pc);//判斷為空if (pc->sz == 0){printf("通訊錄為空,無法排序!\n");Sleep(3000);system("cls");return;}menu2();int n = 0;printf("請選擇排序方式:> ");scanf("%d", &n);//排序switch (n){case 1:SortName(pc);system("cls");printf("排序成功!\n");break;case 2:SortAge(pc);system("cls");printf("排序成功!\n");break;default:printf("選擇數非法!\n");Sleep(3000);system("cls");break;}
}
test.c
#include"contact.h"void menu()
{printf("*****************************************\n");printf("********* 1. add 2. del **********\n");printf("********* 3. search 4. modify **********\n");printf("********* 5. show 6. sort **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}void test()
{int input = 0;Contact con;InitContact(&con);do{menu();printf("請選擇操作數:> ");scanf("%d", &input);switch (input){case 1:AddContact(&con);system("cls");break;case 2:DelContact(&con);system("cls"); break;case 3:system("cls");Search(&con);break;case 4:ModContact(&con);break;case 5:system("cls");ShowContact(&con);break;case 6:SortContact(&con);break;case 0:printf("退出成功!\n");break;default:printf("選擇數非法!\n");break;}} while (input);
}int main()
{test();return 0;
}
OK,本期分享就到這里!好兄弟,我們下期再見!