日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言动态规划和文件操作练习——通讯录

發布時間:2024/3/13 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言动态规划和文件操作练习——通讯录 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

用C語言實現一個簡單通訊錄功能:

  • 命令端輸入對應指令可執行相應操作
  • 可以實現增、刪、查、改、排序、顯示、一鍵清除等功能
  • 每次輸入的信息保存到txt文件中
  • 每次打開通訊錄自動加載已有的txt文件中的信息

本文主要是針對指針、動態內存管理、以及文件讀寫操作的綜合練習。

目錄

聲明結構體

構建程序框架

函數創建

InitCon初始化函數

LoadCon加載信息函數

AddCapacity擴容函數

ADD增加信息函數

DEL,CHECK,MODIFY刪查改函數

SHOW顯示信息函數?

DEL ALL一鍵刪除,SaveCon保存通訊錄,Distory釋放內存:


聲明結構體

//通訊錄結構體 typedef struct Date {char name[20];char sex[5];int age;char tel[20];char address[30]; }Date;typedef struct Con {Date* p;int count;int capacity; }Con;

其中:

  • 結構體Date是用來存儲學生信息的
  • 結構體Con嵌套了一個Date結構體
  • Con中count用來計數,記錄當前目錄存儲學生的個數
  • Con中capacity用來記錄當前結構體內存大小(內存不夠時申請擴容)

構建程序框架

聲明完結構體后,先構建程序框架

//Con.h #include<stdio.h> //main.c void menu() {printf("******************************************\n");printf("******** 1.ADD 2.DEL ******\n");printf("******** 3.SHOW 4.MODIFY ******\n");printf("******** 5.CHECK 6.DEL ALL ******\n");printf("******** 7.SORT 0.EXIT ******\n");printf("******************************************\n");} int main() {int input = 0;Con arr;do{menu();scanf("%d", &input);} while (input);return 0; }

其中:

  • 創建一個菜單欄
  • 創建一個結構體變量arr
  • do while循環至少執行一次,當intput=0時循環結束

完善框架:?

只針對于上述要求,我們需要創建以下函數:

InitCon:初始化通訊錄,開辟內存

LoadCon:加載txt中的信息到內存中

ADD,DEL,CHECK,MODIFY:對應增、刪、查、改

SHOW:顯示全部信息

SORT:對某一項進行排序

DEL ALL:一鍵清除所有信息

EXIT:退出并保存

Distory:釋放內存

完善后的主函數如下:?

int main() {int input = 0;Con arr;InitCon(&arr);LoadCon(&arr);void (*pf[])(Con*) = {0,add,del,show,modify,check,del_all,sort};do{menu();scanf("%d", &input);if (input == 0){SaveCon(&arr);Distory(&arr);printf("退出!\n");}else if (input > 0 && input < 8){pf[input](&arr);}elseprintf("輸入錯誤,請重新輸入!\n");} while (input);return 0; }

其中:

  • void (*pf[])(Con*)為函數指針數組,其數組中存儲的類型void (*)(Con*)的函數指針,關于函數指針以及函數指針數組的知識,在回調函數中對其進行了詳細講解。
  • 通過調用函數指針數組的下標,從而實現調用程序的不同功能。
  • 當input=0時,需要先將內存中的信息保存到txt文件中,然后再釋放內存

函數創建

為了日后的維護與管理,所有函數均存入到Con.c中,與主函數分開。?

InitCon初始化函數

在程序開始時開辟指定大小的內存,在動態規劃中有malloc和calloc兩種方式開辟內存,這里使用calloc的形式進行開辟。

//Con.h #include<assert.h> #include<string.h> #include<errno.h>#define INIT_NUM 3//Con.c void InitCon(Con* pc) {assert(pc);pc->p = (Date*)calloc(INIT_NUM, sizeof(Date));if (pc->p == NULL){printf("InitCon::%s", strerror(errno));return;}pc->count = 0;pc->capacity = INIT_NUM; }

其中:

  • 傳入參數為結構體指針
  • 需要判斷結構體指針不能為空,assert需要包含頭文件<assert.h>
  • calloc使用方法:void* calloc (size_t num, size_t size);num為指定個數,size為指定大小,返回值為void*類型的指針
  • 開辟完內存后,最好再對其進行檢查,是否為空。
  • strerror(arrno)為打印錯誤信息,strerror需要包含頭文件<string.h>,arrno需要包含頭文件<errno.h>
  • 開辟內存時,同時初始化count=0,capcity等于calloc開辟的個數

LoadCon加載信息函數

這里用到了文件指針,對文件進行操作,大致思想為:

  • 如果文件中沒有信息則退出
  • 如果文件中存有信息,按行讀取
  • 每讀取一行,對應的count需要+1
  • 如果讀取的過程中,初始開辟的內存不夠了,需要對內存進行加載
void LoadCon(Con* pc) {assert(pc);FILE* pfopen = fopen("con.txt", "r");if (pfopen == NULL){perror("LoadCon");return;}Date tmp = { 0 };while (fread(&tmp, sizeof(Date), 1, pfopen)==1){AddCapacity(pc);pc->p[pc->count] = tmp;pc->count++;}printf("加載成功!\n");fclose(pfopen);pfopen = NULL; }

其中:

  • FIEL*表示為文件指針,fopen為打開文件操作,‘r’表示以只讀的方式打開
  • fread為二進制讀取數據(前提是文件中的數據是以二進制存儲的)?
  • fread的使用方法:size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
  • ptr為讀取輸出地址,size為一次讀取大小,count為讀取個數,stream為文件指針,當fread讀取成功時,會返回讀取個數
  • AddCapacity為自定義函數,檢查內存是否夠用,不足時擴容
  • 當讀取完文件后,記得關閉文件

AddCapacity擴容函數

擴容函數需要完成兩個任務:

1、檢查內存是否足夠

2、內存不足時進行擴容,并將capacity中存儲的信息更新?

void AddCapacity(Con* pc) {assert(pc);if (pc->count == pc->capacity){Date* str = (Date*)realloc(pc->p, sizeof(Date)*(pc->capacity+ ADD_NUM));if (pc->p == NULL){printf("AddCapacity::%s", strerror(errno));return;}pc->p = str;pc->capacity += ADD_NUM;printf("增容成功\n");} }

其中:

  • 當count中的個數等于capacity容量的個數時,便需要進行擴容
  • realloc函數的用法如下:void* realloc (void* ptr, size_t size);ptr為需要擴容的地址,size為擴容后新的空間總大小

ADD增加信息函數

ADD函數比較簡單,只需要在每次增加前對其內存進行檢查,然后格式化輸入信息即可,每次增加一個聯系人的信息后,其所對應的count計數也需要進行+1。

void add(Con* pc) {assert(pc);AddCapacity(pc);printf("請輸入名字:>\n");scanf("%s", pc->p[pc->count].name);printf("請輸入性別:>\n");scanf("%s", pc->p[pc->count].sex);printf("請輸入年齡:>\n");scanf("%d", &(pc->p[pc->count].age));printf("請輸入電話:>\n");scanf("%s", pc->p[pc->count].tel);printf("請輸入地址:>\n");scanf("%s", pc->p[pc->count].address);printf("輸入成功\n");pc->count++; }

DEL,CHECK,MODIFY刪查改函數

以上三個函數需要用到查找函數,先假設所有人的名字不重復,根據人的姓名進行查找,返回其所屬的count值。

int FindByName(const Con* pc) {assert(pc);int i = 0;char input[20] = {0};printf("請輸入名字\n");scanf("%s", input);for (i = 0; i <pc->count; i++){if (strcmp(input, pc->p[i].name) == 0){return i;}}return -1; }

其中:

  • 傳參時const保護結構體指針pc,不能通過解引用的方式來更改內容
  • 比較兩個字符串用strcmp,需要包含頭文件<string,h>,使用方法如下:int strcmp ( const char * str1, const char * str2 );str1和str2為相比較的兩個字符串,返回值分為>0,<0 =0,相等時返回0

當查找函數完成后,剩余的刪查改函數就簡單許多,需要注意的是:刪除函數需要更改count,并且使所有人信息往前挪一位。

刪除函數

//刪除 void del(Con* pc) {assert(pc);if (pc->count == 0){printf("聯系人為空\n");return;}int i = FindByName(pc);if (i != -1){int j = 0;for (j = i; j < pc->count; j++){pc->p[j] = pc->p[j + 1];}pc->count--;printf("刪除成功!\n");}elseprintf("沒有找到!\n"); }

查找并顯示函數

void check(const Con* pc) {assert(pc);int i = FindByName(pc);if (i != -1 ){printf("%-20s%-10s%-10s%-20s%-30s\n", "姓名", "性別", "年齡", "電話", "地址");printf("%-20s%-10s%-10d%-20s%-30s\n",pc->p[i].name,pc->p[i].sex,pc->p[i].age,pc->p[i].tel,pc->p[i].address);}elseprintf("沒有找到!\n"); }

修改函數?

void modify(Con* pc) {assert(pc);if (pc->count == 0){printf("聯系人為空\n");return;}int i = FindByName(pc);if (i != -1){printf("請輸入名字:>\n");scanf("%s", pc->p[i].name);printf("請輸入性別:>\n");scanf("%s", pc->p[i].sex);printf("請輸入年齡:>\n");scanf("%d", &(pc->p[i].age));printf("請輸入電話:>\n");scanf("%s", pc->p[i].tel);printf("請輸入地址:>\n");scanf("%s", pc->p[i].address);printf("修改成功!\n");}elseprintf("沒有找到!\n"); }

SHOW顯示信息函數?

從第一行開始循環打印,以聯系人數量count為限制,打印時為了保持美觀,最好設計一個表頭。

void show(const Con* pc) {assert(pc);if (pc->count == 0){printf("聯系人為空\n");return;}printf("%-20s%-10s%-10s%-20s%-30s\n", "name", "sex", "age", "tel", "address");int i = 0;for (i = 0; i < pc->count; i++){printf("%-20s%-10s%-10d%-20s%-30s\n", pc->p[i].name, pc->p[i].sex, pc->p[i].age,pc->p[i].tel, pc->p[i].address);} }

SORT排序函數

使用qsort函數進行排序。

int cmp_by_name(const void* e1, const void* e2) {return strcmp(((Date*)e1)->name, ((Date*)e2)->name); }void sort(Con* pc) {assert(pc);qsort(pc->p, pc->count, sizeof(Date), cmp_by_name);printf("排序成功!\n"); }

其中:

sqort函數使用方法如下:

  • qosrt函數需要包含頭文件<stdlib.h>
  • void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
  • base為排序的對象,num為排序的個數,size為單個排序類型的大小,最后需要給qsort傳入一個排序原則的函數,其返回值需要是個整數,根據返回值>0,<0,=0的情況進行排序。
  • qsort適合所有類型的排序,所以傳入給qsort的函數,其接收參數的形式必須是void*類型的。
  • cmp_by_name是傳入sqort的比大小函數

?

DEL ALL一鍵刪除,SaveCon保存通訊錄,Distory釋放內存

DEL ALL一鍵刪除

將通訊錄重新初始化,就可以完成一鍵刪除的功能了。

void del_all(Con* pc) {assert(pc);InitCon(pc);printf("全部刪除成功!\n"); }

SaveCon保存通訊錄

將內存中的數據以二進制的方式寫入到txt文本中。

void SaveCon(Con* pc) {assert(pc);FILE* pfwrite = fopen("con.txt", "w");if (pfwrite == NULL){perror("SaveCon");return ;}int i = 0;for (i = 0; i < pc->count; i++){fwrite(pc->p + i, sizeof(Date), 1, pfwrite);}printf("保存成功!\n");fclose(pfwrite);pfwrite = NULL; }

其中:

  • 先創建一個文件指針,以只寫的方式打開文件
  • 根據count的計數,按行依次將數據讀寫到txt文件中
  • fwrite為二進制讀寫,fwrite使用方法如下:
  • size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
  • ptr為起始的指針位置,size為讀取類型的大小,count為讀取的個數,stream為文件指針

以二進制的讀寫打開txt文件后,會是一堆亂碼,也可以格式化輸出到txt文件中。代碼如下:

//格式化輸出 fprintf(pfwrite, "%10s%10s%10d%10s%10s\n", pc->p[i].name, pc->p[i].sex, pc->p[i].age, pc->p[i].tel, pc->p[i].address);

如果要使用格式化輸出,在LoadCon加載函數中,信息載入的方式也需要進行修改。

Distory銷毀函數

void Distory(Con* pc) {assert(pc);free(pc->p);pc->p = NULL; }

直接釋放內存,并將指針置空。

在寫程序時,主要思想就是:先想好大致框架,每個函數的作用、參數以及返回值,框架構建好后依次完善函數即可。在寫程序中,要養成步步調試,寫注釋的好習慣。

總結

以上是生活随笔為你收集整理的C语言动态规划和文件操作练习——通讯录的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。