test.c
define _CRT_SECURE_NO_WARNINGS
include "contact.h"
void menu()
{
printf("\n");
printf("1.add 2.del \n");
printf("3.seach 4.modify \n");
printf("5.show 6.sort \n");
printf("0.exit \n");
printf("****\n");
}
int main()
{
int input = 0; //不能把input放进do里
PeoInfo data[100]; //说明通讯录的数据总数
int count = 0; //用于记录当前通讯录的个数
Contact con; //创建一个通讯录
InitContact(&con); //初始化通讯录,把con的地址作为形参
do
{
menu();
printf("请选择:> ");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con); //实现向通讯录中添加数据
break;
case 2:
DelContact(&con);
break;
case 3:
SeachContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 6:
SortContact(&con);
break;
case 0:
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
contact.c
define _CRT_SECURE_NO_WARNINGS
include "contact.h"
//静态版本初始化通讯录
//void InitContact(Contact* pc)
//{
// assert(pc); //断言防止pc为空指针
// pc->count = 0; //pc指向的count等于0,因为刚创建的通讯录什么都没有
// memset(pc->data, 0, sizeof(pc->data)); //用memset内存设置函数把一段连续的内存空间设置为0
//}
//动态版本通讯录
int InitContact(Contact* pc)
{
assert(pc);
pc->count = 0;
pc->data = (PeoInfo*)malloc(3, sizeof(PeoInfo));//用malloc函数申请3段空间每段空间大小就是在通讯录中存放一个联系人所需要的空间大小
//malloc函数的3表示要申请的空间个数,sizeof(PeoInfo)表示申请的每个空间的大小
if(pc->data == NULL)
{
printf("%s\n",strerror(errno));
return 1;
}
pc->pacacity = 3;//如果pc->data返回的不是空指针说明内存申请成功,把容量置3
return 0;
}
//静态版本添加联系人到通讯录
void AddContact(Contact* pc)
{
assert(pc);
if (pc->count == MAX)
{
printf("人数已满,无法添加\n");
return;
}
else
{
printf("请输入名字:>\n");
scanf("%s", pc->data[pc->count].name);
//pc->data[pc->count].name 表示要将读取的字符串存储在通讯录中的一个联系人的名称字段中。
//pc 是指向 Contact 结构体的指针。
// data 是 Contact 结构体中的一个成员,它是一个数组,用于存储联系人的信息。
//pc->count 是当前通讯录中已经存在的联系人数量。
// .name 是联系人结构体中的一个成员,表示联系人的名称字段。
// 因此,这行代码的作用是从用户输入中读取一个字符串,并将其存储在通讯录中的一个联系人的名称字段中。
printf("请输入年龄:>\n");
scanf("%d", &(pc->data[pc->count].age)); //因为这里打印的是一个整形
printf("请输入性别:>\n");
scanf("%s", pc->data[pc->count].sex);
printf("请输入电话:>\n");
scanf("%s", pc->data[pc->count].tele);
printf("请输入地址:>\n");
scanf("%s", pc->data[pc->count].addr);
pc->count++; //添加联系人到通讯录后通讯录的实际联系人要加1;
}
}
//动态版本添加联系人到通讯录
void AddContact(Contact* pc)
{
assert(pc);
if (pc->count == pc->capacity)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
//判断实际人数是否满容量如果满就扩容
if(ptr == NULL)
{
printf("AddContact::%s\n", strerror(error));//有可能扩容失败,判断ptr接收到的是否为空指针若为空则输出错误信息
return;
}
else
{
pc->data = ptr;
pc->capacity += INC_SZ;
}
}
printf("请输入名字:>\n");
scanf("%s", pc->data[pc->count].name);
//pc->data[pc->count].name 表示要将读取的字符串存储在通讯录中的一个联系人的名称字段中。
//pc 是指向 Contact 结构体的指针。
// data 是 Contact 结构体中的一个成员,它是一个数组,用于存储联系人的信息。
//pc->count 是当前通讯录中已经存在的联系人数量。
// .name 是联系人结构体中的一个成员,表示联系人的名称字段。
// 因此,这行代码的作用是从用户输入中读取一个字符串,并将其存储在通讯录中的一个联系人的名称字段中。
printf("请输入年龄:>\n");
scanf("%d", &(pc->data[pc->count].age)); //因为这里打印的是一个整形
printf("请输入性别:>\n");
scanf("%s", pc->data[pc->count].sex);
printf("请输入电话:>\n");
scanf("%s", pc->data[pc->count].tele);
printf("请输入地址:>\n");
scanf("%s", pc->data[pc->count].addr);
pc->count++; //添加联系人到通讯录后通讯录的实际联系人要加1;
}
//打印通讯录
void ShowContact(const Contact* pc)
{
assert(pc);
int i = 0;
printf("%-18s\t %-2s\t %-4s\t %-11s\t %-29s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->count; i++)
{
printf("%-20s\t%-6d\t%-7s\t%-14s\t%-30s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
int FindName(Contact* pc, char name[])
{
assert(pc);
int i = 0;
for (i = 0; i < pc->count; i++)
{
if (strcmp(pc->data[i].name, name) == 0) //等于0说明它们相等
{
return i; //返回i是为了方便记录是第几个联系人
}
}
return -1; //找不到就返回-1
}
//删除通讯录指定联系人 要找到了才能删除
void DelContact(Contact* pc)
{
assert(pc);
char name[MAX_NAME] = { 0 };
printf("请输入要删除的联系人的名字:> ");
scanf("%s", name);
int ret = FindName(pc, name);
int i = 0;
if (ret == -1)
{
printf("找不到要删除的联系人\n");
return;
}
for (i = ret; i < pc->count; i++) //这段代码的作用是将被删除联系人后面的联系人向前移动一个位置,
//然后将通讯录中的联系人数量减一。
{
pc->data[i] = pc->data[i + 1];
}
pc->count--; //这样做的目的是确保通讯录的联系人数量与实际的联系人数量保持一致,
//避免出现无效的联系人信息或访问越界的情况。实际的联系人就只有pc->count个这么多
printf("删除成功\n");
}
//查找联系人
void SeachContact(Contact* pc)
{
assert(pc);
char name[MAX_NAME] = { 0 };
printf("请输入要查找联系人的名字:> ");
scanf("%s", name);
//查找
int ret = FindName(pc, name);
if (ret == -1)
{
printf("要查找的联系人不存在\n");
return;
}
//打印
printf("%-18s\t %-2s\t %-4s\t %-11s\t %-29s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-6d\t%-7s\t%-14s\t%-30s\n", pc->data[ret].name,
pc->data[ret].age,
pc->data[ret].sex,
pc->data[ret].tele,
pc->data[ret].addr);
}
//修改指定联系人
void ModifyContact(Contact* pc)
{
assert(pc);
char name[MAX_NAME] = { 0 };
printf("请输入要修改联系人的名字:> ");
scanf("%s", name);
//查找
int ret = FindName(pc, name);
if (ret == -1)
{
printf("找不到相应的的联系人不存在\n");
return;
}
printf("要修改的联系人已找到现在开始修改\n");
//修改
printf("请输入名字:>\n");
scanf("%s", pc->data[ret].name);
printf("请输入年龄:>\n");
scanf("%d", &(pc->data[ret].age));
printf("请输入性别:>\n");
scanf("%s", pc->data[ret].sex);
printf("请输入电话:>\n");
scanf("%s", pc->data[ret].tele);
printf("请输入地址:>\n");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
}
int CmpName(void* e1, void* e2)
{
return strcmp(((PeoInfo)e1)->name, ((PeoInfo)e2)->name);
}
//首字母大小排序
void SortContact(Contact* pc)
{
assert(pc);
//void qsort (void* base, size_t num, size_t size, int (compar)(const void, const void*));
qsort(pc->data, pc->count, sizeof(PeoInfo), CmpName);
printf("排序成功\n");
}
contact.h
pragma once
include <stdio.h>
include <assert.h>
include <string.h>
include <stdlib.h>
//宏定义后面不能加分号和注释
define INC_SZ 2
define MAX 100
define MAX_NAME 20
define MAX_SEX 10
define MAX_TELE 12
define MAX_ADDR 30
//类型的声明,声明一个结构体,里面放有通讯录的数据类型和大小,也就是人的信息
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
//通讯录
//静态版本
typedef struct Contact
{
PeoInfo data[MAX]; //用于存放人的信息
int count; //用于记录当前通讯录的实际人数 定义结构体不能在结构体内部直接初始化变量
}Contact;
//动态版本初始化通讯录,通讯录默认存放3个人的信息,若空间不够了每次增加2个人的空间
//typedef struct Contact
{
PeoInfo* data;//用于存放人的信息
int count;//用于记录通讯录实际联系人的个数
int capacity;//用于记录当前通讯录的容量,方便判断通讯录是否满容量,若满了就扩容
}
//初始化通讯录
void InitContact(Contact* pc);
//添加联系人到通讯录
void AddContact(Contact* pc);
//动态版本添加联系人到通讯录
void AddContact(Contact* pc);
//打印通讯录
void ShowContact(const Contact* pc);
//删除通讯录的联系人
void DelContact(Contact* pc);
//查找联系人
void SeachContact(Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//首字母大小排序
void SortContact(Contact* pc);
课堂杂记
malloc函数:
void* malloc (size_t size);
1.这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
2.如果开辟成功,则返回一个指向开辟好空间的指针。
3.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
4.返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
5.如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器
free函数:
void free(void* ptr);
free函数用来释放动态开辟的内存。
1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
2.如果参数 ptr 是NULL指针,则函数什么事都不做。
malloc和free都声明在 stdlib.h 头文件中
calloc函数:
void* calloc (size_t num, size_t size);
1.函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
2.与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
realloc函数:
void* realloc (void* ptr, size_t size);
1.ptr 是要调整的内存地址
2.size 调整之后新大小
3.返回值为调整之后的内存起始位置。
4.这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
realloc函数调整内存有两种情况:
1.原有空间后面有足够的未被占用的空间,直接在原有空间后面调整就行;
2.原有空间后面没有足够的空间,此时realloc函数会在一个有足够的未被占用的空间处开辟出一段连续的空间,并把原有空间的内容复制到新的空间,原有的空间会被释放;