文章目录
- 🏳🌈前言
- 🔊项目需求
- 📝项目知识点包含
- 🧩项目框架
- 🔑框架拆解分析
- 📚Struct_Book1.h头文件分析
- 📚Struct_Book1.c源文件分析
- 📚test_book.c源文件分析
- 🎥效果演示
- ✨完整代码
🏳🌈前言
第一个版本是静态的通讯录,就是用的结构体数组来存储的信息,这样会导致空间不够或者浪费的缺点;因此动态版本就解决这样的问题,减少此情况的发生。接下来讲解中阶版本的练手小项目——《动态通讯录管理系统》
🔊项目需求
动态版本的通讯录也是解决联系人的增、删、查、改、显示、排序的操作。如果看了我写的静态版本的通讯录文章的,其实结构和上一篇文章提到的差不多,只是改变了申请存储空间的方式。通过动态开辟内存来存储信息。
📝项目知识点包含
此动态版本的通讯录,主要是C语言中的动态内存管理知识、结构体、指针操作等,加上C语言的基础知识,接下来就详细描述该项目。👇👇👇👇
🧩项目框架
🔑框架拆解分析
接下来分别对
Struct_Book1.h
头文件、Struct_Book1.c
源文件、test_book.c
源文件作分析:👇
📚Struct_Book1.h头文件分析
代码展示
#define DEFAULT_SIZE 3
#define MAX_NAME 12
#define MAX_AGE 3
#define MAX_SEX 4
#define MAX_TELE 12
#define MAX_ADDRESS 40
//枚举功能
enum address_book
{
Exit,
Add,
Delete,
Find,
Modify,
Sort,
Show
};
//信息结构体
typedef struct person_information
{
char name[MAX_NAME];
char age[MAX_AGE];
char sex[MAX_SEX];
char tel[MAX_TELE];
char address[MAX_ADDRESS];
}PenInfo;
typedef struct Contact
{
PenInfo* data;//和静态的对比,就是此处改变了,用结构体指针来表示
int capacity_size;//动态空间容量
int number_person;//实际的人的数量
}Con;
void init_contact(Con* ptr);//初始化函数
void addition_information(Con* ptr);//增加函数
void delete_information(Con* ptr);//删除函数
void find_information(Con* ptr);//查找函数
void modify_information(Con* ptr);//修改函数
void sort_information(Con* ptr);//排序函数
void show_information(const Con* ptr);//显示函数
void destroy_contact(Con* ptr);//释放函数
📚Struct_Book1.c源文件分析
直接代码展示出来:主要展示动态申请的代码块
//初始化函数[动态开辟]
void init_contact(Con* ptr)
{
ptr->data = (PenInfo*)malloc(DEFAULT_SIZE*sizeof(PenInfo));
//判断
if (ptr->data == NULL)
{
perror("malloc");
return;
}
ptr->capacity_size = DEFAULT_SIZE;//初始的空间大小
ptr->number_person = 0;
}
//扩容检测
void check_capacity(Con* ps)
{
assert(ps);
if (ps->capacity_size == ps->number_person)
{
//扩容
PenInfo* new_capacity = (PenInfo*)realloc(ps->data,
(ps->capacity_size+2) * sizeof(PenInfo));//可以修改为成倍数增长
//判断,此处不能直接把开辟的空间,
//如果开辟失败会返回NULL,那么直接赋值给原来的空间,会把原来的内容全部删除,所以先判断是否申请成功
if (new_capacity == NULL)
{
printf("增容失败!\n");
return;
}
//申请成功
if (new_capacity != NULL)
{
ps->data = new_capacity;
ps->capacity_size += 2;//此处可以修改成倍数增长,
//pc->capacity*=2;//此处用倍数,那么在初始化的时候
//也要改为倍数增长
printf("增容成功!\n");
printf("现在容量:%d\n", ps->capacity_size);
}
}
}
📚test_book.c源文件分析
代码如下:没有具体说的,比较好理解
void test()
{
Con con;
init_contact(&con);
int input = 0;
while (1)
{
menu();
printf("请选择功能:\n");
scanf("%d", &input);
switch (input)
{
case Exit:
destroy_contact(&con);//主要的是要记住释放申请的空间
printf("欢迎下次使用!\n");
return;
case Add:
addition_information(&con);
break;
case Delete:
delete_information(&con);
break;
case Find:
find_information(&con);
break;
case Modify:
modify_information(&con);
break;
case Sort:
sort_information(&con);
break;
case Show:
show_information(&con);
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
_getch();
system("cls");
}
}
int main()
{
test();
return 0;
}
注意:释放申请的动态空间,把它还给计算机内存,“有借有还、再借不难”
🎥效果演示
✨完整代码
Struct_Book1.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<conio.h>
#include<Windows.h>
#define DEFAULT_SIZE 3
#define MAX_NAME 12
#define MAX_AGE 3
#define MAX_SEX 4
#define MAX_TELE 12
#define MAX_ADDRESS 40
//枚举功能
enum address_book
{
Exit,
Add,
Delete,
Find,
Modify,
Sort,
Show
};
//信息结构体
typedef struct person_information
{
char name[MAX_NAME];
char age[MAX_AGE];
char sex[MAX_SEX];
char tel[MAX_TELE];
char address[MAX_ADDRESS];
}PenInfo;
typedef struct Contact
{
PenInfo* data;
int capacity_size;
int number_person;
}Con;
void init_contact(Con* ptr);//初始化函数
void addition_information(Con* ptr);//增加函数
void delete_information(Con* ptr);//删除函数
void find_information(Con* ptr);//查找函数
void modify_information(Con* ptr);//修改函数
void sort_information(Con* ptr);//排序函数
void show_information(const Con* ptr);//显示函数
void destroy_contact(Con* ptr);//释放函数
Struct_Book1.c
#include "Struct_Book1.h"
//初始化函数[动态开辟]
void init_contact(Con* ptr)
{
ptr->data = (PenInfo*)malloc(DEFAULT_SIZE*sizeof(PenInfo));
//判断
if (ptr->data == NULL)
{
perror("malloc");
return;
}
ptr->capacity_size = DEFAULT_SIZE;
ptr->number_person = 0;
}
//扩容检测
void check_capacity(Con* ps)
{
assert(ps);
if (ps->capacity_size == ps->number_person)
{
//扩容
PenInfo* new_capacity = (PenInfo*)realloc(ps->data, (ps->capacity_size+2) * sizeof(PenInfo));//可以修改为成倍数增长
//判断
if (new_capacity == NULL)
{
printf("增容失败!\n");
return;
}
if (new_capacity != NULL)
{
ps->data = new_capacity;
ps->capacity_size += 2;//此处可以修改成倍数增长
printf("增容成功!\n");
printf("现在容量:%d\n", ps->capacity_size);
}
}
}
//增加函数
void addition_information(Con* ptr)
{
//增加之前必须判断是否指针为空和空间是否满
assert(ptr);//断言一下
//检查是否扩容
check_capacity(ptr);
//满足条件,添加数据
printf("------------------------\n");
printf("|请输入联系人对应的信息|\n");
printf("|信息录入中…… |\n");
printf("------------------------\n");
printf("姓名—>:");
scanf("%s", ptr->data[ptr->number_person].name);
printf("年龄—>:");
scanf("%s", ptr->data[ptr->number_person].age);
printf("性别—>:");
scanf("%s", ptr->data[ptr->number_person].sex);
printf("电话—>:");
scanf("%s", ptr->data[ptr->number_person].tel);
printf("地址—>:");
scanf("%s", ptr->data[ptr->number_person].address);
ptr->number_person++;
printf("添加成功\n");
}
//显示函数
void show_information(const Con* ptr)
{
assert(ptr);
if (ptr->number_person == 0)
{
printf("通讯录为空,无法显示!\n");
return;
}
printf("*************************************************************************************************\n");
printf("***************************** 通讯录信息 ***************************************\n");
printf("*************************************************************************************************\n");
printf("-------------------------------------------------------------------------------------------------\n");
printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n","姓名","年龄","性别","电话","地址");
for (int i = 0; i < ptr->number_person; i++)
{
printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n", ptr->data[i].name
, ptr->data[i].age
, ptr->data[i].sex
, ptr->data[i].tel
,ptr->data[i].address);
}
printf("-------------------------------------------------------------------------------------------------\n");
}
//只能在当前.c文件中使用
static int find(Con* ps,char* name1)
{
assert(ps);
for (int i = 0; i < ps->number_person; i++)
{
//此处不能这样比较
//if (ps->data[i].name == name1)
//{
// return i;//返回找到的位置
//}
if ((strcmp(ps->data[i].name, name1)) == 0)
{
return i;
}
}
return -1;
}
//删除函数
void delete_information(Con* ptr)
{
//断言
assert(ptr);
if (ptr->number_person == 0)
{
printf("通讯录为空,无法删除!\n");
return;
}
//找到对应要删除的联系人
char name[MAX_NAME];
printf("请输入要删除联系人的姓名:");
scanf("%s", name);
int ret=find(ptr,name);
if (ret == -1)
{
printf("没找到,无法删除!\n");
return;
}
//删除
for (int i = ret; i < ptr->number_person - 1; i++)
{
ptr->data[i] = ptr->data[i + 1];
}
//人数-1
ptr->number_person--;
printf("删除成功!\n");
show_information(ptr);
}
//查找函数
void find_information(Con* ptr)
{
assert(ptr);
if (ptr->number_person == 0)
{
printf("通讯录为空,无法查找!\n");
return;
}
char name1[MAX_NAME];
printf("请输入要查找联系人的姓名:");
scanf("%s", name1);
int ret = find(ptr,name1);
if (ret == -1)
{
printf("没找到!\n");
return;
}
printf("-------------------------------------------------------------------------------------------------\n");
printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n", "姓名", "年龄", "性别", "电话", "地址");
printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n", ptr->data[ret].name
, ptr->data[ret].age
, ptr->data[ret].sex
, ptr->data[ret].tel
, ptr->data[ret].address);
printf("-------------------------------------------------------------------------------------------------\n");
}
//修改函数
void modify_information(Con* ptr)
{
assert(ptr);
if (ptr->number_person == 0)
{
printf("通讯录为空,无法修改!\n");
return;
}
char name2[MAX_NAME];
printf("请输入要修改联系人的姓名:");
scanf("%s", name2);
int ret = 0;
ret = find(ptr,name2);
if (ret == -1)
{
printf("没找到!\n");
return;
}
printf("修改进行中…………\n");
printf("请输入新的姓名:");
scanf("%s", ptr->data[ret].name);
printf("请输入新的年龄:");
scanf("%s", ptr->data[ret].age);
printf("请输入新的性别:");
scanf("%s", ptr->data[ret].sex);
printf("请输入新的电话:");
scanf("%s", ptr->data[ret].tel);
printf("请输入新的地址:");
scanf("%s", ptr->data[ret].address);
printf("修改成功!\n");
show_information(ptr);
}
int compar_by_name(const void* e1, const void* e2)
{
return strcmp(((PenInfo*)e1)->name,((PenInfo*)e2)->name);
}
int compar_by_age(const void* e1, const void* e2)
{
return strcmp(((PenInfo*)e1)->age, ((PenInfo*)e2)->age);
}
//排序函数
void sort_information(Con* ptr)
{
assert(ptr);
if (ptr->number_person == 0)
{
printf("通讯录为空,无法排序!\n");
return;
}
printf("请选择排序方式:1、按照姓名排序,2、按照年龄排序。\n");
int select1= 0;
scanf("%d", &select1);
if (select1 == 1)
{
//qsort()用法
//void qsort (void* base, size_t num, size_t size,int (*compar)(const void*, const void*));
/*
base ——>Pointer to the first object of the array to be sorted, converted to a void*.
num ——>Number of elements in the array pointed to by base.size_t is an unsigned integral type.
size ——> Size in bytes of each element in the array.size_t is an unsigned integral type.
compar ——> Pointer to a function that compares two elements.This function is called repeatedly
by qsort to compare two elements.
It shall follow the following prototype:int compar (const void* p1, const void* p2);*/
qsort(ptr->data, ptr->number_person, sizeof(PenInfo), compar_by_name);
printf("排序成功!\n");
}
if (select1 == 2)
{
qsort(ptr->data, ptr->number_person, sizeof(PenInfo), compar_by_age);
printf("排序成功!\n");
}
}
//释放函数
void destroy_contact(Con* ptr)
{
free(ptr->data);
ptr->data = NULL;
}
test_book.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Struct_Book1.h"
void menu()
{
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t********* 通讯录 *********\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 1、添加联系人 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 2、删除联系人 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 3、查找联系人 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 4、修改联系人 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 5、通讯录排序 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 6、显示联系人 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
printf("\t\t\t\t\t**** ——> 0、退出通讯录 <—— ****\n");
printf("\t\t\t\t\t***************************************\n");
}
void test()
{
Con con;
init_contact(&con);
int input = 0;
while (1)
{
menu();
printf("请选择功能:\n");
scanf("%d", &input);
switch (input)
{
case Exit:
destroy_contact(&con);
printf("欢迎下次使用!\n");
return;
case Add:
addition_information(&con);
break;
case Delete:
delete_information(&con);
break;
case Find:
find_information(&con);
break;
case Modify:
modify_information(&con);
break;
case Sort:
sort_information(&con);
break;
case Show:
show_information(&con);
break;
default:
printf("选择错误,请重新选择:\n");
break;
}
_getch();
system("cls");
}
}
int main()
{
test();
return 0;
}
动态版本的就这样了,后面会更新文件版本和数据库版本的通讯录👌