目錄
前言
一、使通訊可以動態更新內存
1、contact.h
2、contact.c
存信息:
刪除聯系人,并試一個不存在的人的信息,看看會不會把其他人刪了
?編輯
修改:
?編輯
排序:
?編輯
銷毀:
?編輯
?編輯
二、通訊錄的信息保存,加載
通訊錄的信息保存;
再增加一個保存信息的函數
Save_Contact:
再增加一個從文件中加載信息的函數
Load_Contact:
?編輯
?編輯
三、下面是完整的代碼:
test.c
castact.c
contact.h
總結
前言
? ? ?經過長達六個月的時間,年邁的博主終于想起來,通訊錄還需要完善,那么這篇文章就對我們的初代通訊錄(初代通訊錄(詳細講解+代碼)_化學系通訊錄最簡單三個步驟-CSDN博客)進行完善,增加動態更新內存的內容,以及建立一個文件,將通訊錄的信息保存下來;
一、使通訊可以動態更新內存
我們先看一下原通訊錄
1、contact.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#pragma once#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define MAX 100//定義人的信息
typedef struct PeoInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;//定義通訊錄的信息
typedef struct Contact
{PeoInfo data[MAX];//存放人的信息int sz;//記錄目前通訊錄中存放的人的信息個數
}Contact;//初始化通訊錄
void Init_Contact(Contact* pc);
//增加聯系人
void Add_Contact(Contact* pc);
//刪除聯系人
void Del_Contact(Contact* pc);
查找聯系人
void Search_Contact(Contact* pc);
修改聯系人信息
void Modify_Contact(Contact* pc);
通訊錄信息查看
void Show_Contact(Contact* pc);
通訊錄信息排列
void Sort_Contact(Contact* pc);
2、contact.c
#include"contact.h"//初始化通訊錄
void Init_Contact(Contact* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));}
//增加聯系人
void Add_Contact(Contact* pc)
{assert(pc);if (pc->sz == MAX){printf("通訊錄滿了,無法添加");return;}//添加一個聯系人的信息//printf("請輸入你要添加的聯系人的信息:");//scanf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n", pc->data[pc->sz].name, pc->data[pc->sz].sex, &(pc->data[pc->sz].age), pc->data[pc->sz].tele, pc->data[pc->sz].addr);printf("請輸入姓名:");scanf("%s", pc->data[pc->sz].name);printf("請輸入性別:");scanf("%s", pc->data[pc->sz].sex);printf("請輸入年齡:");scanf("%d", &(pc->data[pc->sz].age)); printf("請輸入電話:");scanf("%s", pc->data[pc->sz].tele);printf("請輸入地址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;}
//刪除聯系人
void Del_Contact(Contact* pc)
{char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通訊錄為空,無法刪除");return;}//不為空,刪除printf("請輸入要刪除人的名字:");scanf("%s", name);int i = 0;int del = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){del = i;break;}}for (i = del; i < pc->sz-1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("已成功刪除該聯系人");}
//查找聯系人
void Search_Contact(Contact* pc)
{char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通訊錄為空");return;}printf("請輸入要查找的人的名字:");scanf("%s", name);int i = 0;int pos = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i; }}printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性別", "年齡", "電話", "地址");printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",pc->data[pos].name,pc->data[pos].sex,pc->data[pos].age,pc->data[pos].tele,pc->data[pos].addr);}
//修改聯系人信息
void Modify_Contact(Contact* pc)
{char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通訊錄為空,無法修改");return;}printf("請輸入要修改的人的名字:");scanf("%s", name);int i = 0;int pos = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i;}}printf("請輸入姓名:");scanf("%s", pc->data[pos].name);printf("請輸入性別:");scanf("%s", pc->data[pos].sex);printf("請輸入年齡:");scanf("%d", &(pc->data[pos].age));printf("請輸入電話:");scanf("%s", pc->data[pos].tele);printf("請輸入地址:");scanf("%s", pc->data[pos].addr);}
//通訊錄信息查看/打印通訊錄
void Show_Contact(Contact* pc)
{assert(pc);printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性別", "年齡", "電話", "地址");int i = 0;for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}}
int cmp_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}//通訊錄信息排列
void Sort_Contact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通訊錄為空,無法排序\n");return;}qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);printf("排序完成\n");}
可以看到原通訊錄是用100大小的靜態數組存儲信息的,現在我們將其改為動態的數組;
主要是初始化,增加聯系人,信息排列,銷毀通訊錄需要用到動態內存管理;
動態內存函數可以在這里看:動態內存函數_內存創建用什么函數-CSDN博客
我們先將定義進行更改,增加容量capacity,以及將存放信息的data數組不加限制;
然后在初始化Init_Contact這里進行修改,先設置初始容量capacity的值,然后再通過malloc設置初始內存,再用assert斷言確保內存設置好了,不為空;
然后就是增加聯系人這塊,當通訊錄滿了即pc->sz == pc->capacity的時候,將pc->capacity乘2,增加容量,當然了,想擴大多少倍看你自己喜歡,然后再對存放數據的data數組經行擴容,擴成修改后的capacity大小;最后加個assert斷言
然后是信息排列
最后是對動態數組的銷毀,先將內存釋放掉,然后置為空,再將capacity和sz置為0;
將各個功能都試一下;
存信息:
刪除聯系人,并試一個不存在的人的信息,看看會不會把其他人刪了
修改:
排序:
銷毀:
ok,這邊的通訊錄動態數組已經沒問題,我們接著來看
二、通訊錄的信息保存,加載
通訊錄的信息保存,加載涉及文件的讀寫操作:c語言文件操作-CSDN博客
通訊錄的信息保存;
首先創建一個記事本文檔,再在代碼中定義上
然后完善增加兩個函數的目錄
#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("****** 7.save 8.load ***********\n");printf("****** 0.exit 9.destory ***********\n");printf("**************************************\n");}
enum
{Exit,Add,Del,Search,Modify,Show,Sort,Save,Load,Destory
};//directory
int main()
{//創建通訊錄Contact con;Init_Contact(&con);int input = 0;do{menu();printf("請輸入:");scanf("%d", &input);switch (input){case Exit:printf("已退出通訊錄\n");break; case Add:Add_Contact(&con);break;case Del:Del_Contact(&con);break;case Search:Search_Contact(&con);break;case Modify:Modify_Contact(&con);break;case Show:Show_Contact(&con);break;case Sort:Sort_Contact(&con);break;case Save:Save_Contact(&con);break; case Load:Load_Contact(&con);break;case Destory:Destory_Contact(&con); break;default:printf("選擇錯誤,請重新選擇\n");break;} } while (input);//Destory_Contact(&con);return 0;
}
再增加一個保存信息的函數
Save_Contact:
void Save_Contact(Contact* pc) {assert(pc);FILE* file = fopen(FILENAME, "w");if (!file) {printf("無法打開文件進行保存\n");return;}for (int i = 0; i < pc->sz; i++) {fprintf(file, "%s %s %d %s %s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}fclose(file);printf("聯系人信息已經保存到文件\n");
}
再增加一個從文件中加載信息的函數
1. **打開文件**:
? ?- 使用 `fopen` 函數以只讀模式打開指定的文件。如果文件打開失敗,程序會輸出錯誤信息并返回。
2. **動態擴展內存**:
? ?- 在讀取每個聯系人之前,檢查當前的聯系人數量 `sz` 是否達到了當前容量 `capacity`。如果達到了,就使用 `realloc` 擴展內存,以便可以存儲更多的聯系人信息。
3. **讀取數據**:
? ?- 使用 `fscanf` 從文件中讀取每個聯系人的信息。格式字符串 `"%s %s %d %s %s\n"` 指定了要讀取的數據類型:
? ? ?- `%s` 用于讀取字符串(姓名、性別、電話、地址)。
? ? ?- `%d` 用于讀取整數(年齡)。
4. **存儲數據**:
? ?- 將讀取到的數據存儲到 `PeoInfo` 結構體的相應字段中。
為什么 `age` 需要加引用
在 C 語言中,`fscanf` 函數的工作原理是通過指針來修改變量的值。具體來說:
引用的概念:
?在 C 語言中,變量的值是存儲在內存中的某個地址。當你想要修改一個變量的值時,你需要提供這個變量的地址。
? 使用 `&` 符號可以獲取變量的地址。例如,`&pc->data[pc->sz].age` 獲取 `age` 字段的地址。
fscanf?的參數:
? fscanf的參數需要是指向變量的指針,以便它可以直接在內存中修改該變量的值。
? 對于 age字段,fscanf需要知道它的地址,以便將讀取到的整數值存儲到這個地址中。因此,必須使用 `&` 符號。
?示例:
假設我們有以下代碼:
int age;
fscanf(file, "%d", &age);
`&age` 將 `age` 變量的地址傳遞給 `fscanf`,這樣 `fscanf` 就可以在該地址上寫入讀取到的整數值。
- 如果不使用 `&`,例如 `fscanf(file, "%d", age);`,編譯器會報錯,因為 `age` 是一個整數,而 `fscanf` 需要的是一個指向整數的指針。
Load_Contact:
void Load_Contact(Contact* pc) {assert(pc);FILE* file = fopen(FILENAME, "r");if (!file) {printf("無法打開文件進行加載\n");return;}while (!feof(file)) {if (pc->sz == pc->capacity) {pc->capacity *= 2;pc->data = (PeoInfo*)realloc(pc->data, pc->capacity * sizeof(PeoInfo));//重新分配assert(pc->data);//確保內存分配成功}fscanf(file, "%s %s %d %s %s\n",pc->data[pc->sz].name,pc->data[pc->sz].sex,&pc->data[pc->sz].age,pc->data[pc->sz].tele,pc->data[pc->sz].addr);pc->sz++;}fclose(file);printf("聯系人信息已經加載\n");
}
再來驗一下貨,看看我們的代碼靈不靈
應該是沒有問題
三、下面是完整的代碼:
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("****** 7.save 8.load ***********\n");printf("****** 0.exit 9.destory ***********\n");printf("**************************************\n");}
enum
{Exit,Add,Del,Search,Modify,Show,Sort,Save,Load,Destory
};//directory
int main()
{//創建通訊錄Contact con;Init_Contact(&con);int input = 0;do{menu();printf("請輸入:");scanf("%d", &input);switch (input){case Exit:printf("已退出通訊錄\n");break; case Add:Add_Contact(&con);break;case Del:Del_Contact(&con);break;case Search:Search_Contact(&con);break;case Modify:Modify_Contact(&con);break;case Show:Show_Contact(&con);break;case Sort:Sort_Contact(&con);break;case Save:Save_Contact(&con);break; case Load:Load_Contact(&con);break;case Destory:Destory_Contact(&con); break;default:printf("選擇錯誤,請重新選擇\n");break;} } while (input);//Destory_Contact(&con);return 0;
}
castact.c
#include"contact.h"//初始化通訊錄
void Init_Contact(Contact* pc)
{assert(pc);pc->sz = 0;pc->capacity = 2;//設置初始容量pc->data = (PeoInfo*)malloc(pc->capacity * sizeof(PeoInfo));//初始內存assert(pc->data);//確保內存不為空}
//增加聯系人
void Add_Contact(Contact* pc)
{assert(pc);if (pc->sz == pc->capacity){//printf("通訊錄滿了,無法添加");//return;pc->capacity *= 2;pc->data = (PeoInfo*)realloc(pc->data,pc->capacity*sizeof(PeoInfo));assert(pc->data);//確保內存分配成功}//添加一個聯系人的信息//printf("請輸入你要添加的聯系人的信息:");//scanf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n", pc->data[pc->sz].name, pc->data[pc->sz].sex, &(pc->data[pc->sz].age), pc->data[pc->sz].tele, pc->data[pc->sz].addr);printf("請輸入姓名:");scanf("%s", pc->data[pc->sz].name);printf("請輸入性別:");scanf("%s", pc->data[pc->sz].sex);printf("請輸入年齡:");scanf("%d", &(pc->data[pc->sz].age)); printf("請輸入電話:");scanf("%s", pc->data[pc->sz].tele);printf("請輸入地址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;}
//刪除聯系人
void Del_Contact(Contact* pc)
{char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通訊錄為空,無法刪除");return;}//不為空,刪除printf("請輸入要刪除人的名字:");scanf("%s", name);int i = 0;int del = -1;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){del = i;break;}}if (del == -1) {printf("未找到該聯系人");return;}for (i = del; i < pc->sz-1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("已成功刪除該聯系人");}
//查找聯系人
void Search_Contact(Contact* pc)
{char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通訊錄為空");return;}printf("請輸入要查找的人的名字:");scanf("%s", name);int i = 0;int pos = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i; printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性別", "年齡", "電話", "地址");printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",pc->data[pos].name,pc->data[pos].sex,pc->data[pos].age,pc->data[pos].tele,pc->data[pos].addr);return;}}printf("未找到該聯系人");}
//修改聯系人信息
void Modify_Contact(Contact* pc)
{char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通訊錄為空,無法修改");return;}printf("請輸入要修改的人的名字:");scanf("%s", name);int i = 0;int pos = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i;printf("請輸入姓名:");scanf("%s", pc->data[pos].name);printf("請輸入性別:");scanf("%s", pc->data[pos].sex);printf("請輸入年齡:");scanf("%d", &(pc->data[pos].age));printf("請輸入電話:");scanf("%s", pc->data[pos].tele);printf("請輸入地址:");scanf("%s", pc->data[pos].addr);return;}}printf("未找到該聯系人");}
//通訊錄信息查看/打印通訊錄
void Show_Contact(Contact* pc)
{assert(pc);printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性別", "年齡", "電話", "地址");int i = 0;for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}}
int cmp_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}//通訊錄信息排列
void Sort_Contact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通訊錄為空,無法排序\n");return;}//qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);printf("排序完成\n");}
void Destory_Contact(Contact* pc)
{assert(pc);free(pc->data);//釋放內存/*if (pc->sz == 0){printf("通訊錄為空\n");return;}for (int i =pc->sz; i >=0; i--){pc->sz--;pc->data[i] = pc->data[i + 1];}*/pc->data = NULL;//置空pc->sz = 0;pc->capacity = 0;printf("已全部刪除,通訊錄銷毀\n");}void Save_Contact(Contact* pc) {assert(pc);FILE* file = fopen(FILENAME, "w");if (!file) {printf("無法打開文件進行保存\n");return;}for (int i = 0; i < pc->sz; i++) {fprintf(file, "%s %s %d %s %s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}fclose(file);printf("聯系人信息已經保存到文件\n");
}void Load_Contact(Contact* pc) {assert(pc);FILE* file = fopen(FILENAME, "r");if (!file) {printf("無法打開文件進行加載\n");return;}while (!feof(file)) {if (pc->sz == pc->capacity) {pc->capacity *= 2;pc->data = (PeoInfo*)realloc(pc->data, pc->capacity * sizeof(PeoInfo));//重新分配assert(pc->data);//確保內存分配成功}fscanf(file, "%s %s %d %s %s\n",pc->data[pc->sz].name,pc->data[pc->sz].sex,&pc->data[pc->sz].age,pc->data[pc->sz].tele,pc->data[pc->sz].addr);pc->sz++;}fclose(file);printf("聯系人信息已經加載\n");
}
contact.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<math.h>
#pragma once
#define FILENAME "contacts.txt"//文件名#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
//#define MAX 1000//定義人的信息
typedef struct PeoInfo
{char name[NAME_MAX];char sex[SEX_MAX];int age;char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;//定義通訊錄的信息
typedef struct Contact
{//PeoInfo data[MAX];//存放人的信息PeoInfo* data;//存放人的信息int sz;//記錄目前通訊錄中存放的人的信息個數int capacity;//記錄目前容量
}Contact;//初始化通訊錄
void Init_Contact(Contact* pc);
//增加聯系人
void Add_Contact(Contact* pc);
//刪除聯系人
void Del_Contact(Contact* pc);
查找聯系人
void Search_Contact(Contact* pc);
修改聯系人信息
void Modify_Contact(Contact* pc);
通訊錄信息查看
void Show_Contact(Contact* pc);
通訊錄信息排列
void Sort_Contact(Contact* pc);
//通訊錄刪除全部聯系人
void Destory_Contact(Contact* pc);void Save_Contact(Contact* pc); //保存void Load_Contact(Contact* pc);//加載
總結
首先非常感謝大家的觀看,這期對通訊錄進行了完善,增加了動態變化數組已經信息的保存與加載
修改了很多缺陷:
1.這個通訊錄能保存了,不會出現一旦退出就消失了
2.這個通訊錄沒有固定大小,減少了空間的浪費
本期內容到這里就結束了,有什么不足的地方可以提出來,我會虛心接受改進的,大家一起加油吧