1.引言
在我们学完了文件操作,指针,以及结构体等等知识后,尝试写出一个通讯录程序,并且能实现以下功能:增加通讯录,删除通讯录,查找通讯录,显示通讯录,退出程序,下面我们来进行代码实现;
2.test.c源文件的编写
首先我们想把通讯录页面打印出来,那么我们可以定义一个input变量,并且用do...while循环来循环整个过程,用menu函数来打印菜单,用switch语句来分不同情况,但我们可以将平常我们习惯使用的单个整形来表示不同的情况,例如:case 0: case 1: ......,将数字替换成枚举常量,枚举常量可以代表不同的含义,可以起到提示我们撰写函数名的作用,那么我们就可以定义一个enum INPUT的枚举类型,这里值得注意的是,如果退出的选项对应的枚举常量为exit的话,会与库函数exit起冲突,发生错误,这里我们统一用大写来定义枚举常量,这里我们先定义一个结构体变量,稍后我们会在contact.c中具体介绍,然后使用一个初始化函数来初始化结构体,再根据不同的情况,写出我们将在contact.c中定义的函数的函数名,到此,源文件test.c的撰写就结束了,下面是具体代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("*************************************\n");
printf("******** CONTACT PLUS *******\n");
printf("******** 1. add 2. del ******\n");
printf("******** 3. search 4. modify ******\n");
printf("******** 5. show 0. exit ******\n");
printf("*************************************\n");
printf("*************************************\n");
}
enum INPUT
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
Contact con;
int main()
{
int input = 0;
Initcontact(&con);
do
{
menu();
scanf("%d", &input);
switch (input)
{
case EXIT:
break;
case ADD:
Addcontact(&con);
break;
case DEL:
Delcontact(&con);
break;
case SEARCH:
Searchcontact(&con);
break;
case MODIFY:
Modifycontact(&con);
break;
case SHOW:
Showcontact(&con);
break;
}
} while (input);
return 0;
}
3.contact.c源文件的编写以及各函数的实现
1.Initcontact函数的编写
这里就不得不提及到我们在test.c源文件定义的结构体变量了,我们想在通讯录中记录名字,性别,电话,以及住址等信息,那么我们就定义一个结构体,并将其重命名:
typedef struct peoinfo
{
char name[20];
char sex[10];
char tele[13];
char addr[40];
}Peo;
如果想对通讯录进行操作,我们还需要知道通讯录中成员的具体数量,那么我们就将数量sz和结构体数组data一起定义成一个新的结构体:
typedef struct contact
{
Peo data[MAX];
int sz;
}Contact;
这样,定义完结构体后,我们就可以对结构体进行初始化了,这里我们取con的地址传给函数,那么就用Contact*这个类型的指针去接收,使用指向操作符对结构体成员进行访问,pc->sz=0;
再利用memset将pc->data初始化,大小计算可以使用sizeof(pc->data),这样,完成了函数的编写:
void Initcontact(Contact* pc)
{
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
2.Addcontact函数的编写
在添加通讯录成员前,我们需要在函数开始判断是否放得下将要添加的成员,如果sz等于了我们#define的MAX,那么就显示满了,无法添加,如果不满足这一个条件,那么允许添加,我们scanf每一个成员,最后sz++,代表添加成功,成员数+1,这里值得注意的是,字符串在这里本身就代表首元素地址,不必再用&,但如果是整形,仍然需要使用&符号:
void Addcontact(Contact* pc)
{
if (pc->sz >= MAX)
{
printf("contact满了\n");
}
else
{
printf("请输入name:\n");
printf("请输入sex:\n");
printf("请输入tele:\n");
printf("请输入addr:\n");
scanf("%s\n%s\n%s\n%s", pc->data[pc->sz].name, pc->data[pc->sz].sex, pc->data[pc->sz].tele, pc->data[pc->sz].addr);
pc->sz++;
}
}
3.Delcontact函数的编写
如果我们想要删除通讯录中的某一个成员,首先要让用户输入想要删除成员的名称,然后在整个结构体中去查找这个成员,如果找到了那么就进行下一步,没有找到就结束,下一步,将这个成员后面所有成员分别依次向前赋值,注意在结构体成员中,整形数据是可以直接赋值的,但是字符串等等数据就需要用到memmove,memcpy,strcpy这样的函数去操作,同时将sz自减一个,就可以实现删除的功能:
void Delcontact(Contact* pc)
{
printf("请输入你想删除的名字:\n");
char input[10] = { 0 };
gets(input);
int i = 0;
int flag = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(input, pc->data[i].name) == 0)
{
flag = 1;
break;
}
}
if (flag == 0)
{
printf("没有找到,你在搞什么啊!\n");
}
else
{
int j = 0;
for (j = i; j < pc->sz - 1; j++)
{
strcpy(pc->data[j].name , pc->data[j + 1].name);
strcpy(pc->data[j].sex, pc->data[j + 1].sex);
strcpy(pc->data[j].tele, pc->data[j + 1].tele);
strcpy(pc->data[j].addr, pc->data[j + 1].addr);
}
pc->sz--;
}
}
4.Searchcontact函数的编写
这一函数相对前几个函数比较简单,只需要在结构体中查找与用户输入字符串相同的结构i成员组,如果找到那么就将这一结构体打印出来,如果没有找到则不打印,直接给出代码:
void Searchcontact(Contact* pc)
{
printf("请输入你想找到的名字:\n");
char input[10] = { 0 };
scanf("%s", &input);
int i = 0;
int flag = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(input, pc->data[i].name) == 0)
{
flag = 1;
printf("嘿,你小汁!\n");
printf("%-10s\t%-10s\t%-12s\t%-40s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
break;
}
}
if (flag == 0)
{
printf("没有找到,你在搞什么啊!\n");
}
}
5.Modifycontact函数的编写
想要修改通讯录中的成员信息,也是先要找到,重复与前两个函数同样的动作,其实查找成员最好是写成一个新函数,否则这样显得麻烦和累赘,这也是我的通讯录中需要改进的地方,找到之后,重新scanf每个结构体的信息,将新的信息覆盖旧的信息,其他的地方则不加改动:
void Modifycontact(Contact* pc)
{
printf("请输入你想修改的名字:\n");
char input[10] = { 0 };
scanf("%s", &input);
int i = 0;
int flag = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(input, pc->data[i].name) == 0)
{
flag = 1;
printf("嘿,你小汁!\n");
printf("请输入name:\n");
printf("请输入sex:\n");
printf("请输入tele:\n");
printf("请输入addr:\n");
scanf("%s\n%s\n%s\n%s", pc->data[i].name, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
break;
}
}
if (flag == 0)
{
printf("没有找到,你在搞什么啊!\n");
}
}
4.升级初级通讯录
学完文件操作和动态内存开辟之后,我们可以来对我们的初阶通讯录进行一个升级,那么大多数函数和结构是不需要改动的,需要改动的是下面几个部分:
1.升级Contact结构体
如果我们想要对结构体所占空间实现一个动态分配和开辟,加载通讯录中以及有的内容,那么我们首先就要对Contact结构体进行升级,首先我们可以把data改为Peo*类型的指针,再来增添一个capacity来表示开辟空间后所有容纳的成员数量,代码如下:
typedef struct contact
{
Peo* data;
int sz;
int capacity;
}Contact;
2.升级Initcontact函数
前面既然定义了一个Peo*的指针,想要放下结构体成员,那么肯定要用到动态内存开辟函数calloc,在开辟的同时对结构体进行初始化,首先我们开辟一个设定的初始容量,用tmp指针接收,如果为NULL,说明空间开辟失败,那么就结束这个函数,开辟成功,我们就对通讯录进行一个信息加载,用到Loadcontact函数,在Loadcontact函数中,我们用fopen来打开文件,用fread来读取数据,这里我们每次让fread读取一次,如果fread的返回值为0,就说明读取结束了,在读取数据的同时,我们还要检查内存是否够用,于是就有了checkdata函数,此函数中,如果sz等于了capacity那么就要进行扩容,一次扩容inc_sz这么多个结构体大小,随后capacity加上inc_sz,整体如下:
void Loadcontact(Contact* pc)
{
FILE* tmp = fopen("Contact_Plus.txt", "rb");
if (tmp == NULL)
{
printf("%s", strerror(errno));
return;
}
else
{
Peo p;
int i = 0;
//fread的返回值为读到的数据个数
while (fread(&p, sizeof(Peo), 1, tmp))
{
checkdata(pc);
pc->data[i] = p;
pc->sz++;
i++;
}
fclose(tmp);
tmp = NULL;
}
}
void Initcontact(Contact* pc)
{
pc->sz = 0;
Peo* tmp = (Peo*)calloc(strat_sz, sizeof(Peo) * strat_sz);
if (tmp == NULL)
{
perror("calloc");
}
else
{
pc->data = tmp;
pc->capacity = strat_sz;
}
//加载信息到通讯录
Loadcontact(pc);
}
void checkdata(Contact* pc)
{
if (pc->sz == pc->capacity)
{
Peo* tmp = (Peo*)realloc(pc->data, sizeof(Peo) * (pc->capacity + inc_sz));
if (tmp == NULL)
{
perror("realloc");
return;
}
printf("扩容成功!\n");
pc->data = tmp;
pc->capacity += inc_sz;
}
}
3.增添Savecontact函数
在对通讯录编辑完成后,我们将数据写入到我们的文件中,保存到磁盘中,这里我给出两个写法,一个是使用二进制形式对文件打开,使用fwrite进行写入,或者使用只写形式打开,使用fprintf函数进行格式化写入,如下:
void Savecontact1(Contact* pc)
{
FILE* tmp =fopen("Contact_Plus.txt", "wb");
if (tmp == NULL)
{
perror("fopen");
}
else
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fwrite(pc->data, sizeof(Peo), 1, tmp);
}
fclose(tmp);
tmp = NULL;
}
}
void Savecontact2(Contact* pc)
{
FILE* tmp = fopen("Contact_Plus.txt", "w");
if (tmp == NULL)
{
perror("fopen");
return;
}
else
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
fprintf(tmp ,"%s %s %s %s", pc->data[i].name, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
}
}
fclose(tmp);
tmp = NULL;
}
4.增添Destroycontact函数
使用那么多动态开辟指针,我们需要使用free释放,然后主动置为空指针,养成这样的好习惯,可以避免内存泄露和野指针的引用:
void Destroycontact(Contact* pc)
{
pc->sz = 0;
pc->capacity = 0;
free(pc->data);
pc->data = NULL;
}
5.contact.h的总结
将各个函数都在头文件中进行声明,以及结构体的定义,这里我们一并给出:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<windows.h>
#include<time.h>
#define strat_sz 3
#define inc_sz 2
#define MAX 100
typedef struct peoinfo
{
char name[20];
char sex[10];
char tele[13];
char addr[40];
}Peo;
//typedef struct contact
//{
// Peo data[MAX];
// int sz;
//}Contact;
typedef struct contact
{
Peo* data;
int sz;
int capacity;
}Contact;
void Initcontact(Contact* pc);
void Addcontact(Contact* pc);
void Delcontact(Contact* pc);
void Searchcontact(Contact* pc);
void Modifycontact(Contact* pc);
void Showcontact(Contact* pc);
void checkdata(Contact* pc);
void Destroycontact(Contact* pc);
void Savecontact1(Contact* pc);
void Savecontact2(Contact* pc);
通讯录程序到此结束,谢谢读者们的支持!
标签:sz,printf,pc,Contact,通讯录,经典,编写,data From: https://blog.csdn.net/Dai_renwen/article/details/143866044