C語言靜態(tài)動態(tài)兩版本通訊錄實戰(zhàn)源碼
正片開始
這里為了方便對照,我搬出整個程序的前后修改版本,并分別作為靜態(tài)和動態(tài)版本,實際差距并不大,提供出來只供君參考
動機
為什么要寫一個通訊錄?
1.當然是一部分原因是看了b站上的資源自己比較感興趣
2.其次就是在C語言實際應用上可以深化和鞏固,其實都看得出來我中間斷更了好久的數(shù)據(jù)結(jié)構(gòu)與算法的博客,主要想法還是想把C語言從里到外不遺余力的殺穿,又走了一遍底層原理,不過我相信費時是值得的
3.在學習數(shù)據(jù)結(jié)構(gòu)和算法這板塊,需要C語言基礎高度的掌握,所以這部分算對前邊理論部分的一個實戰(zhàn)項目,數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu),無非就是數(shù)據(jù)的增刪查改,這里我們提前熱身一下也不錯。
靜態(tài)版本
頭文件( phonebook.h)
我們這里依舊使用之前寫掃雷的分裝思想,將這個通訊錄分為 頭文件,接口,功能三個部分。
首先我們寫出頭文件,就是我們需要用到什么,給他整出來,我們簡單粗暴給命名為“ phonebook.h ”,展示為下面三個部分:
#pragma once # define _CRT_SECURE_NO_WARNINGS # include<stdio.h> #include<assert.h> #include<string.h> #include<stdlib.h>
#define max 1000 #define x 30 #define y 30 #define z 20 typedef struct num { char name[x]; int age; char sex[y]; char addr[y]; char num[z]; }num; typedef struct book { num data[max]; int count; }book; enum conmenu { 退出, 增添, 刪減, 查找, 修改, 排序, 全覽, };
void start(book* p); //初始化通訊錄 void add(book* p);//增添信息 void show(const book* p);//展示信息 void delete(book* p);//刪除信息 int search(book* p,char name[]);//庫中搜尋要查找信息 void find(book* p);//查找信息 void modify(book* p);//修改信息 void sort(book* p);//排序信息
整個一坨掰成三瓣就是為了方便理解,從上到下分別是菜單(展示必要),自定義類型變量(結(jié)構(gòu)體,枚舉類型),需要實現(xiàn)的功能函數(shù)名。
為什么結(jié)構(gòu)體變量有兩個
book結(jié)構(gòu)體是我們整個通訊錄的骨骼,包含count(成員數(shù)量)和data(成員數(shù)據(jù)),這里就相當于模擬了一個數(shù)組出來,更方便管理; num 結(jié)構(gòu)體則是存放各個成員數(shù)據(jù)的細節(jié)成分(姓名,年齡,性別……)。
接口(test.c)
設置接口的目的就相當于是設置了一個大門,就是一個統(tǒng)攬全局的作用,我們把菜單,和功能模塊在這里作展示作用,代碼的實現(xiàn)我們會放在功能板塊實現(xiàn)。
#include"phonebook.h" void menu() { printf("-----------------------------\n"); printf("---- 歡迎使用通訊錄 ----\n"); printf("-----------------------------\n"); printf("----- 1.增添 | 2.刪減-----\n"); printf("----- 3.查找 | 4.修改-----\n"); printf("----- 5.排序 | 6.全覽-----\n"); printf("----- 0.退出 -----\n"); printf("-----------------------------\n"); } int main() { int input; book con; start(&con); do { menu(); printf("請選擇:\n"); scanf("%d", &input); switch (input) { case 增添: add(&con); break; case 刪減: delete(&con); break; case 查找: find(&con); break; case 修改: modify(&con); break; case 排序: sort(&con); break; case 全覽: show(&con); break; case 退出: break; default: printf("錯誤輸入!\n"); return 0; } } while (input); return 0; }
拋開菜單部分,do-while 語句就是對應了菜單給出的功能遙控器,需要什么點什么。
為什么我們需要一個枚舉類型,其原因就是為了對應上面菜單中的功能(枚舉類型成員在未賦值情況下,第一個成員默認值為 0 ,其下依次遞增,從而實現(xiàn)一一對應),我們作為設計者,主要是為了方便我們自己寫代碼,switch-case 語句中,case 條件我們一眼瞄過去如果是 1,2,3 的數(shù)字,我們在完善時就可能云里霧里,還要去查目錄,就很麻煩,所以我們用枚舉類型來避免這種小尷尬。
功能板塊(phonebook.c)
我們在通訊錄中需要達到的目的有基本的增刪查改,以及作為稱得上完美通訊錄的全覽,排序和退出,下面我們 一 一 實現(xiàn):
1. 初始化:
void start(book* p) { assert(p); p->count = 0; memset(p->data, 0, sizeof(p->data)); }
初識化首先就要避免空指針和隨機數(shù),所以這里安排一手 assert 和 置0 就夠了。
2. 增添:
void add(book* p) { assert(p); if (p->count == max) { printf("已滿,請先進行刪除\n"); return; } else { printf("請輸入姓名:"); scanf("%s", p->data[p->count].name); printf("請輸入年齡:"); scanf("%d", &(p->data[p->count].age)); printf("請輸入性別(F\\M):"); scanf("%s", p->data[p->count].sex); printf("請輸入地址:"); scanf("%s", p->data[p->count].addr); printf("請輸入電話:"); scanf("%s", p->data[p->count].num); p->count++; printf("\n增添成功!\n"); } }
增添數(shù)據(jù)我們要對應好結(jié)構(gòu)體中的類型,因為年齡比較特殊,它是 %d 的形式,輸入類型不是字符串,所以我們要進行取地址操作
3.查找
void find(book* p) { assert(p); if (p->count == 0) printf("\n目錄為空!\n"); else { char name[max] = { 0 }; printf("請輸入查找對象:\n"); scanf("%s", &name); int i = search(p, name); if (i != -1) { printf("聯(lián)系人存在,在第%d位\n", i + 1); } else { printf("聯(lián)系人不存在!\n"); } } }
注意在開始要判斷一下現(xiàn)在通訊錄狀態(tài)是否為空,空就無法查找,注意要在庫中尋找我們的查找對象,我們還要需要一個對信息進行篩選比對的函數(shù)來找出該對象,因此我們引入 search 函數(shù):
int search(book* p, char name[]) { for (int i = 0; i < p->count; i++) { if (strcmp(name , p->data[i].name)==0) { return i; } else { return -1; } } }
4.刪除
void delete(book* p) { assert(p); if (p->count == 0) { printf("\n為空,無法刪除!\n"); return; } else { printf("請輸入刪除對象:\n"); char name[max] = {0}; scanf("%s", &name); int f = search(p,name); if (f == -1) { printf("該對象不存在!\n"); } else { for (int j = f; j < p->count-1; j++) { p->data[j] = p->data[j + 1]; } p->count--; printf("\n刪除成功!\n"); } } }
注意在開始要判斷一下現(xiàn)在通訊錄狀態(tài)是否為空,空就無法刪除;確定不為空再找到刪除對象,我們所謂的刪除本質(zhì)就是用后面的數(shù)據(jù)進行依次覆蓋,最后讓數(shù)組大小 count -1 即可。
5.修改
void modify(book* p) { assert(p); if (p->count == 0) { printf("為空,無法刪除!\n"); return; } else { printf("請輸入對象姓名:\n"); char name[max] = { 0 }; scanf("%s", &name); int i = search(p,name); if(i!=-1) { printf("該對象存在\n"); printf("請輸入修改內(nèi)容:\n"); printf("請輸入姓名:\n"); scanf("%s", p->data[i].name); printf("請輸入年齡:\n"); scanf("%d", &(p->data[p->count].age)); printf("請輸入性別(F\M):\n"); scanf("%c", &(p->data[p->count].sex)); printf("請輸入地址:\n"); scanf("%s", p->data[p->count].addr); printf("請輸入電話:\n"); scanf("%s", p->data[p->count].num); printf("\n修改成功!\n"); } else { printf("該對象不存在!\n"); return 0; } } }
6.排序
int compare(const void* a, const void* b) { return strcmp(((book*)a)->data->name , ((book*)b)->data->name); } int compare2(const void* a, const void* b) { return strcmp(((book*)b)->data->name, ((book*)a)->data->name); } void sort(book* p) { printf("請選擇:\n"); printf("-----------------------------\n"); printf("----- 1.順序 | 2.逆序-----\n"); printf("-----------------------------\n"); int in; scanf("%d", &in); switch (in) { case 1: qsort(p->data, p->count, sizeof(p->data[0]), compare); case 2: qsort(p->data, p->count, sizeof(p->data[0]), compare2); } printf("\n 排序完成!\n"); }
排序我細分為了順序和逆序兩種,基本思想就是 qsort 函數(shù)一步到胃,不再贅述。
7.全覽
void show(book* p) { assert(p); int i = 0; printf("%10s %5s %5s %10s %13s\n","姓名","年齡","性別","地址","電話"); for (i = 0; i < p->count; i++) { printf("%10s %5d %5s %10s %13s\n", p->data[i].name, p->data[i].age, p->data[i].sex, p->data[i].addr, p->data[i].num); } }
就是全部打印出來即可,注意信息之間的間隔調(diào)整,讀者可自主更改其大小。
到這里我們所有功能就算實現(xiàn)完了,來康康實際效果如何(僅展示其中幾個):
靜態(tài)版全部代碼
為了方便讀者查看與創(chuàng)作,我把三個部分的代碼全部貼出來:
test.c(接口)
#include"phonebook.h" void menu() { printf("-----------------------------\n"); printf("---- 歡迎使用通訊錄 ----\n"); printf("-----------------------------\n"); printf("----- 1.增添 | 2.刪減-----\n"); printf("----- 3.查找 | 4.修改-----\n"); printf("----- 5.排序 | 6.全覽-----\n"); printf("----- 0.退出 -----\n"); printf("-----------------------------\n"); } int main() { int input; book con; start(&con); do { menu(); printf("請選擇:\n"); scanf("%d", &input); switch (input) { case 增添: add(&con); break; case 刪減: delete(&con); break; case 查找: find(&con); break; case 修改: fix(&con); break; case 排序: sort(&con); break; case 全覽: show(&con); break; case 退出: break; default: printf("錯誤輸入!\n"); return 0; } } while (input); return 0; }
phonebook.h(頭文件)
#pragma once # define _CRT_SECURE_NO_WARNINGS # include<stdio.h> #include<assert.h> #include<string.h> #include<stdlib.h> #define max 1000 #define x 30 #define y 30 #define z 20 typedef struct num { char name[x]; int age; char sex[y]; char addr[y]; char num[z]; }num; typedef struct book { num data[max]; int count; int capacity; }book; enum conmenu { 退出, 增添, 刪減, 查找, 修改, 排序, 全覽, }; void start(book* p); void add(book* p); void show(const book* p); void delete(book* p); int search(book* p,char name[]); void find(book* p); void fix(book* p); void sort(book* p);
phonebook.c(功能)
# define _CRT_SECURE_NO_WARNINGS #include"phonebook.h" void start(book* p) { assert(p); p->count = 0; memset(p->data, 0, sizeof(p->data)); } void add(book* p) { assert(p); if (p->count == max) { printf("已滿,請先進行刪除\n"); return; } else { printf("請輸入姓名:"); scanf("%s", p->data[p->count].name); printf("請輸入年齡:"); scanf("%d", &(p->data[p->count].age)); printf("請輸入性別(F\\M):"); scanf("%s", p->data[p->count].sex); printf("請輸入地址:"); scanf("%s", p->data[p->count].addr); printf("請輸入電話:"); scanf("%s", p->data[p->count].num); p->count++; printf("\n增添成功!\n"); } } void show(book* p) { assert(p); int i = 0; printf("%10s %5s %5s %10s %13s\n","姓名","年齡","性別","地址","電話"); for (i = 0; i < p->count; i++) { printf("%10s %5d %5s %10s %13s\n", p->data[i].name, p->data[i].age, p->data[i].sex, p->data[i].addr, p->data[i].num); } } int search(book* p, char name[]) { for (int i = 0; i < p->count; i++) { if (strcmp(name , p->data[i].name)==0) { return i; } else { return -1; } } } void delete(book* p) { assert(p); if (p->count == 0) { printf("\n為空,無法刪除!\n"); return; } else { printf("請輸入刪除對象:\n"); char name[max] = {0}; scanf("%s", &name); int f = search(p,name); if (f == -1) { printf("該對象不存在!\n"); } else { for (int j = f; j < p->count-1; j++) { p->data[j] = p->data[j + 1]; } p->count--; printf("\n刪除成功!\n"); } } } void find(book* p) { assert(p); if (p->count == 0) printf("\n目錄為空!\n"); else { char name[max] = { 0 }; printf("請輸入查找對象:\n"); scanf("%s", &name); int i = search(p, name); if (i != -1) { printf("聯(lián)系人存在,在第%d位\n", i + 1); } else { printf("聯(lián)系人不存在!\n"); } } } void fix(book* p) { assert(p); if (p->count == 0) { printf("為空,無法刪除!\n"); return; } else { printf("請輸入對象姓名:\n"); char name[max] = { 0 }; scanf("%s", &name); int i = search(p,name); if(i!=-1) { printf("該對象存在\n"); printf("請輸入修改內(nèi)容:\n"); printf("請輸入姓名:\n"); scanf("%s", p->data[i].name); printf("請輸入年齡:\n"); scanf("%d", &(p->data[p->count].age)); printf("請輸入性別(F\M):\n"); scanf("%c", &(p->data[p->count].sex)); printf("請輸入地址:\n"); scanf("%s", p->data[p->count].addr); printf("請輸入電話:\n"); scanf("%s", p->data[p->count].num); printf("\n修改成功!\n"); } else { printf("該對象不存在!\n"); return 0; } } } int compare(const void* a, const void* b) { return strcmp(((book*)a)->data->name , ((book*)b)->data->name); } int compare2(const void* a, const void* b) { return strcmp(((book*)b)->data->name, ((book*)a)->data->name); } void sort(book* p) { printf("請選擇:\n"); printf("-----------------------------\n"); printf("----- 1.順序 | 2.逆序-----\n"); printf("-----------------------------\n"); int in; scanf("%d", &in); switch (in) { case 1: qsort(p->data, p->count, sizeof(p->data[0]), compare); case 2: qsort(p->data, p->count, sizeof(p->data[0]), compare2); } printf("\n 排序完成!\n"); }
動態(tài)版
動態(tài)版的區(qū)別就在于他的內(nèi)存是活的,按需索取,要多少拿多少,絕不少給也絕不浪費空間,在空間上做出了質(zhì)的優(yōu)化,原理就是動態(tài)內(nèi)存分配。
typedef struct book { num data[max]; int count; int capacity; }book;
首先我們在 book 定義的結(jié)構(gòu)體中引入一個 capacity 變量,意為容量,它就相當于一個空間使用的指標變量。
動態(tài)初始化:
void start(book* p) { assert(p); num* container = (num*)malloc(DEFAULT * sizeof(num)); if (container != NULL) { p->data = container; p->count = 0; p->capacity = DEFAULT; } else { printf("%s\n", strerror(errno)); return; } }
重點是這里不再使用死板的數(shù)組,改用 malloc 函數(shù)先開辟一塊默認大小,這個大小我們隨意設置但避免浪費我們要盡量往小了設置。注意空指針,我們后面設置一個查看錯誤類型方便排空(注意引<errno.h>頭文件) 。
擴容函數(shù)
則擴容的關鍵就在于我們的 capa_city 函數(shù):
void capa_city(book* p) { num* container = (num*)realloc(p->data, sizeof(book) * (p->capacity + 2)); if (container != NULL) { p->data = container; p->capacity += 2 ; printf("增容成功!\n"); } else { printf("%s\n", strerror(errno)); return; } }
我們?yōu)榱梭w現(xiàn)空間利用的得當,我們每次擴容控制在 +2 數(shù)據(jù)空間即可。
接下來不用變動即可實現(xiàn)動態(tài)版本了,我直接上代碼:
動態(tài)版全部代碼
test.c
#include"phonebook.h" void menu() { printf("-----------------------------\n"); printf("---- 歡迎使用通訊錄 ----\n"); printf("-----------------------------\n"); printf("----- 1.增添 | 2.刪減-----\n"); printf("----- 3.查找 | 4.修改-----\n"); printf("----- 5.排序 | 6.全覽-----\n"); printf("----- 0.退出 -----\n"); printf("-----------------------------\n"); } int main() { int input; book con; start(&con); do { menu(); printf("請選擇:\n"); scanf("%d", &input); switch (input) { case 增添: add(&con); break; case 刪減: delete(&con); break; case 查找: find(&con); break; case 修改: fix(&con); break; case 排序: sort(&con); break; case 全覽: show(&con); break; case 退出: break; default: printf("錯誤輸入!\n"); return 0; } } while (input); return 0; }
phonebook.h
#pragma once # define _CRT_SECURE_NO_WARNINGS # include<stdio.h> #include<assert.h> #include<string.h> #include<stdlib.h> #include<errno.h> #define max 1000 #define x 30 #define DEFAULT 50 #define z 20 typedef struct num { char name[x]; int age; char sex[x]; char addr[x]; char num[z]; }num; typedef struct book { num* data; int count; int capacity; }book; enum conmenu { 退出, 增添, 刪減, 查找, 修改, 排序, 全覽, }; void start(book* p); void add(book* p); void show(const book* p); void delete(book* p); int search(book* p,char name[]); void find(book* p); void fix(book* p); void sort(book* p); void capa_city(book* p);
phonebook.c
# define _CRT_SECURE_NO_WARNINGS #include"phonebook.h" void capa_city(book* p) { num* container = (num*)realloc(p->data, sizeof(book) * (p->capacity + 2)); if (container != NULL) { p->data = container; p->capacity += 2 ; printf("增容成功!\n"); } else { printf("%s\n", strerror(errno)); return; } } void start(book* p) { assert(p); num* container = (num*)malloc(DEFAULT * sizeof(num)); if (container != NULL) { p->data = container; p->count = 0; p->capacity = DEFAULT; } else { printf("%s\n", strerror(errno)); return; } } void add(book* p) { if (p->capacity == p->count) { capa_city(p); } assert(p); if (p->count == max) { printf("已滿,請先進行刪除\n"); return; } else { printf("請輸入姓名:"); scanf("%s", p->data[p->count].name); printf("請輸入年齡:"); scanf("%d", &(p->data[p->count].age)); printf("請輸入性別(F\\M):"); scanf("%s", p->data[p->count].sex); printf("請輸入地址:"); scanf("%s", p->data[p->count].addr); printf("請輸入電話:"); scanf("%s", p->data[p->count].num); p->count++; printf("\n增添成功!\n"); } } void show(book* p) { assert(p); if (p->capacity == p->count) { capa_city(p); } int i = 0; printf("%10s %5s %5s %10s %13s\n","姓名","年齡","性別","地址","電話"); for (i = 0; i < p->count; i++) { printf("%10s %5d %5s %10s %13s\n", p->data[i].name, p->data[i].age, p->data[i].sex, p->data[i].addr, p->data[i].num); } } int search(book* p, char name[]) { for (int i = 0; i < p->count; i++) { if (strcmp(name , p->data[i].name)==0) { return i; } else { return -1; } } } void delete(book* p) { assert(p); if (p->count == 0) { printf("\n為空,無法刪除!\n"); return; } else { printf("請輸入刪除對象:\n"); char name[max] = {0}; scanf("%s", &name); int f = search(p,name); if (f == -1) { printf("該對象不存在!\n"); } else { for (int j = f; j < p->count-1; j++) { p->data[j] = p->data[j + 1]; } p->count--; printf("\n刪除成功!\n"); } } } void find(book* p) { assert(p); if (p->count == 0) printf("\n目錄為空!\n"); else { char name[max] = { 0 }; printf("請輸入查找對象:\n"); scanf("%s", &name); int i = search(p, name); if (i != -1) { printf("聯(lián)系人存在,在第%d位\n", i + 1); } else { printf("聯(lián)系人不存在!\n"); } } } void fix(book* p) { assert(p); if (p->count == 0) { printf("為空,無法刪除!\n"); return; } else { printf("請輸入對象姓名:\n"); char name[max] = { 0 }; scanf("%s", &name); int i = search(p,name); if(i!=-1) { printf("該對象存在\n"); printf("請輸入修改內(nèi)容:\n"); printf("請輸入姓名:\n"); scanf("%s", p->data[i].name); printf("請輸入年齡:\n"); scanf("%d", &(p->data[p->count].age)); printf("請輸入性別(F\M):\n"); scanf("%c", &(p->data[p->count].sex)); printf("請輸入地址:\n"); scanf("%s", p->data[p->count].addr); printf("請輸入電話:\n"); scanf("%s", p->data[p->count].num); printf("\n修改成功!\n"); } else { printf("該對象不存在!\n"); return 0; } } } int compare(const void* a, const void* b) { return strcmp(((book*)a)->data->name , ((book*)b)->data->name); } int compare2(const void* a, const void* b) { return strcmp(((book*)b)->data->name, ((book*)a)->data->name); } void sort(book* p) { printf("請選擇:\n"); printf("-----------------------------\n"); printf("----- 1.順序 | 2.逆序-----\n"); printf("-----------------------------\n"); int in; scanf("%d", &in); switch (in) { case 1: qsort(p->data, p->count, sizeof(p->data[0]), compare); case 2: qsort(p->data, p->count, sizeof(p->data[0]), compare2); } printf("\n 排序完成!\n"); }
今天就到這里吧,摸了家人們。
以上就是C語言靜態(tài)動態(tài)兩版本通訊錄實戰(zhàn)源碼的詳細內(nèi)容,更多關于C語言靜態(tài)動態(tài)通訊錄的資料請關注腳本之家其它相關文章!
相關文章
深入剖析Android中init進程實現(xiàn)的C語言源碼
這篇文章主要介紹了Android中init進程實現(xiàn)的C語言源碼,init屬性服務在安卓中屬于系統(tǒng)的底層Linux服務,需要的朋友可以參考下2015-07-07