通讯录
// 用宏替换后续经常出现的数字,便于程序的维护
#define NAME_MAX 20 // 名字的字符串的最大长度
#define SEX_MAX 20 // 性别的字符串的最大长度
#define TELE_MAX 20 // 电话的最大字符串的长度
#define ADDR_MAX 20 // 住址的最大字符串的长度#define CON_INIT 3 // 通讯录的初识大小// 定义每个人的信息为一个结构体,同时为了方便表示,采用typedef
typedef struct PeoInfo
{char name[NAME_MAX]; // 每个人的姓名char sex[SEX_MAX]; // 每个人的性别int age; // 每个人的年龄char tele[TELE_MAX]; // 每个人的电话char addr[ADDR_MAX]; // 每个人的住址
}PeoInfo;// 定义整个通讯录为一个结构体,同时为了方便表示,采用typedef
typedef struct Contact
{// 为了节省空间,采用动态内存分配的方法来获取整个通讯录的空间PeoInfo* data; // 结构体指针,可通过运算访问到所有人的信息int size; // 表示当前通讯录存了多少人的信息int capacity; // 表示该通讯录总共可以存多少人的信息
}Contact;
// 定义一个通讯录结构体Contact con;// 对该通讯录结构体进行初始化// 此后应该传结构体的地址而不是结构体,原因是要对结构体作出修改// 并且传结构体的地址节省空间且效率更高InitContact(&con);
// 在这个函数内部对通讯录结构体进行初始化
void InitContact(Contact* pc)
{// 此时通讯录是空的,应先为通讯录分配空间pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CON_INIT);// 如果分配成功,将通讯录的size设为0,capacity设为初识大小pc->size = 0;pc->capacity = CON_INIT;
}
菜单如下:
void menu()
{printf("请输入你的选择\n");printf("***** 1. add 2. del *****\n");printf("***** 3. search 4. modify *****\n");printf("***** 5. sort 6. print *****\n");printf("***** 0. exit *****\n");return;
}
用户的选择功能如下
// 定义input,用户输入input进行选择int input;do{menu(); // 打印菜单,提示用户进行选择scanf("%d", &input); // 用户输入input进行选择switch (input) // 用户选择后进入不同的功能{case ADD: // 增加联系人AddContact(&con);break;case DEL: // 删除联系人DeleteContact(&con);break;case SEARCH: // 查找联系人SearchContact(&con);break;case MODIFY: // 改变联系人ModifyContact(&con);break;case SORT: // 对联系人进行排序SortContact(&con);break;case PRINT: // 打印所有联系人的信息PrintContact(&con);break;case EXIT: // 退出程序并销毁DestroyContact(&con);break;}} while (input);
用枚举常量来表示各种选项
在每次增加联系人前,都要进行检查是否需要扩容的操作,若需要则进行扩容
void AddContact(Contact* pc)
{// 判断通讯录是否已满,若满,进行扩容if (pc->size == pc->capacity){PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 2);if (tmp == NULL){printf("realloc fail\n");return;}pc->data = tmp;// 若扩容成功,增大capacitypc->capacity *= 2;}// 输入要添加的联系人的信息// 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息printf("请输入名字\n");scanf("%s", pc->data[pc->size].name);printf("请输入性别\n");scanf("%s", pc->data[pc->size].sex);printf("请输入年龄\n");scanf("%d", &pc->data[pc->size].age);printf("请输入电话\n");scanf("%s", pc->data[pc->size].tele);printf("请输入住址\n");scanf("%s", pc->data[pc->size].addr);pc->size++; // 将存入的联系人的数量加1// 添加成功后,向用户展示新的通讯录PrintContact(pc);
}
void DeleteContact(Contact* pc)
{printf("请输入要删除的联系人的名字\n");char name[20];scanf("%s", name);// 定义一个新函数find,用来查找是否有这个联系人// 如果有,返回联系人的下标,如果没有,返回-1int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{for (int i = ret; i < pc->size - 1; i++){pc->data[i] = pc->data[i + 1];}pc->size--;}return;
}
这里还要定义一个find函数来找该联系人,如果找到返回该联系人的下标,如果找不到则返回-1。
int find(Contact* pc, char* name)
{for (int i = 0; i < pc->size; i++){if (strcmp(name, pc->data[i].name) == 0){return i;}}return -1;
}
查找时利用find函数来查找
void SearchContact(Contact* pc)
{printf("请输入要查找的联系人的名字\n");char name[20];scanf("%s", name);// 利用已经定义的find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{// 如果找到,打印该联系人的信息,首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[ret].name,pc->data[ret].sex,pc->data[ret].age,pc->data[ret].tele,pc->data[ret].addr);}return;
}
void ModifyContact(Contact* pc)
{printf("请输入要修改的联系人的名字\n");char name[20];scanf("%s", name);// 利用find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{printf("请输入名字\n");scanf("%s", pc->data[ret].name);printf("请输入性别\n");scanf("%s", pc->data[ret].sex);printf("请输入年龄\n");scanf("%d", &pc->data[ret].age);printf("请输入电话\n");scanf("%s", pc->data[ret].tele);printf("请输入住址\n");scanf("%s", pc->data[ret].addr);}return;
}
void SortContact(Contact* pc)
{// 这里采用升序排列// 且采用冒泡排序的方式进行排列for (int i = 0; i < pc->size; i++){for (int j = 0; j < pc->size - 1 - i; i++){if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0){char tmp[20];strcpy(tmp, pc->data[j].name);strcpy(pc->data[j].name, pc->data[j + 1].name);strcpy(pc->data[j + 1].name, tmp);}}}printf("排序成功\n");
}
// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{// 首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");// 然后用for循环打印所有联系人的信息for (int i = 0; i < pc->size; i++){printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}
}
void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->size = 0;pc->capacity = 0;
}
利用文件实现
当通讯录退出的时候,把信息写到文件。
当通讯录初始化后,加载文件的信息到通讯录中。
在每次销毁通讯录之前将通讯录的信息保存到文件中
CheckCapacity函数中的内容直接拷贝AddContact函数中的数组扩容即可
#include
#include
#include // 用宏替换后续经常出现的数字,便于程序的维护
#define NAME_MAX 20 // 名字的字符串的最大长度
#define SEX_MAX 20 // 性别的字符串的最大长度
#define TELE_MAX 20 // 电话的最大字符串的长度
#define ADDR_MAX 20 // 住址的最大字符串的长度#define CON_INIT 3 // 通讯录的初识大小// 定义每个人的信息为一个结构体,同时为了方便表示,采用typedef
typedef struct PeoInfo
{char name[NAME_MAX]; // 每个人的姓名char sex[SEX_MAX]; // 每个人的性别int age; // 每个人的年龄char tele[TELE_MAX]; // 每个人的电话char addr[ADDR_MAX]; // 每个人的住址
}PeoInfo;// 定义整个通讯录为一个结构体,同时为了方便表示,采用typedef
typedef struct Contact
{// 为了节省空间,采用动态内存分配的方法来获取整个通讯录的空间PeoInfo* data; // 结构体指针,可通过运算访问到所有人的信息int size; // 表示当前通讯录存了多少人的信息int capacity; // 表示该通讯录总共可以存多少人的信息
}Contact;// 使用枚举常量来表示用户的各种选择
enum Option
{EXIT,ADD,DEL,SEARCH,MODIFY,SORT,PRINT
};void SaveContact(Contact* pc)
{// 以读的形式打开文件FILE* pf = fopen("contact.txt", "wb");if (pf == NULL){perror("fopen");return;}// 将通讯录以二进制的形式保存到文件中// 由于联系人的信息有很多,所以这里可以采用for循环for (int i = 0; i < pc->size; i++){fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);}// 关闭文件fclose(pf);pf = NULL;
}CheckCapacity(Contact* pc)
{// 判断通讯录是否已满,若满,进行扩容if (pc->size == pc->capacity){PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 2);if (tmp == NULL){printf("realloc fail\n");return;}pc->data = tmp;// 若扩容成功,增大capacitypc->capacity *= 2;}
}void LoadContact(Contact* pc)
{// 以读的形式打开文件FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("fopen");return;}// 将文件中的内容加载到通讯录中// 这里用fread函数,当fread函数读取的联系人信息数为0时,说明读取结束PeoInfo tmp = { 0 }; // 定义一个联系人信息的结构体,便于读取while (fread(&tmp, sizeof(PeoInfo), 1, pf)){CheckCapacity(pc); // 在这个函数中查看数组是否需要扩容,若需要,则扩容pc->data[pc->size] = tmp;pc->size++;}// 关闭文件fclose(pf);pf = NULL;return;
}// 在这个函数内部对通讯录结构体进行初始化
void InitContact(Contact* pc)
{// 此时通讯录是空的,应先为通讯录分配空间pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CON_INIT);// 如果分配成功,将通讯录的size设为0,capacity设为初识大小pc->size = 0;pc->capacity = CON_INIT;LoadContact(pc);return;
}void menu()
{printf("请输入你的选择\n");printf("***** 1. add 2. del *****\n");printf("***** 3. search 4. modify *****\n");printf("***** 5. sort 6. print *****\n");printf("***** 0. exit *****\n");return;
}// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{// 首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");// 然后用for循环打印所有联系人的信息for (int i = 0; i < pc->size; i++){printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}
}void AddContact(Contact* pc)
{CheckCapacity(pc);// 输入要添加的联系人的信息// 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息printf("请输入名字\n");scanf("%s", pc->data[pc->size].name);printf("请输入性别\n");scanf("%s", pc->data[pc->size].sex);printf("请输入年龄\n");scanf("%d", &pc->data[pc->size].age);printf("请输入电话\n");scanf("%s", pc->data[pc->size].tele);printf("请输入住址\n");scanf("%s", pc->data[pc->size].addr);pc->size++; // 将存入的联系人的数量加1// 添加成功后,向用户展示新的通讯录PrintContact(pc);
}int find(Contact* pc, char* name)
{for (int i = 0; i < pc->size; i++){if (strcmp(name, pc->data[i].name) == 0){return i;}}return -1;
}void DeleteContact(Contact* pc)
{printf("请输入要删除的联系人的名字\n");char name[20];scanf("%s", name);// 定义一个新函数find,用来查找是否有这个联系人// 如果有,返回联系人的下标,如果没有,返回-1int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{for (int i = ret; i < pc->size - 1; i++){pc->data[i] = pc->data[i + 1];}pc->size--;}return;
}void SearchContact(Contact* pc)
{printf("请输入要查找的联系人的名字\n");char name[20];scanf("%s", name);// 利用已经定义的find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{// 如果找到,打印该联系人的信息,首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[ret].name,pc->data[ret].sex,pc->data[ret].age,pc->data[ret].tele,pc->data[ret].addr);}return;
}void ModifyContact(Contact* pc)
{printf("请输入要修改的联系人的名字\n");char name[20];scanf("%s", name);// 利用find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{printf("请输入名字\n");scanf("%s", pc->data[ret].name);printf("请输入性别\n");scanf("%s", pc->data[ret].sex);printf("请输入年龄\n");scanf("%d", &pc->data[ret].age);printf("请输入电话\n");scanf("%s", pc->data[ret].tele);printf("请输入住址\n");scanf("%s", pc->data[ret].addr);}return;
}void SortContact(Contact* pc)
{// 这里采用升序排列// 且采用冒泡排序的方式进行排列for (int i = 0; i < pc->size; i++){for (int j = 0; j < pc->size - 1 - i; i++){if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0){char tmp[20];strcpy(tmp, pc->data[j].name);strcpy(pc->data[j].name, pc->data[j + 1].name);strcpy(pc->data[j + 1].name, tmp);}}}printf("排序成功\n");
}void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->size = 0;pc->capacity = 0;
}int main()
{// 定义一个通讯录结构体Contact con;// 对该通讯录结构体进行初始化// 此后应该传结构体的地址而不是结构体,原因是要对结构体作出修改// 并且传结构体的地址节省空间且效率更高InitContact(&con); // 定义input,用户输入input进行选择int input;do{menu(); // 打印菜单,提示用户进行选择scanf("%d", &input); // 用户输入input进行选择switch (input) // 用户选择后进入不同的功能{case ADD: // 增加联系人AddContact(&con);break;case DEL: // 删除联系人DeleteContact(&con);break;case SEARCH: // 查找联系人SearchContact(&con);break;case MODIFY: // 改变联系人ModifyContact(&con);break;case SORT: // 对联系人进行排序SortContact(&con);break;case PRINT: // 打印所有联系人的信息PrintContact(&con);break;case EXIT: // 退出程序并销毁//在销毁之前将通讯录内的信息保存到文件中SaveContact(&con);DestroyContact(&con);break;}} while (input);return 0;
}