目录
1. 动态版通讯录和普通通讯录的区别
首先,要能写出动态的通讯录,我们必须明确动态内存分配下的通讯录究竟和普通的通讯录有哪些区别?
1. 普通的通讯录我们首先会初始化一个我们想要的值作为通讯录的总人数;这个值一旦定义,就意味着我们编写的这个通讯录总人数就会有上限;比方说我们的qq群、微信群的人数上限;
2. 普通的通讯录我们初始化时,定义两个成员变量,一个通讯录能够容纳的总人数,我们即为MAX;另一个是我们已经加上的好友个数size;动态版通讯录因为不存在总人数,所以我们需要定义一个容纳量,当超过这个容纳量,我们就进行动态开辟内存;
3. 动态开辟的内存在离开通讯录时需要进行内存释放;
本动态通讯录我们设置容纳量为3人,可以说一旦加的好友人数超过3人,通过realloc函数动态开辟内存至5人,每次容量满以后,就动态开辟2人的内存,以此进行循环;
2. 通讯录模版
2.1 主函数(main)
int main()
{
int input = 0;//通讯录中当前有几个元素,也可以说是通讯录所要实现的功能有多少,增加、删除
//修改等,每个功能对应一个数字,通讯录首先最需要考虑的就是用户的需求,加好友或者 ……
struct Contact con;//创建通讯录,通过结构体的成员变量来表达,增加了代码的可读性
InitContact(&con);//初始化通讯录
do//do while 循环保证不管程序正确与否的前提下,我先运行一次菜单,输入用户的需求
{
menu();
printf("请选择:>");
scanf("%d", &input);//用户输入需要,加好友、删好友、修改好友情况等等
switch (input)//switch case 进行判断,我要进行哪种功能,对应哪种需要,进入哪种情况
{
//添加好友
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;
//将好友进行分类
case SORT:
SortContact(&con);
break;
//清空通讯录
case CLEAR:
ClearContact(&con);
break;
//退出
case EXIT:
DestroyContact(&con);
printf("退出通讯录\n");
break;
//switch选择的情况,也可以说是功能不在菜单里
default:
printf("选择错误\n");
break;
}
} while (input);//只要通讯录的功能在case 里有显示,就进行do while 循环;
return 0;
}//因为所有case的功能函数都是传址调用,所以对应的外部函数都需要用指针来接收传过去的地址
2.2 通讯录菜单
void menu()
{
printf("************************************\n");
printf("****** 1. add 2. del ******\n");
printf("****** 3. search 4. modify ******\n");
printf("****** 5. show 6. sort ******\n");
printf("****** 7. clear 0. exit ******\n");
printf("************************************\n");
}
// 主函数调用menu函数,menu()放在主函数do while循环体中首行,保证程序一旦运行,首先出现的就是供用
// 户选择的菜单,菜单上对应增加、删除、查找、修改、显示、分类、离开通讯录等功能,每个功能对应每
// 个不同的代码1 2 3 4 5 6 0, 为了防止修改数字而影响主函数case的使用,可以进行宏定义或者枚举, // 本程序 我们采用的是枚举
2.3 通讯录枚举类型的声明
enum Option
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
SORT//6
CLEAR//7
};
//通过枚举来进行通讯录功能的声明,可以大大增加代码的可读性
2.4 初始化通讯录
struct Peopleinformation//个人信息的结构体
{
char name[20];
int age;
char sex[5];
char tel[12];
char address[20];
};
struct Contact//结构体嵌套
{
struct Peopleinformation *data;//动态化的通讯录用结构体变量指针
int size;//通讯录已经加上的好友人数
int capacity;//通讯录的容纳量
};
void InitContact(struct Contact* ps)//初始化通讯录
{
ps->data = (struct Peopleinformation*)malloc(3 * sizeof(struct Peopleinformation));//malloc动态开辟3个结构体大小的空间
if (ps->data == NULL)//判断是否开辟空间成功
{
return;//初始化函数的返回类型是void,所以直接return;就行
}
ps->capacity = 3;
ps->size = 0;
}
2.5 增加好友的功能函数
void AddContact(struct Contact* ps)//添加通讯录
{
if (ps->size == ps->capacity)//如果已经加上的好友个数等于通讯录的最大容量,本程序设置是3
{
struct Peopleinformation* ptr = realloc(ps->data, (ps->capacity + 2)*sizeof(struct Peopleinformation));//realloc空间拓展到5个数组大小
if (ptr != NULL)//判断是否拓展成功
{
ps->data = ptr;
ps->capacity += 2;
printf("增容成功\n");
}
}
printf("请输入名字:>");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄:>");
scanf("%d", &(ps->data[ps->size].age));
printf("请输入性别:>");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话:>");
scanf("%s", ps->data[ps->size].tel);
printf("请输入地址:>");
scanf("%s", ps->data[ps->size].address);
ps->size++;
printf("添加成功\n");
}//在此->和[]的优先级是一样的,但是需要注意,还是要从左到右进行计算,如果直接进行data[ps->size],操作系统根本不知道data是什么,因为data是定义在
//嵌套结构体里面的,所以还是要从左到右进行计算,ps->data拿到整个数组,整个数组是可以用数组名表示的,知道数组名就可以通过 . 进行结构体成员的访问
2.6 显示好友的功能函数
static int FindByName(struct Contact* ps, char name[20])//static在此处的意义为每循环一次外部函数,上次查找过得名字不会再次被查找
{
int i = 0;
for (i = 0; i < ps->size; i++)//循环的范围时通讯录中已经添加的有效人员的个数
{
if (0 == strcmp(ps->data[i].name, name))//字符串比较函数,==0 表示相等的情况
{//if判断语句的意思是结构体变量数组data中的名字和要查找的名字相同,我就返回该名字对应数组的下角标
return i;
}
}
return -1;//返回-1,表示两个不相等,也可以说是没有找到该人
}
void ShowContact(const struct Contact* ps)//const 它限定一个变量不允许被改变,产生静态作用。
{
if (ps->size == 0)
{
printf("通讯录为空\n");//ps->size表示通讯录中的有效人数
}
else//通讯录目前是有好友的,显示出来也可以说是将通讯录中好友的信息打印出来
{
int i = 0;
printf("%-5s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < ps->size; i++)//循环的次数为通讯录中具有有效信息的好友个数
{
printf("%-5s\t %-4d\t %-5s\t %-12s\t %-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tel,
ps->data[i].address);
}
}//for循环打印结构体信息
}
2.7 删除好友的功能函数
static int FindByName(struct Contact* ps, char name[20])//static在此处的意义为每循环一次外部函数,上次查找过得名字不会再次被查找
{
int i = 0;
for (i = 0; i < ps->size; i++)//循环的范围时通讯录中已经添加的有效人员的个数
{
if (0 == strcmp(ps->data[i].name, name))//字符串比较函数,==0 表示相等的情况
{//if判断语句的意思是结构体变量数组data中的名字和要查找的名字相同,我就返回该名字对应数组的下角标
return i;
}
}
return -1;//返回-1,表示两个不相等,也可以说是没有找到该人
}
void DelContact(struct Contact* ps)
{
char name[20];
printf("请输入要删除人的名字:>");
scanf("%s", name);
int pos = FindByName(ps, name);//同样的,先通过外部函数找到所要找的人
if (pos == -1)
{
printf("要删除的人不存在\n");//无
}
else
{
int j = 0;
for (j = 0; j < ps->size - 1; j++)//之所以j < ps->size - 1;是因为ps->size是已经添加的有效人数
//该代码的意思是将我举例子0 1 2 3 5 6 7 8 9 9中的最后一个9删除,打印时不访问最后一个元素,相当于把最后一个9删除了
{
ps->data[pos] = ps->data[j + 1];
//该循环的意思是:假如数组中的成员为0 1 2 3 4 5 6 7 8 9,我想要删除4,该代码的意思是我用5覆盖4,6覆盖5
//7覆盖6,8覆盖7,9覆盖8,这样数组会变成0 1 2 3 5 6 7 8 9 9,成功达到了删除4的目的,并且没有改变原数组中其他元素的顺序
}
ps->size--;//通讯录有效人数随循环次数--
printf("删除成功\n");
}
}
2.8 查找好友的功能函数
static int FindByName(struct Contact* ps, char name[20])//static在此处的意义为每循环一次外部函数,上次查找过得名字不会再次被查找
{
int i = 0;
for (i = 0; i < ps->size; i++)//循环的范围时通讯录中已经添加的有效人员的个数
{
if (0 == strcmp(ps->data[i].name, name))//字符串比较函数,==0 表示相等的情况
{//if判断语句的意思是结构体变量数组data中的名字和要查找的名字相同,我就返回该名字对应数组的下角标
return i;
}
}
return -1;//返回-1,表示两个不相等,也可以说是没有找到该人
}
void SearchContact(struct Contact* ps)//结构体指针接收传址调用的地址
{
char name[20];
printf("请输入要查找人的名字\n");
scanf("%s", name);
int pos = FindByName(ps, name);//通过外部函数的返回值查找到想要找到的人
if (pos == -1)
{
printf("要查找的人不存在\n");//无
}
else
{//找到了,打印出该人的信息
printf("%-5s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-5s\t %-4d\t %-5s\t %-12s\t %-20s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tel,
ps->data[pos].address);
}//%s 打印字符,%d打印整型,%-5s\t表示对齐
//"\t"在C语言里表示水平制表(HT) (跳到下一个TAB位置)
}
2.9 修改好友的功能函数
static int FindByName(struct Contact* ps, char name[20])//static在此处的意义为每循环一次外部函数,上次查找过得名字不会再次被查找
{
int i = 0;
for (i = 0; i < ps->size; i++)//循环的范围时通讯录中已经添加的有效人员的个数
{
if (0 == strcmp(ps->data[i].name, name))//字符串比较函数,==0 表示相等的情况
{//if判断语句的意思是结构体变量数组data中的名字和要查找的名字相同,我就返回该名字对应数组的下角标
return i;
}
}
return -1;//返回-1,表示两个不相等,也可以说是没有找到该人
}
void ModifyContact(struct Contact* ps)//传址调用通过结构体指针来接收
{
char name[20];//通过名字找到所要修改人的信息
printf("请输入要修改人的名字:>");
scanf("%s", name);
int pos = FindByName(ps, name);//定义外部函数
if (pos == -1)
{
printf("要修改人的信息不存在\n");//找不到该人
}
else
{//如果找到该人,则通过外部函数的返回值找到该名字在数组中对应的下角标,并且修改该人的信息
printf("请输入名字:>");
scanf("%s", ps->data[pos].name);
printf("请输入年龄:>");
scanf("%d", &(ps->data[pos].age));
printf("请输入性别:>");
scanf("%s", ps->data[pos].sex);
printf("请输入电话:>");
scanf("%s", ps->data[pos].tel);
printf("请输入地址:>");
scanf("%s", ps->data[pos].address);
printf("修改完成\n");
}
}
2.10 排序好友的功能函数
qsort冒泡排序函数,函数定义:void qsort(void* base,size_t num,size_t size,int(*compar)(const void*,const void*))
void* base:首元素的地址;size_t num:待排序的数据个数;size_t size:所占字节的大小;int(*compar)(const void*,const void*):比较两个数据的大小;
enum Option_qsort
{
exit_sort,//0
name,//1
age,//2
sex,//3
address,//4
tel//5
};
void menu_qsort()
{
printf("************************************\n");
printf("****** 1. name 2. age ******\n");
printf("****** 3. sex 4. address ******\n");
printf("****** 5. tel 0. exit_sort ******\n");
printf("************************************\n");
}
int compare_tel(const void*e1, const void*e2)
{
return strcmp(((struct Peopleinformation*)e1)->tel, ((struct Peopleinformation*)e2)->tel);
}
int compare_address(const void*e1, const void*e2)
{
return strcmp(((struct Peopleinformation*)e1)->address, ((struct Peopleinformation*)e2)->address);
}
int compare_sex(const void*e1, const void*e2)
{
return strcmp(((struct Peopleinformation*)e1)->sex, ((struct Peopleinformation*)e2)->sex);
}
int compare_age(const void*e1, const void*e2)
{
return strcmp(((struct Peopleinformation*)e1)->age, ((struct Peopleinformation*)e2)->age);
}
int compare_name(const void*e1, const void*e2)
{
return strcmp(((struct Peopleinformation*)e1)->name, ((struct Peopleinformation*)e2)->name);
}
void print(struct Contact* ps)
{
int i = 0;
printf("%-5s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < ps->size; i++)
{
printf("%-5s\t %-4d\t %-5s\t %-12s\t %-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tel,
ps->data[i].address);
}
}
void SortContact(struct Contact* ps)
{//通讯录排序仿照main函数来进行;
int input = 0;
do
{
menu_qsort();//打印排序的菜单方式
printf("请选择排序的方式:>");
scanf("%d", &input);
switch (input)
{
case name://按照名字排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_name);
print(ps);//打印排序以后的个人信息
break;
case age://按照年龄排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_age);
print(ps);
break;
case sex://按照性别排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_sex);
print(ps);
break;
case address://按照住址排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_address);
print(ps);
break;
case tel://按照电话排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_tel);
print(ps);
break;
case exit_sort://离开排序功能
printf("通讯录排序功能退出成功\n");
printf("\n");
break;
default:
printf("选择排序的方式错误,请重新选择:>");
break;
}
} while (input);
}
2.11 清除好友的功能函数
void ClearContact(struct Contact* ps)
{
(ps->size) = 0;
memset(ps->data, 0, sizeof(ps->data));
printf("清空成功\n");
printf("\n");
}//清空所有联系人的函数很好理解,用memset内存设置函数,将数组data设置为0,通讯录中有效人数设置为0
2.12 摧毁通讯录的功能函数
void DestroyContact(struct Contact* ps)
{
free(ps->data);//释放空间
ps->data = NULL;//将指针赋值为NULL
ps->size = 0;//通讯录上已经添加的好友清0
ps->capacity = 0;//通讯录的可容纳的容量清0
}
3. 总的程序代码
本程序未采用模块化编程,可能会给阅读带来一定的困难;
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <stddef.h>
#include <errno.h>
#include <stdlib.h>
//#define MAX 1000 //动态版通讯录是不存在总人数的,通讯录初始化能放3个好友,满3个拓展2个,依次循环;
void menu()//功能菜单
{
printf("************************************\n");
printf("****** 1. add 2. del ******\n");
printf("****** 3. search 4. modify ******\n");
printf("****** 5. show 6. sort ******\n");
printf("****** 7. clear 0. exit ******\n");
printf("************************************\n");
}
struct Peopleinformation//个人信息的结构体
{
char name[20];
int age;
char sex[5];
char tel[12];
char address[20];
};
struct Contact//结构体嵌套
{
struct Peopleinformation *data;//动态化的通讯录用结构体变量指针
int size;//通讯录已经加上的好友人数
int capacity;//通讯录的容纳量
};
enum Option_qsort//排序的枚举类型
{
exit_sort,//0
name,//1
age,//2
sex,//3
address,//4
tel//5
};
void menu_qsort()//排序依据的菜单
{
printf("************************************\n");
printf("****** 1. name 2. age ******\n");
printf("****** 3. sex 4. address ******\n");
printf("****** 5. tel 0. exit_sort ******\n");
printf("************************************\n");
}
int compare_tel(const void*e1, const void*e2)//按电话进行比较的外部函数
{
return strcmp(((struct Peopleinformation*)e1)->tel, ((struct Peopleinformation*)e2)->tel);
}
int compare_address(const void*e1, const void*e2)//按地址进行比较的外部函数
{
return strcmp(((struct Peopleinformation*)e1)->address, ((struct Peopleinformation*)e2)->address);
}
int compare_sex(const void*e1, const void*e2)//按性别进行比较的外部函数
{
return strcmp(((struct Peopleinformation*)e1)->sex, ((struct Peopleinformation*)e2)->sex);
}
int compare_age(const void*e1, const void*e2)//按年龄进行比较的外部函数
{
return ((struct Peopleinformation*)e1)->age- ((struct Peopleinformation*)e2)->age;
}
int compare_name(const void*e1, const void*e2)//按名字进行比较的外部函数
{
return strcmp(((struct Peopleinformation*)e1)->name, ((struct Peopleinformation*)e2)->name);
}
void print(struct Contact* ps)//打印排序之后的信息
{
int i = 0;
printf("%-5s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < ps->size; i++)
{
printf("%-5s\t %-4d\t %-5s\t %-12s\t %-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tel,
ps->data[i].address);
}
}
void SortContact(struct Contact* ps)//排序功能
{//通讯录排序仿照main函数来进行;
int input = 0;
do
{
menu_qsort();//打印排序的菜单方式
printf("请选择排序的方式:>");
scanf("%d", &input);
switch (input)
{
case name://按照名字排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_name);
print(ps);//打印排序以后的个人信息
break;
case age://按照年龄排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_age);
print(ps);
break;
case sex://按照性别排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_sex);
print(ps);
break;
case address://按照住址排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_address);
print(ps);
break;
case tel://按照电话排序
qsort(ps->data, ps->size, sizeof(ps->data[0]), compare_tel);
print(ps);
break;
case exit_sort://离开排序功能
printf("通讯录排序功能退出成功\n");
printf("\n");
break;
default:
printf("选择排序的方式错误,请重新选择:>");
break;
}
} while (input);
}
void InitContact(struct Contact* ps)//初始化通讯录
{
ps->data = (struct Peopleinformation*)malloc(3 * sizeof(struct Peopleinformation));//malloc动态开辟3个结构体大小的空间
if (ps->data == NULL)//判断是否开辟空间成功
{
return;//初始化函数的返回类型是void,所以直接return;就行
}
ps->capacity = 3;
ps->size = 0;
}
void AddContact(struct Contact* ps)//添加通讯录
{
if (ps->size == ps->capacity)//如果已经加上的好友个数等于通讯录的最大容量,本程序设置是3
{
struct Peopleinformation* ptr = realloc(ps->data, (ps->capacity + 2)*sizeof(struct Peopleinformation));//realloc空间拓展到5个数组大小
if (ptr != NULL)//判断是否拓展成功
{
ps->data = ptr;
ps->capacity += 2;
printf("增容成功\n");
}
}
printf("请输入名字:>");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄:>");
scanf("%d", &(ps->data[ps->size].age));
printf("请输入性别:>");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话:>");
scanf("%s", ps->data[ps->size].tel);
printf("请输入地址:>");
scanf("%s", ps->data[ps->size].address);
ps->size++;
printf("添加成功\n");
}
void ShowContact(const struct Contact* ps)//显示通讯录
{
if (ps->size == 0)
{
printf("通讯录为空\n");
}
else
{
int i = 0;
printf("%-5s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < ps->size; i++)
{
printf("%-5s\t %-4d\t %-5s\t %-12s\t %-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tel,
ps->data[i].address);
}
}
}
static int FindByName(struct Contact* ps, char name[20])//在已经存在的好友中找到某个人
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (0 == strcmp(ps->data[i].name, name))
{
return i;
}
}
return -1;
}
void DelContact(struct Contact* ps)//删除某位好友
{
char name[20];
printf("请输入要删除人的名字:>");
scanf("%s", name);
int pos = FindByName(ps, name);
if (pos == -1)
{
printf("要删除的人不存在\n");
}
else
{
int j = 0;
for (j = 0; j < ps->size - 1; j++)
{
ps->data[pos] = ps->data[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}
void SearchContact(struct Contact* ps)//查找某位好友
{
char name[20];
printf("请输入要查找人的名字\n");
scanf("%s", name);
int pos = FindByName(ps, name);
if (pos == -1)
{
printf("要查找的人不存在\n");
}
else
{
printf("%-5s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-5s\t %-4d\t %-5s\t %-12s\t %-20s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tel,
ps->data[pos].address);
}
}
void ModifyContact(struct Contact* ps)//修改某位好友的信息
{
char name[20];
printf("请输入要修改人的名字:>");
scanf("%s", name);
int pos = FindByName(ps, name);
if (pos == -1)
{
printf("要修改人的信息不存在\n");
}
else
{
printf("请输入名字:>");
scanf("%s", ps->data[pos].name);
printf("请输入年龄:>");
scanf("%d", &(ps->data[pos].age));
printf("请输入性别:>");
scanf("%s", ps->data[pos].sex);
printf("请输入电话:>");
scanf("%s", ps->data[pos].tel);
printf("请输入地址:>");
scanf("%s", ps->data[pos].address);
printf("修改完成\n");
}
}
void ClearContact(struct Contact* ps)
{
(ps->size) = 0;
memset(ps->data, 0, sizeof(ps->data));
printf("清空成功\n");
printf("\n");
}//清空所有联系人的函数很好理解,用memset内存设置函数,将数组data设置为0,通讯录中有效人数设置为0
void DestroyContact(struct Contact* ps)
{
free(ps->data);//释放空间
ps->data = NULL;//将指针赋值为NULL
ps->size = 0;//通讯录上已经添加的好友清0
ps->capacity = 0;//通讯录的可容纳的容量清0
}
enum Option//通讯录功能的枚举类型
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SHOW,//5
SORT,//6
CLEAR//7
};
int main()//主函数
{
int input = 0;
struct Contact con;
InitContact(&con);
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
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;
case SORT:
SortContact(&con);
break;
case CLEAR:
ClearContact(&con);
break;
case EXIT:
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}