C语言基础要求:结构体,动态内存管理,顺序表,文件操作
思路:利用已经完成的顺序表,对顺序表简易修改,应用到通讯录.
通讯录只能能存100个人
用户信息:名字,性别,年龄,电话,地址
通讯录的展示,增,删,改,查,找.
我们已经完成了顺序表,我备份并且更名为SeqList_copy.h
和SeqList_copy.c
创建Contact.h
和Contact.c
以及测试代码功能的test.c
test.c
和Contact.c
引用的头文件都是Contact.h
和SeqList_copy.h
在Contact.h
头文件中
定义名字最大长度,性别最大长度,电话最大长度,地址最大长度
#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50
//定义结构体
typedef struct ContactInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}CInfo;
//通讯录的底层是顺序表来实现
//给 SeqList 取名叫 contact ,更改名称
typedef struct SeqList contact;
在顺序表已经完成了初始化和销毁,这里我们直接套用
void ContactInit(contact* pcon)
{
SLInit(pcon); //直接调用我们已经实现的顺序表的方法
}
void ContactDestory(contact* pcon)
{
SLInit(pcon);
}
我想要一步一步完成对联系人的添加,就分次加入,显得臃肿了.
我们获取的都是CInfo结构体要求的数据
name,sex,tel,addr都是字符串数组,整个数组代表数组的首元素地址,使用scanf,不需要再&取地址
但是age是int类型数组,使用scanf,仍需要&取地址
void ContactAdd(contact* pcon)
{
CInfo info;
printf("输入联系人姓名:\n");
scanf("%s", info.name);
printf("输入联系人性别:\n");
scanf("%s", info.sex);
printf("输入联系人年龄:\n");
scanf("%d", &info.age); //这里注意,定义age是int类型数组,使用scanf,仍需要&取地址
printf("输入联系人电话:\n");
scanf("%s", info.tel);
printf("输入联系人住址:\n");
scanf("%s", info.addr);
printf("输入完成\n\n");
//往通讯录插入数据
SLPushBack(pcon, info);
}
我这里通过姓名删除联系人,比较简易
要点1,创建FindByName
函数,检查是否能通过名字找到.
要点2,利用顺序表的SLErase
函数,观察它的参数,巧妙利用下标
创建
FindByName
函数
int FindByName(contact* pcon,char name[])
{
for (int i = 0; i < pcon->size; i++)
{
if (strcmp(pcon->a[i].name, name) == 0) //比较字符串姓名
{
return i; //找到了就返回索引i
}
}
return -1; //没有找到就用-1代替
}
//删除联系人
void ContactDel(contact* pcon)
{
printf("需要删除联系人的名字:\n");
char name[NAME_MAX];
scanf("%s", name);
//找到人的下标记录一下
int findindex = FindByName(pcon, name);
//没找到联系人就是不存在
if (findindex < 0) {
printf("不存在!\n");
return;
}
//这里找到联系人,删除findindex位置的数据
//参考函数void SLErase(SL* ps, int pos) 根据索引删除
SLErase(pcon, findindex);
}
再次利用FindByName
函数.通过姓名修改联系人
void ContactModify(contact* pcon)
{
char name[NAME_MAX];
printf("请输入您要修改的联系人:\n");
scanf("%s", name); //同样的道理,name本身是字符串数据,不用&
//获取到的通讯录下标的位置find,利用数组修改
int find = FindByName(pcon, name);
if (find < 0)
{
printf("要修改的用户不存在\n");
return;
}
printf("请输入新的用户名:\n");
scanf("%s", pcon->a[find].name);
printf("请输入新的用户性别\n");
scanf("%s", pcon->a[find].sex);
printf("请输入新的用户年龄:\n");
scanf("%d", &pcon->a[find].age); //age 是整型,我们要把地址传递过去
printf("请输入新的用户电话:\n");
scanf("%s", pcon->a[find].tel);
printf("请输入新的用户地址:\n");
scanf("%s", pcon->a[find].addr);
printf("修改完成\n");
}
void ContactFind(contact* pcon)
{
char name[NAME_MAX];
printf("请输入要查找的用户名:\n");
scanf("%s", name);
int find = FindByName(pcon, name);
if (find < 0)
{
printf("该联系人不存在\n");
return;
}
//提前打印表头,便于查看
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
printf(
"%s %s %d %s %s\n",
pcon->a[find].name,
pcon->a[find].sex,
pcon->a[find].age,
pcon->a[find].tel,
pcon->a[find].addr
);
}
这个逻辑跟顺序表有点不一样,就重新编写了一份
void ContactShow(contact* pcon)
{
//打印第一列header
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
for (int i = 0; i < pcon->size; i++)
{
printf("%s %s %d %s %s\n", pcon->a[i].name, pcon->a[i].sex,
pcon->a[i].age, pcon->a[i].tel, pcon->a[i].addr);
}
}
SeqList_copy.h
部分的代码,查找顺序表的函数,不适用于通讯录,就注释了
这一步一定注意更改动态顺序表数据类型为通讯录数据类型
#pragma once
#include
#include
#include
#include
#include
#include"Contact.h" //添加头文件
//动态顺序表
//typedef int SLDataType;
//1. 更改动态顺序表数据类型为通讯录数据类型
typedef struct ContactInfo SLDataType; //这里不能直接使用ContactInfo的别名 CInfo
//或者在这里再次命名,但是这样很多余
/*
//typedef struct ContactInfo CInfo;
//typedef CInfo SLDataType;
*/
typedef struct SeqList
{
SLDataType* a;
int size; // 顺序表中有效的个数
int capacity; //顺序表当前的空间大小
}SL; //简化结构体名称
//对顺序表进行初始化
void SLInit(SL* ps);
void SLDestory(SL* ps);
//头部,尾部 插入,删除
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//打印SL
SLprint(SL* ps);
//判断SL是否为空
bool SLIsEmpty(SL* ps);
//在指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//删除指定位置的数据
void SLErase(SL* ps, int pos);
//查找数据
//bool SLFind(SL* ps, SLDataType x);
SLFind()
函数不适用,我们的FindByName()
函数是通过姓名查看姓名是否存在通讯录
SLPrint()
函数同样不适用,所以才有ContactShow()
函数
#include"SeqList_copy.h"
void SLInit(SL* ps) {
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps) {
if (ps->a)
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps) {
if (ps->size == ps->capacity) {
//空间不足以再额外插入一个数据
//扩容
int newCapcity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapcity * sizeof(SLDataType));
if (tmp == NULL) {
perror("realloc fail!\n");
return 1;
}
ps->a = tmp;
ps->capacity = newCapcity;
}
}
//尾插
void SLPushBack(SL* ps, SLDataType x) {
//assert(ps != NULL);
//暴力的方式
assert(ps);
//柔和方式
//if (ps == NULL) {
// return;
//}
SLCheckCapacity(ps);
//直接插入数据
ps->a[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDataType x) {
assert(ps);
//判断空间是否足够,不够则扩容
SLCheckCapacity(ps);
//空间足够,历史数据后移一位
for (size_t i = ps->size; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[0] = x;
ps->size++;
}
//尾插
void SLPopBack(SL* ps) {
//判断顺序表是否为空
assert(ps);
assert(!SLIsEmpty(ps));
//ps->a[ps->size - 1] = 0;
ps->size--;
}
void SLPopFront(SL* ps) {
assert(ps);
assert(!SLIsEmpty(ps));
//让后面的数据往前挪动一位
for (size_t i = 0; i < ps->size - 1; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
//void SLPrint(SL* ps) {
// for (size_t i = 0; i < ps->size; i++)
// {
// printf("%d ", ps->a[i]);
// }
// printf("\n");
//}
bool SLIsEmpty(SL* ps) {
assert(ps);
//这样子是不对的,这里只能判断空间是否足够
//return ps->size == ps->capacity;
return ps->size == 0;
}
//在指定的位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x) {
assert(ps);
//不要忘了对pos加以限制
assert(pos >= 0 && pos <= ps->size);
//扩容
SLCheckCapacity(ps);
//把pos位置及以后的数据往后挪动一位
//循环条件里i的初始值是size还是size-1都是可以的,但不同的初始值对应不同的结束条件
//for (int i = ps->size; i > pos; i--)
//{
// //pos+1
// ps->a[i] = ps->a[i - 1];//pos->a[pos+1] = ps->a[pos]
//}
for (int i = ps->size - 1; i > pos - 1; i--)
{
//最后一次进来的i是pos
ps->a[i + 1] = ps->a[i];//ps->a[pos+1] = ps->a[pos]
}
ps->a[pos] = x;
ps->size++;
}
//删除指定位置的数据
void SLErase(SL* ps, int pos) {
assert(ps);
assert(!SLIsEmpty(ps));
//pos的范围需要限制
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i < ps->size - 1; i++)
{
//最后一次进来的i的数据ps->size-2
ps->a[i] = ps->a[i + 1];//ps->a[ps->size-2] = ps->a[ps->size-1]
}
ps->size--;
}
//bool SLFind(SL* ps, SLDataType x) {
// assert(ps);
// for (int i = 0; i < ps->size; i++)
// {
// if (ps->a[i].name == x) {
// //找到了
// return true;
// }
// }
// return false;
//}
#pragma once
//创建保存联系人数据的结构
#define NAME_MAX 120
#define SEX_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 50
typedef struct ContactInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}CInfo;
//通讯录的底层是顺序表来实现
//给 SeqList 取名叫 contact
typedef struct SeqList contact;
//1.通讯录的初始化和销毁
void ContactInit(contact* pcon);
void ContactDestory(contact* pcon);
//2.添加联系人
void ContactAdd(contact* pcon);
//3.删除联系人
void ContactDel(contact* pcon);
//4.修改联系人
void ContactModify(contact* pcon);
//5.查找联系人
void ContactFind(contact* pcon);
//6.查看通讯录
void ContactShow(contact* pcon);
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList_copy.h"
#include"Contact.h"
//1.通讯录的初始化和销毁
void ContactInit(contact* pcon)
{
SLInit(pcon); //直接调用我们已经实现的顺序表的方法
}
void ContactDestory(contact* pcon)
{
SLInit(pcon);
}
//2.添加联系人
//我们获取的都是CInfo结构体要求的数据
//name,sex,tel,addr都是字符串数组,整个数组代表数组的首元素地址,使用scanf,不需要再&取地址
//但是age是int类型,使用scanf,仍需要&取地址
void ContactAdd(contact* pcon)
{
CInfo info;
printf("输入联系人姓名:\n");
scanf("%s", info.name);
printf("输入联系人性别:\n");
scanf("%s", info.sex);
printf("输入联系人年龄:\n");
scanf("%d", &info.age);
printf("输入联系人电话:\n");
scanf("%s", info.tel);
printf("输入联系人住址:\n");
scanf("%s", info.addr);
printf("输入完成\n\n");
//往通讯录插入数据
SLPushBack(pcon, info);
}
//3.删除联系人
//我们这里通过姓名删除联系人
int FindByName(contact* pcon,char name[])
{
for (int i = 0; i < pcon->size; i++)
{
if (strcmp(pcon->a[i].name, name) == 0) //比较字符串姓名
{
return i; //找到了就返回索引i
}
}
return -1; //没有找到就用-1代替
}
void ContactDel(contact* pcon)
{
printf("需要删除联系人的名字:\n");
char name[NAME_MAX];
scanf("%s", name);
//找到人的下标记录一下
int findindex = FindByName(pcon, name);
//没找到联系人就是不存在
if (findindex < 0) {
printf("不存在!\n");
return;
}
//这里找到联系人,删除findindex位置的数据
//参考函数void SLErase(SL* ps, int pos) 根据索引删除
SLErase(pcon, findindex);
}
//4.修改联系人
void ContactModify(contact* pcon)
{
char name[NAME_MAX];
printf("请输入您要修改的联系人:\n");
scanf("%s", name);
//获取到的通讯录下标的位置find,利用数组修改
int find = FindByName(pcon, name);
if (find < 0)
{
printf("要修改的用户不存在\n");
return;
}
printf("请输入新的用户名:\n");
scanf("%s", pcon->a[find].name);
printf("请输入新的用户性别\n");
scanf("%s", pcon->a[find].sex);
printf("请输入新的用户年龄:\n");
scanf("%d", &pcon->a[find].age); //age 是整型,我们要把地址传递过去
printf("请输入新的用户电话:\n");
scanf("%s", pcon->a[find].tel);
printf("请输入新的用户地址:\n");
scanf("%s", pcon->a[find].addr);
printf("修改完成\n");
}
//5.查找联系人
void ContactFind(contact* pcon)
{
char name[NAME_MAX];
printf("请输入要查找的用户名:\n");
scanf("%s", name);
int find = FindByName(pcon, name);
if (find < 0)
{
printf("该联系人不存在\n");
return;
}
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
printf(
"%s %s %d %s %s\n",
pcon->a[find].name,
pcon->a[find].sex,
pcon->a[find].age,
pcon->a[find].tel,
pcon->a[find].addr
);
}
//6.查看通讯录
void ContactShow(contact* pcon)
{
//打印第一列header
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
for (int i = 0; i < pcon->size; i++)
{
printf("%s %s %d %s %s\n", pcon->a[i].name, pcon->a[i].sex,
pcon->a[i].age, pcon->a[i].tel, pcon->a[i].addr);
}
}
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList_copy.h"
#include"Contact.h"
void Contact_Test() //测试函数
{
contact con;
ContactInit(&con);
//以下是测试
//添加联系人测试
ContactAdd(&con); // 1 1 1 1 1
ContactAdd(&con); // 2 2 2 2 2
ContactShow(&con);// 1 1 1 1 1 2 2 2 2 2
//删除联系人测试
//ContactDel(&con);
//ContactShow(&con);
//修改联系人测试
//ContactModify(&con); //1 3 3 3 3 3
//ContactShow(&con); //3 3 3 3 3 2 2 2 2 2
//查找联系人测试
ContactFind(&con);
//以上是测试
ContactDestory(&con);
}
void menu()
{
printf("****************************\n");
printf("****************************\n");
printf("************通讯录***********\n");
printf("*********1.添加联系人********\n");
printf("*********2.删除联系人********\n");
printf("*********3.修改联系人********\n");
printf("*********4.查找联系人********\n");
printf("*********5.查看通讯录********\n");
printf("*********0.退 出************\n");
printf("****************************\n");
printf("****************************\n");
}
int main()
{
//Contact_Test();
//测试完成,函数封装成菜单,在以下展示
int op = 0;
contact con;
ContactInit(&con);
do {
menu();
printf("请选择:\n");
scanf("%d", &op);
switch (op)
{
case 1:
ContactAdd(&con);
break;
case 2:
ContactDel(&con);
break;
case 3:
ContactModify(&con);
break;
case 4:
ContactFind(&con);
break;
case 5:
ContactShow(&con);
break;
case 0:
printf("退出了哈");
break;
default:
printf("输入错误,请重新输入");
break;
}
} while (op!=0);
ContactDestory(&con);
//system("Pause");
return 0;
}