该通讯录中联系人信息包括:
程序代码完善过程:
该通讯录小程序实现了:
重要部分(重点所在):
#include
#include
#include
#include
#define PERSON_INFO_MAX_SIZE 1000
typedef struct PersonInfo{ //单个联系人信息结构体
char name[1024];
char gender[10];
int age;
char phone[1024];
char adder[1024];
}PersonInfo;
typedef struct AddressBook{ //电话本结构体
PersonInfo* infos;
int capacity; //容量,infos指针指向的动态数组的长度(元素个数)
int size; //[0, size)范围是数组的有效元素区间
}AddressBook;
AddressBook g_address_book;
void Init(AddressBook* addr_book){
assert(addr_book != NULL);
//对 addre_book 初始化
addr_book->size = 0;
//由于size控制了结构体数组中有效元素的区间范围,只要将size设为0,无论数组中存的是什么都是没有意义的,不太需要把数组元素设为某些特定值
addr_book->capacity = PERSON_INFO_MAX_SIZE;
addr_book->infos = (PersonInfo*)malloc(addr_book->capacity * sizeof(PersonInfo));
}
void Realloc(AddressBook* addr_book){
assert(addr_book != NULL);
addr_book->capacity *= 2;
addr_book->infos = (PersonInfo*)realloc(addr_book->infos, addr_book->capacity);
}
void AddPresonInfo(AddressBook* addr_book){ //-----新增-----
assert(addr_book != NULL);
if (addr_book->size >= addr_book->capacity){
printf("当前通信录已满,进行扩容!\n");
Realloc(addr_book);
}
printf("插入一个联系人!\n");
//每次都把新的联系人添加到有效数组的最后一个元素上
PersonInfo* p = &addr_book->infos[addr_book->size];
//此处不能取一个结构体变量 PersonInfo p = addr_book->infos[addr_book->size];
//只能取结构体指针 PersonInfo* p = &addr_book->infos[addr_book->size];
//如果取的是结构体变量,该变量相当于数组中对应元素的副本,即拷贝
//此时修改结构体变量只是对副本进行修改,没有影响到原来的数组
printf("请输入联系人姓名: ");
scanf("%s", p->name);
//如果之前不用p简化代码,此处要写成 scanf("%s",addr_book->infos[addr_book->size].name);
printf("请输入联系人性别('男','女','未知'): ");
scanf("%s", p->gender);
while (strcmp("男", p->gender) != 0 && strcmp("女", p->gender) != 0 && strcmp("未知", p->gender) != 0){
printf("性别输入错误,请重新输入:");
scanf("%s", p->gender);
}
printf("请输入该联系人年龄: ");
scanf("%d", &p->age);
while (p->age < 0 || p->age > 300){
printf("年龄输入不规范,请重新输入:");
scanf("%d", &p->age);
}
printf("请输入该联系人电话: ");
scanf("%s", p->phone);
printf("请输入联系人住址: ");
scanf("%s", p->adder);
++addr_book->size; //新增完成后,需要更新 size
printf("插入联系人成功!\n");
}
void DelPresonInfo(AddressBook* addr_book){ //-----删除-----
assert(addr_book != NULL);
printf("请输入要删除的联系人序号: ");
int id = 0;
scanf("%d", &id);
if (id < 0 || id >= addr_book->size){ //对输入进行验证
printf("您输入的序号有误! 删除失败!\n");
return;
}
PersonInfo* p = &addr_book->infos[id];
printf("请确认您要删除的联系人: [%d] %s (确认删除请输入'Y'): ", id, p->name); //确认操作
char cmd[1024] = { 0 };
scanf("%s", cmd);
if (strcmp(cmd, "Y") != 0){
printf("删除操作已经取消!\n");
return;
}
PersonInfo* from = &addr_book->infos[addr_book->size - 1];
PersonInfo* to = p;
*to = *from; //用最后一个联系人的信息覆盖要删除的联系人的位置
--addr_book->size; //并把存储个数减一
printf("删除成功!\n");
}
void ModifyPresonInfo(AddressBook* addr_book){ //-----修改-----
assert(addr_book != NULL);
printf("请输入需要修改的联系人序号: ");
int id = 0;
scanf("%d", &id);
if (id < 0 || id >= addr_book->size){ //对输入进行验证
printf("您输入的序号有误! 修改失败!\n");
return;
}
PersonInfo* p = &addr_book->infos[id];
char input[1024] = { 0 };
printf("请输入修改姓名(若不需要修改姓名请输入'#'): ");
scanf("%s", input);
if (strcmp(input, "#") != 0){
strcpy(p->name, input);
}
printf("请输入修改性别(若不需要修改性别请输入'#'): ");
scanf("%s", input);
if (strcmp(input, "#") != 0){
while (strcmp("男", input) != 0 && strcmp("女", input) != 0 && strcmp("未知", input) != 0){
printf("性别输入错误,请重新输入:");
scanf("%s", input);
}
strcpy(p->gender, input);
}
printf("请输入修改年龄(若不需要修改年龄请输入'-1'): ");
scanf("%d", &p->age);
if (p->age != -1){
while (p->age < 0 || p->age > 300){
printf("年龄输入不规范,请重新输入:");
scanf("%d", &p->age);
}
}
printf("请输入修改电话(若不需要修改电话请输入'#'): ");
scanf("%s", input);
if (strcmp(input, "#") != 0){
strcpy(p->phone, input);
}
printf("请输入修改住址(若不需要修改姓名请输入'#'): ");
scanf("%s", input);
if (strcmp(input, "#") != 0){
strcpy(p->adder, input);
}
printf("修改成功!\n");
}
void FindPresonInfo(AddressBook* addr_book){ //-----查找-----
assert(addr_book != NULL);
printf("请输入需要查找的联系人姓名: ");
char name[1024] = { 0 };
scanf("%s", name);
int i = 0, count = 0;
for (; i < addr_book->size; ++i){
PersonInfo* p = &addr_book->infos[i];
if (strcmp(name, p->name) == 0){
printf("[%d] %s\t%s\t%d岁\t%s\t%s\n", i, p->name, p->gender, p->age, p->phone, p->adder);
//break; 姓名是有可能重复的,尽量把匹配到的都找到
++count;
}
}
if (count == 0){
printf("没有找到该联系人!\n");
return;
}
else{
printf("共查找到 %d 个联系人!\n", count);
}
printf("查找完毕!\n");
}
void PrintAllPresonInfo(AddressBook* addr_book){ //-----显示全部-----
assert(addr_book != NULL);
printf("显示所有联系人如下:\n");
int i = 0;
for (; i < addr_book->size; ++i){
PersonInfo* p = &addr_book->infos[i];
printf("[%d] %s\t%s\t%d岁\t%s\t%s\n", i, p->name, p->gender, p->age, p->phone, p->adder);
}
printf("共显示了 %d 个联系人的信息!\n", addr_book->size);
}
void SortPresonInfo(AddressBook* addr_book){ //-----排序-----
assert(addr_book != NULL);
int i = 0, j = 0;
for (; i < addr_book->size - 1; ++i){
PersonInfo* p = &addr_book->infos[i];
for (j = i + 1; j < addr_book->size; ++j){
PersonInfo* q = &addr_book->infos[j];
if (strcmp(p->name, q->name) > 0){
PersonInfo x;
x = *p;
*p = *q;
*q = x;
}
}
}
printf("所有联系人排序成功!\n");
PrintAllPresonInfo(addr_book);
}
void ClearPresonInfo(AddressBook* addr_book){ //-----清除全部-----
assert(addr_book != NULL);
printf("您确认要清空所有联系人吗?(输入'Y'确认):\n");
char cmd[1024] = { 0 };
scanf("%s", cmd);
if (strcmp(cmd, "Y") != 0){
printf("清除操作已经取消!\n");
return;
}
addr_book->size = 0;
printf("成功清除所有联系人!\n");
}
void ReadPersonInfo(AddressBook* addr_book){ //-----读文件-----
assert(addr_book != NULL);
FILE *fp = fopen("./addr_book.txt", "rb");
if(fp == NULL){
perror("fopen");
return;
}
Init(addr_book);
while (fread((addr_book->infos + (addr_book->size)), sizeof(PersonInfo), 1, fp) == 1)
{
addr_book->size++;
if (addr_book->size == addr_book->capacity)
{
Realloc(addr_book);
}
}
fclose(fp);
PrintAllPresonInfo(addr_book);
}
void SavePersonInfo(AddressBook* addr_book){ //-----写文件-----
assert(addr_book != NULL);
FILE *fp = fopen("./addr_book.txt", "wb");
if(fp == NULL){
perror("fopen");
return;
}
int count = 0;
while(count != addr_book->size){
if(fwrite(addr_book->infos, sizeof(PersonInfo), 1, fp) != 1){
break;
}
addr_book->infos++;
count++;
}
fclose(fp);
}
int Menu(){
printf("=====================\n");
printf("1.新增\n");
printf("2.删除\n");
printf("3.修改\n");
printf("4.查找\n");
printf("5.排序\n");
printf("6.显示全部\n");
printf("7.清除全部\n");
printf("0.退出\n");
printf("=====================\n");
printf("请输入您的选择: ");
int choice = 0;
scanf("%d", &choice);
return choice;
}
int main(){
Init(&g_address_book); //对通讯录进行初始化
ReadPersonInfo(&g_address_book);
typedef void(*pfunc_t)(AddressBook*);
pfunc_t table[] = { //函数指针数组
AddPresonInfo,
DelPresonInfo,
ModifyPresonInfo,
FindPresonInfo,
SortPresonInfo,
PrintAllPresonInfo,
ClearPresonInfo
};
int c = (int)(sizeof(table) / sizeof(table[0])); //计算表元素个数,也就是可选择个数
//无符号数太危险,尽量往有符号转,sizeof返回值为 unsigned int,我们把它转为 int
while (1){
int choice = Menu();
if (choice < 0 || choice > c){
printf("您的输入有误!\n");
continue;
}
if (choice == 0){
printf("编辑退出!goodbye!\n");
break;
}
table[choice - 1](&g_address_book); //通过表驱动调用相应函数
}
SavePersonInfo(&g_address_book);
free(addr_book->infos);
//释放内存,该语句不写也可以,当程序执行完操作系统会进行释放。
//但当在大型项目中或者当程序循环执行时,必须对动态开辟的内存进行释放,否则会造成内存泄露。
//在C++中有智能指针,用户不用担心这个问题,会自行调用释放内存。
return 0;
}
虽然对C++的学习使我能够写出更加完善的代码项目,但最近几天查漏补缺发现我的C语言基础还有好多漏洞需要补,一些部分需要深入理解。(不去动手敲代码永远不知道自己的短板在哪里)在做通讯录这个小代码程序时,发现自己的基础还有好多部分不够牢靠,需要查漏补缺,倍感惭愧。
在编写代码过程中遇到文件读取和保存的问题,好长时间都没有得到有效解决。我查看了大量参考手册,看了文件操作中一些函数的标准用法,然而并没有解决问题。在参考并学习了下面博主的代码后,恍然大悟,明确知道自己问题所在,然后再去更改自己的代码发现很多问题,并吸取教训。在今后如果遇到问题,自己解决不了,不要不撞南墙不回头地死磕,要高效的解决问题。首先就是去看前辈们是如何写的,向前人学习,不要踩前人踩过的坑,再找出自己的问题所在,这样解决问题能够事半功倍。