C语言通讯录
C語言通訊錄
文章目錄
- C語言通訊錄
- 1.需求分析
- 2. 具體實現
- 2.1 準備工作
- 2.2 初始化通訊錄
- 2.3 增加一個聯系人
- 2.4 打印通訊錄
- 2.5 刪除一個聯系人
- 2.6 查找指定聯系人
- 2.7 修改指定聯系人
- 2.8 清空所有聯系人
- 2.9 排序
- 3. 完整代碼
1.需求分析
首先我們要自己寫一個通訊錄,我們就要先知道通訊錄大概包括哪些內容。
這里我們就設計一個最簡單的版本,可以容納1000個人的靜態通訊錄。
通訊錄中要包含:聯系人名字、年齡、性別、電話、住址五個基本信息。
我們對通訊錄的操作無非就是增、刪、查、改四大類。
所以接下來我們要實現以下幾個模塊:
綜上我們采用分為三個文件進行編寫
test.c 用于測試程序。
contact.h 用于頭文件的引用,類型定義,函數聲明等。
contact.c 用于實現程序的主要業務代碼等。
2. 具體實現
2.1 準備工作
當我們得到以上信息時就可以逐步開始實現我們的通訊錄了。
首先通訊錄要包含:聯系人名字、年齡、性別、電話、住址五個基本信息。顯而易見我們要定義一個結構體來表示一個人的信息
// 聯系人的信息 typedef struct PeoInfo {char name[10]; int age;char sex[5];char tele[12];char addr[30]; } PeoInfo;我們這樣寫真的是好的寫法嗎?就比如名字我們只給了10個字符,如果有的名字超過了10個字符,那我們程序不就錯了嗎?
所以我們采用定義符號常量的方法,來便于以后不夠了對其進行修改。
#define NAME_MAX 10 // 名字最大容量 #define SEX_MAX 5 // 性別最大容量 #define TELE_MAX 12 // 電話最大容量 #define ADDR_MAX 30 // 地址最大容量// 聯系人的信息 typedef struct PeoInfo {char name[NAME_MAX]; // 名字int age; // 年齡char sex[SEX_MAX]; // 性別char tele[TELE_MAX]; // 電話char addr[ADDR_MAX]; // 住址 } PeoInfo;這時我們已經定義好了每個聯系人的結構體類型,可以創建我們的數組了。
#define MAX_SIZE 1000 // 通訊錄最大容量 PeoInfo con[MAX_SIZE]; // 存儲1000個人的信息這樣寫總覺得少了些什么?我們并不知道此時通訊錄中到底有多少數據。這里有人可能直接說那還不簡單,我再定義一個變量 sz 來表示當前通訊錄中有效信息的個數不就可以了嗎? 這個辦法可以是可以,但是欠缺考慮。比如我們在對函數傳參時 ,不僅要把結構體傳過去,還要單獨再傳一個 sz表示當前通訊錄中有效信息的個數 ,這就比較麻煩了。
所以我們選擇再封裝一個結構體類型來表示通訊錄。
// 通訊錄 typedef struct contact {PeoInfo data[MAX_SIZE]; // 存放添加進來的人的信息int sz; // 記錄的是當前通訊錄中有效信息的個數 } contact;這下我們終于大功告成可以創建我們的通訊錄了。
contact con;2.2 初始化通訊錄
通訊錄創建好之后,我們要先對其進行初始化之后才可以正常使用,不然里面存儲的全部都隨機值。
我們創建一個函數來初始化通訊錄,對于結構體而言我們普遍采用傳地址的方式來實現,并不是說傳值調用不可以,只是不好,在傳值調用時形參是實參的一份臨時拷貝,修改形參的值不會影響實參,傳值調用只適用于不對結構體進行修改的函數。同時當我們的結構體非常大時,在函數傳參時會造成極大的開銷,不像我們傳址調用,僅傳遞一個指向該結構體的指針。
// 初始化通訊錄 void InitContact(contact* pc) {assert(pc); // 斷言保證指針不為空memset(pc->data, 0, sizeof(pc->data)); // 將數組中每個元素置為0pc->sz = 0; }函數的實現就比較簡單了,將所有變量全部置為0即可。
到此準備步驟已經全部完成了,讓我們來開始實現具體細節吧!
首先程序一開始執行應該給我們顯示一個菜單來供我們來選擇進行什么操作。
// 打印菜單 void menu() {printf("*****************************\n");printf("***** 1.add 2.del *****\n");printf("***** 3.search 4.modify *****\n");printf("***** 5.print 6.remove *****\n");printf("***** 7.sort 0.exit *****\n");printf("*****************************\n"); }接下來我們就根據輸入的值,進行判斷執行什么操作,因為可能并不只使用一次,所以我們要寫一個循環。
enum Option {EXIT, // 0ADD, // 1DEL, // 2SEARCH, // 3MODIFY, // 4PRINT, // 5REMOVE, // 6SORT // 7 }; int input = 0;// 創建通訊錄contact con; // 初始化通訊錄InitContact(&con); do{ menu(); // 打印菜單printf("請選擇:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con); // 增break;case DEL:DelContact(&con); // 刪break;case SEARCH:SearchContact(&con); // 查break;case MODIFY:ModifyContact(&con); // 改break;case PRINT:PrintContact(&con); // 顯示break;case REMOVE:RemoveContact(&con); // 清除break;case SORT:SortContact(&con); // 排序break;case EXIT:printf("退出通訊錄\n");break;default:printf("選擇錯誤\n");break;}} while (input);這里的 switch … case 語句中的 case 語句表達示使用枚舉常量來代替。增強了程序的可讀性。
接下來就是一步一步實現各個模塊
2.3 增加一個聯系人
// 增加聯系人 void AddContact(contact* pc) {assert(pc); // 斷言保證指針不為空if (pc->sz == MAX_SIZE){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"); }在增加新的聯系人之前,我們應先判斷當前通訊錄是否已滿,如果已滿,提示用戶無法添加,函數直接結束即可。
未滿則添加一條新記錄,我們的 sz (sz 從0開始)正好指向當前應添加記錄的下標,直接輸入對應數據,添加成功后,sz++ 表示當前通訊錄新增了一條數據。
同時也可以提醒一下用戶,添加成功。
2.4 打印通訊錄
// 打印聯系人信息 void PrintContact(const contact* pc) {assert(pc);int i = 0;// 打印標題printf("%-15s %-5s %-5s %-11s %-30s\n", "名字", "年齡", "性別", "電話", "地址");// 打印記錄for (i = 0; i < pc->sz; i++){printf("%-15s %-5d %-5s %-11s %-30s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);} }添加成功后我們就可以打印出來看看對不對,打印就比較簡單了,直接遍歷所有記錄打印出來即可,注意在打印數據之前,應先把標題打印出來。關于格式大家可自行調整為自己喜歡的樣子。
這里給大家一個建議,在我們寫這種模塊化程序時,我們最好能夠,寫好一個模塊,就測試一個模塊,不要等到最后,整個程序都寫完了,程序一運行幾十個 bug,到底錯在了哪里也不知道,會花費大量時間來調試程序,搞的自己一頭霧水。寫好一個模塊后,立即測試,如果有問題,我們可以迅速定位 bug 出現了什么地方,大大減少了我們調試程序的時間。
2.5 刪除一個聯系人
// 刪除聯系人 void DelContact(contact* pc) {assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要刪除人的名字:>");scanf("%s", name);// 查找要刪除的聯系人int pos = FindByName(pc, name);// 沒有找到if (pos == -1){printf("要刪除的人不存在\n");return;}// 找到了,刪除int i = 0;for (i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1]; // 從后向前覆蓋}pc->sz--;printf("刪除成功\n"); }刪除一個聯系人我們是通過查找名字的方式, 查找程序中第一次出現這個名字的位置,找不到,則通訊錄查無此人,找到了,后面的數據依次往前移動,之后 sz–,將當前通訊錄中的記錄減少一條。同時提示用戶刪除成功
這里我們想到刪除需要查找聯系人,修改、查詢同時也需要通過名字來查找指定聯系人,這時我們就再封裝一個函數 FindByName 通過名字查找指定聯系人是否存在,存在返回對應下標,不存在返回 -1。
// 通過名字查找,指定聯系人 // 找到了返回對應下標 // 找不到返回 -1 static int FindByName(const contact* pc, char name[]) {assert(pc);int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0) // 比較兩個名字是否相同{return i; // 找到了,返回下標}}return -1; // 找不到 }2.6 查找指定聯系人
// 查找指定聯系人 void SearchContact(const contact* pc) {assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要查找人的名字:>");scanf("%s", name);// 查找指定的聯系人int pos = FindByName(pc, name);// 沒有找到if (pos == -1){printf("要查找的人不存在\n");return;}// 找到了,打印printf("%-15s %-5s %-5s %-11s %-30s\n", "名字", "年齡", "性別", "電話", "地址");printf("%-15s %-5d %-5s %-11s %-30s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr); }通過前面實現的函數,查找指定聯系人也變得很簡單,先按名字查找看是否存在,沒有則提示用戶,找不到。找到了直接打印出指定記錄即可,打印數據的代碼基本和打印通訊錄的代碼相似,直接拷貝過來修改一下就可以了。
2.7 修改指定聯系人
// 修改指定聯系人 void ModifyContact(contact* pc) {assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要修改人的名字:>");scanf("%s", name);// 查找要修改的聯系人int pos = FindByName(pc, name);// 沒有找到if (pos == -1){printf("要修改的人不存在\n");return;}// 詢問要改什么printf("請輸入要修改什么信息 (1-名字,2-年齡,3-性別,4-電話,5-地址):>");int msg = 0;scanf("%d", &msg);switch (msg){case 1:printf("請輸入新的名字:>");scanf("%s", pc->data[pos].name);break;case 2:printf("請輸入新的年齡:>");scanf("%d", &(pc->data[pos].age));break;case 3:printf("請輸入新的性別:>");scanf("%s", pc->data[pos].sex);break;case 4:printf("請輸入新的電話:>");scanf("%s", pc->data[pos].tele);break;case 5:printf("請輸入新的地址:>");scanf("%s", pc->data[pos].addr);break;default:printf("輸入錯誤,修改失敗\n");return;break;}printf("修改成功\n"); }同理,先按名字查找,看看聯系人是否存在,不存在提示用戶,找不到,程序返回。找到了則詢問用戶要修改什么信息,輸入信息代碼基本和增加一個聯系人的代碼相同,拷貝過來修改一下即可。
2.8 清空所有聯系人
// 清空聯系人 void RemoveContact(contact* pc) {assert(pc);InitContact(pc); }直接調用我們的初始化通訊錄函數即可,數據全部被清空。
2.9 排序
// 按名字升序 static int cmp_name(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name) < 0)return -1;elsereturn 0; }// 按年齡升序 static int cmp_age(const void* e1, const void* e2) {return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age; }// 按性別升序 static int cmp_sex(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex) < 0)return -1;elsereturn 0; }// 按電話升序 static int cmp_tele(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele) < 0)return -1;elsereturn 0; } // 按住址升序 static int cmp_addr(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr) < 0)return -1;elsereturn 0; }// 按任意類型排序 void SortContact(contact* pc) {assert(pc);int input = 0;// 函數指針數組,存儲對應用于排序的函數指針int(*p[])(const void*, const void*) = {0, cmp_name, cmp_age, cmp_sex, cmp_tele, cmp_addr};printf("請輸入您要按什么排序 (1-名字,2-年齡,3-性別,4-電話,5-地址):>");scanf("%d", &input);if (input >= 1 && input <= 5){qsort(pc->data, pc->sz, sizeof(pc->data[0]), p[input]); // 調用庫函數PrintContact(pc); // 打印顯示出來}else{printf("選擇錯誤\n");} }這里我們實現了對任意一個信息排序的功能,當然為了簡單也可以只實現按照名字排序,代碼基本都一樣的。
排序我們直接調用了庫函數 qsort 來實現,同樣我們也可以自己寫一個函數來實現。這里就不詳細介紹了。
至此一個簡單的通訊錄程序已經實現了。
3. 完整代碼
contact.h
#pragma once #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h> #include <string.h> #include <stdlib.h> #include <assert.h>#define MAX_SIZE 1000 // 通訊錄最大容量 #define NAME_MAX 10 // 名字最大容量 #define SEX_MAX 5 // 性別最大容量 #define TELE_MAX 12 // 電話最大容量 #define ADDR_MAX 30 // 地址最大容量// 類型定義 enum Option {EXIT, // 0ADD, // 1DEL, // 2SEARCH, // 3MODIFY, // 4PRINT, // 5REMOVE, // 6SORT // 7 };// 聯系人的信息 typedef 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_SIZE]; // 存放添加進來的人的信息int sz; // 記錄的是當前通訊錄中有效信息的個數 } contact; // 函數聲明// 初始化通訊錄 void InitContact(contact* pc);// 增加聯系人 void AddContact(contact* pc);// 刪除聯系人 void DelContact(contact* pc);// 查找指定聯系人 void SearchContact(const contact* pc);// 修改指定聯系人 void ModifyContact(contact* pc);// 打印聯系人信息 void PrintContact(const contact* pc);// 清空聯系人 void RemoveContact(contact* pc);// 排序 void SortContact(contact* pc);test.c
#include "contact.h"// 打印菜單 void menu() {printf("*****************************\n");printf("***** 1.add 2.del *****\n");printf("***** 3.search 4.modify *****\n");printf("***** 5.print 6.remove *****\n");printf("***** 7.sort 0.exit *****\n");printf("*****************************\n"); }void test() {int input = 0;// 創建通訊錄contact con; // 初始化通訊錄InitContact(&con); do{ menu(); // 打印菜單printf("請選擇:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case PRINT:PrintContact(&con);break;case REMOVE:RemoveContact(&con);break;case SORT:SortContact(&con);break;case EXIT:printf("退出通訊錄\n");break;default:printf("選擇錯誤\n");break;}} while (input); }int main() {test();return 0; }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_SIZE){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"); }// 通過名字查找,指定聯系人 // 找到了返回對應下標 // 找不到返回 -1 static int FindByName(const contact* pc, char name[]) {assert(pc);int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i; // 找到了,返回下標}}return -1; // 找不到 }// 刪除聯系人 void DelContact(contact* pc) {assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要刪除人的名字:>");scanf("%s", name);// 查找要刪除的人int pos = FindByName(pc, name);// 沒有找到if (pos == -1){printf("要刪除的人不存在\n");return;}// 找到了,刪除int i = 0;for (i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1]; // 從后向前覆蓋}pc->sz--;printf("刪除成功\n"); }// 修改指定聯系人 void ModifyContact(contact* pc) {assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要修改人的名字:>");scanf("%s", name);// 查找要修改的人int pos = FindByName(pc, name);// 沒有找到if (pos == -1){printf("要修改的人不存在\n");return;}// 詢問要改什么printf("請輸入要修改什么信息 (1-名字,2-年齡,3-性別,4-電話,5-地址):>");int msg = 0;scanf("%d", &msg);switch (msg){case 1:printf("請輸入新的名字:>");scanf("%s", pc->data[pos].name);break;case 2:printf("請輸入新的年齡:>");scanf("%d", &(pc->data[pos].age));break;case 3:printf("請輸入新的性別:>");scanf("%s", pc->data[pos].sex);break;case 4:printf("請輸入新的電話:>");scanf("%s", pc->data[pos].tele);break;case 5:printf("請輸入新的地址:>");scanf("%s", pc->data[pos].addr);break;default:printf("輸入錯誤,修改失敗\n");return;break;}printf("修改成功\n"); }// 查找指定聯系人 void SearchContact(const contact* pc) {assert(pc);char name[NAME_MAX] = { 0 };printf("請輸入要查找人的名字:>");scanf("%s", name);// 查找要修改的人int pos = FindByName(pc, name);// 沒有找到if (pos == -1){printf("要查找的人不存在\n");return;}// 找到了,打印printf("%-15s %-5s %-5s %-11s %-30s\n", "名字", "年齡", "性別", "電話", "地址");printf("%-15s %-5d %-5s %-11s %-30s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr); }// 打印聯系人信息 void PrintContact(const contact* pc) {assert(pc);int i = 0;// 打印標題printf("%-15s %-5s %-5s %-11s %-30s\n", "名字", "年齡", "性別", "電話", "地址");// 打印記錄for (i = 0; i < pc->sz; i++){printf("%-15s %-5d %-5s %-11s %-30s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);} }// 清空聯系人 void RemoveContact(contact* pc) {assert(pc);InitContact(pc); }// 按名字升序 static int cmp_name(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name) < 0)return -1;elsereturn 0; }// 按年齡升序 static int cmp_age(const void* e1, const void* e2) {return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age; }// 按性別升序 static int cmp_sex(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex) < 0)return -1;elsereturn 0; }// 按電話升序 static int cmp_tele(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele) < 0)return -1;elsereturn 0; } // 按住址升序 static int cmp_addr(const void* e1, const void* e2) {if (strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr) > 0)return 1;else if (strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr) < 0)return -1;elsereturn 0; }// 按任意類型排序 void SortContact(contact* pc) {assert(pc);int input = 0;int(*p[])(const void*, const void*) = {0, cmp_name, cmp_age, cmp_sex, cmp_tele, cmp_addr};printf("請輸入您要按什么排序 (1-名字,2-年齡,3-性別,4-電話,5-地址):>");scanf("%d", &input);if (input >= 1 && input <= 5){qsort(pc->data, pc->sz, sizeof(pc->data[0]), p[input]);PrintContact(pc); }else{printf("選擇錯誤\n");} }總結
- 上一篇: 20210312 plecs外部模式把示
- 下一篇: 在线摩尔斯密码加密解密工具