C语言通讯录(静态&动态&文件操作/内附源码)(二)

简介: 讲解实现C语言静态通讯录


通讯录结构


  • 参考代码:


//通讯录结构
struct Contact
{
  //联系人总容量
  int capacity;
  //标记动态通讯录的首地址
  struct Peoinfo *data;
  //使用个数记录
  int sz;
};


初始化通讯录

使用malloc来开辟一个默认大小的动态空间


  • 参考代码:


//初始化通讯录
void InitContact(struct Contact* p)
{
  p->capacity = C_SZ;
  p->sz = 0;
  p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ);
  LoadContact(p);
}
//默认容量大小
#define C_SZ  2
//默认添加大小
#define PLUS  3


通讯录增容


  • 注意点:
  1. 添加通讯录联系人时进行判断(这里我们封装成一个函数)
  2. 当人数等于容量大小就增容(用realloc来调整动态开辟空间的大小)
  3. 增容失败则打印错误并结束程序


  • 参考代码:


//检查通讯录是否满
void CheakContact(struct Contact* p)
{
  if (p->sz == p->capacity)
  {
    //如果使用人数已经满了,则申请开辟更大的空间
    struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS));
    //ptr不为NULL则开辟成功
    if (ptr != NULL)
    {
      p->data = ptr;
      p->capacity += PLUS;
    }
    else
    {
      perror("CheakContact");//打印出错误原因
      exit(1);//结束程序
    }
  }
}


通讯录销毁


  • 注意点:

退出通讯录时我们需要将动态开辟的空间给释放(使用free函数)(否则可能造成内存泄漏)


  • 参考代码:


//动态通讯录申请空间后不用还得释放
void DestoryContact(struct Contact* p)
{
  free(p->data);
  p -> data = NULL;
  p->capacity = 0;
  p->sz = 0;
}


数据保存


在我们的通讯录写好后,我们如果还想让通讯录能够保存我们所输入联系人数据(在退出后再打开通讯录)


注:这就需要一点文件操作的知识了(在后面的专题中会仔细讲解)


输出数据

在通讯录运行结束前,我们让通讯录联系人的数据以二进制的形式输出到指定的文件中


(即将数据保存在文件中)


参考代码:


//保存通讯录数据
void SaveContact(struct Contact* p)
{
  //fopen函数:找到指定文件并返回文件的地址
  //wb:以只写的方式打开(为了输出数据,打开一个二进制文件)
  FILE* pf = fopen("contact.txt", "wb");
  if (pf == NULL)
  {
    //打开失败则打印错误信息
    perror("SaveContact");
    return;
  }
  for (int i = 0; i < p->sz; i++)
  {
    //fwrite:二进制输出
    fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf);
  }
  //用完pf后进行释放
  fclose(pf);
  pf = NULL;
  return;
}


输入数据(加载数据)


当下一次进入通讯录将原来保存的数据输入到内存中


  • 参考代码:


//加载通讯录数据
void LoadContact(struct Contact*p)
{
  //rb:以只读的方式打开一个二进制文件
  FILE* pf = fopen("contact.txt","rb");
  if (pf == NULL)
  {
    perror("Loadcontact");
    return;
  } 
  //暂时保存数据
  struct Peoinfo tmp = { 0 };
  //fread:二进制输入(输入的数据完整则返回1,否则返回0)
  while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
  {
    //输入时判断是否需要增容
    CheakContact(p);
    p->data[p->sz] = tmp;
    p->sz++;
  }
  //释放
  fclose(pf);
  pf = NULL;
  return;
}


附上源码


  • contact.h


#define _CRT_SECURE_NO_WARNINGS
#pragma once
//特定数据宏定义方式,包含各种头文件,以及结构体与函数的声明
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX  10
#define ADDR_MAX 30
//默认容量大小
#define C_SZ  2
//默认添加大小
#define PLUS  3
//个人信息结构
struct Peoinfo
{
  char name[NAME_MAX];
  int age;
  char sex[SEX_MAX];
  int number[NUMBER_MAX];
  char addr[ADDR_MAX];
};
//通讯录结构
struct Contact
{
  //联系人总容量
  int capacity;
  //标记动态通讯录的首地址
  struct Peoinfo *data;
  //使用个数记录
  int sz;
};
//初始化通讯录(清空通讯录)
void InitContact(struct Contact* p);
//添加通讯录联系人
void AddContact(struct Contact* p);
//删除通讯录联系人
void DelContact(struct Contact* p);
//查找通讯录联系人
void SearchContact(struct Contact* p);
//修改通讯录联系人信息
void ModifyContact(struct Contact* p);
//展示通讯录联系人
void ShowContact(struct Contact* p);
//通讯录排序
void SortContact(struct Contact* p);
//清除通讯录(释放)
void DestoryContact(struct Contact* p);
void SaveContact(struct Contact* p);
void LoadContact(struct Contact* p);
//操作选项
void option();
int cmp_con_name(void* e1, void* e2);
int cmp_con_age(void* e1, const void* e2);
int cmp_con_sex(const void* e1, const void* e2);
int cmp_con_number(const void* e1, const void* e2);
int cmp_con_addr(const void* e1, const void* e2);


  • contact.c


//实现通讯录功能函数
#include "contact.h"
//检查通讯录是否满
void CheakContact(struct Contact* p)
{
  if (p->sz == p->capacity)
  {
    //如果使用人数已经满了,则申请开辟更大的空间
    struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS));
    //ptr不为NULL则开辟成功
    if (ptr != NULL)
    {
      p->data = ptr;
      p->capacity += PLUS;
    }
    else
    {
      perror("LoadContact");
      exit(1);
    }
  }
}
//加载通讯录数据
void LoadContact(struct Contact*p)
{
  //rb:以只读的方式打开一个二进制文件
  FILE* pf = fopen("contact.txt","rb");
  if (pf == NULL)
  {
    perror("Loadcontact");
    return;
  } 
  //暂时保存数据
  struct Peoinfo tmp = { 0 };
  //fread:二进制输入(输入的数据完整则返回1,否则返回0)
  while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
  {
    //输入时判断是否需要增容
    CheakContact(p);
    p->data[p->sz] = tmp;
    p->sz++;
  }
  //释放
  fclose(pf);
  pf = NULL;
  return;
}
//初始化通讯录
void InitContact(struct Contact* p)
{
  p->capacity = C_SZ;
  p->sz = 0;
  p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ);
  LoadContact(p);
}
//添加通讯录联系人
void AddContact(struct Contact* p)
{
  CheakContact(p);
    printf("请输入新联系人的名字:");
    scanf("%s", p->data[p->sz].name);
    printf("请输入新联系人的年龄:");
    scanf("%d", &p->data[p->sz].age);
    printf("请输入新联系人的性别:");
    scanf("%s", p->data[p->sz].sex);
    printf("请输入新联系人的电话号码:");
    scanf("%s", p->data[p->sz].number);
    printf("请输入新联系人的地址:");
    scanf("%s", p->data[p->sz].addr);
    printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
    p->sz++;
  return;
}
//删除通讯录联系人
void DelContact(struct Contact* p)
{
  if (p->sz == 0)
  {
    printf("没有联系人,无法删除!\n");
    return;
  }
  char name[NAME_MAX];
  printf("请输入要删除的联系人名字:\n");
  scanf("%s", &name);
  for (int i = 0; i < p->sz; i++)
  {
    //字符串比较采用strcmp
    if (strcmp(name, p->data[i].name) == 0)
    {
      for (int j = i; j < p->sz - 1; j++)
      {
        //字符串赋值采用strcpy
        strcpy(p->data[j].name, p->data[j + 1].name);
        p->data[j].age = p->data[j + 1].age;
        strcpy(p->data[j].sex, p->data[j + 1].sex);
        strcpy(p->data[j].number, p->data[j + 1].number);
        strcpy(p->data[j].addr, p->data[j + 1].addr);
      }
      p->sz--;
      printf("删除通讯录联系人%s成功!\n", name);
      return;
    }
  }
  printf("在通讯录中无法查找到联系人%s!\n", name);
  return;
}
//查找通讯录联系人
void SearchContact(struct Contact* p)
{
  char name[NAME_MAX];
  printf("请输入要进行操作的联系人名字:\n");
  scanf("%s", &name);
  for (int i = 0; i < p->sz; i++)
  {
    if (strcmp(name, p->data[i].name) == 0)
    {
      printf("联系人的名字:%s\n", p->data[i].name);
      printf("联系人的年龄:%d\n", p->data[i].age);
      printf("联系人的性别:%s\n", p->data[i].sex);
      printf("联系人的电话号码:%s\n", p->data[i].number);
      printf("联系人的地址:%s\n", p->data[i].addr);
      return;
    }
  }
  printf("在通讯录中无法查找到该联系人!\n");
  return;
}
//操作选项
void option()
{
  printf("**************************\n");
  printf("****1.name       2.age****\n");
  printf("****3.sex     4.number****\n");
  printf("****5.addr      0.exit****\n");
  printf("**************************\n");
}
//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
  int pos = -1;
  int intput = -1;
  ShowContact(p);
  char name[NAME_MAX];
  printf("请输入要进行操作的联系人名字:\n");
  scanf("%s", &name);
  for (int i = 0; i < p->sz; i++)
  {
    if (strcmp(name, p->data[i].name) == 0)
    {
      //找到则记录位置并进行展示
      printf("联系人的名字:%s\n", p->data[i].name);
      printf("联系人的年龄:%d\n", p->data[i].age);
      printf("联系人的性别:%s\n", p->data[i].sex);
      printf("联系人的电话号码:%s\n", p->data[i].number);
      printf("联系人的地址:%s\n", p->data[i].addr);
      pos = i;
      break;
    }
  }
  //找不到则退出
  if (pos == -1)
  {
    printf("在通讯录中无法查找到该联系人!\n");
    return;
  }
  do
  {
    option();
    printf("请输入要修改的选项:->\n");
    scanf("%d", &intput);
    switch (intput)
    {
    case 0:
      printf("退出修改成功!\n");
      break;
    case 1:
      printf("请输入新的名字:\n");
      scanf("%s", &p->data[pos].name);
      break;
    case 2:
      printf("请输入新的年龄:\n");
      scanf("%d", &p->data[pos].age);
      break;
    case 3:
      printf("请输入新的性别:\n");
      scanf("%d", &p->data[pos].sex);
      break;
    case 4:
      printf("请输入新的号码:\n");
      scanf("%d", &p->data[pos].number);
      break;
    case 5:
      printf("请输入新的地址:\n");
      scanf("%d", &p->data[pos].addr);
      break;
    default:
      printf("输入错误,请重新输入:\n");
    }
  } while (intput);
  return;
}
//展示通讯录联系人
void ShowContact(struct Contact* p)
{
  //制表
  printf("————————————————————————————————————————————————\n");
  printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
    "name", "age", "sex", "tele", "addr");
  printf("————————————————————————————————————————————————\n");
  for (int i = 0; i < p->sz; i++)
  {
    printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n",
      p->data[i].name,
      p->data[i].age,
      p->data[i].sex,
      p->data[i].number,
      p->data[i].addr);
    printf("————————————————————————————————————————————————\n");
  }
  return;
}
//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1, const void* e2)
{
  return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}
int cmp_con_age(const void* e1, const void* e2)
{
  return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}
int cmp_con_sex(const void* e1, const void* e2)
{
  return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}
int cmp_con_number(const void* e1, const void* e2)
{
  return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}
int cmp_con_addr(const void* e1, const void* e2)
{
  return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
  int intput = -1;
  do
  {
    option();
    printf("请输入排序方式的选项:\n");
    scanf("%d", &intput);
    switch (intput)
    {
    case 0:
      printf("退出修改成功!\n");
      break;
    case 1:
      qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
      ShowContact(p);
      break;
    case 2:
      qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
      ShowContact(p);
      break;
    case 3:
      qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
      ShowContact(p);
      break;
    case 4:
      qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
      ShowContact(p);
      break;
    case 5:
      qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
      ShowContact(p);
      break;
    default:
      printf("输入错误,请重新输入:\n");
    }
  } while (intput);
  return;
}
//动态通讯录申请空间后不用还得释放
void DestoryContact(struct Contact* p)
{
  free(p->data);
  p -> data = NULL;
  p->capacity = 0;
  p->sz = 0;
}
//保存通讯录数据
void SaveContact(struct Contact* p)
{
  //fopen函数:找到指定文件并返回文件的地址
  //wb:以只写的方式打开(为了输出数据,打开一个二进制文件)
  FILE* pf = fopen("contact.txt", "wb");
  if (pf == NULL)
  {
    //打开失败则打印错误信息
    perror("SaveContact");
    return;
  }
  for (int i = 0; i < p->sz; i++)
  {
    //fwrite:二进制输出
    fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf);
  }
  //用完pf后进行释放
  fclose(pf);
  pf = NULL;
  return;
}


  • test.c


//写通讯录的整体流程和逻辑
#include "contact.h"
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");
}
//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo* i), struct Contact* p)
{
  pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
  int intput;
  struct Contact con;
  InitContact(&con);
  //写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
  void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,DestoryContact };
  //基本逻辑循环
  do
  {
    menu();
    printf("请输入接下来想要进行的操作:->\n");
    scanf("%d", &intput);
    if (intput > 0 && intput <= 7)
    {
      printf("成功进入所选择项操作!\n");
      Calo(table[intput], &con);//回调操作
    }
    else if (intput == 0)
    {
      SaveContact(&con);
      Calo(table[7], &con);
      printf("退出操作成功!!\n");
      break;
    }
    else
    {
      printf("输入项错误!请重新选择:->\n");
    }
  } while (intput);//intput为0则退出
}


相关文章
|
2月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
292 9
|
16天前
|
存储 小程序 C语言
【C语言程序设计——文件】文件操作(头歌实践教学平台习题)【合集】
本文介绍了C语言中的文件操作,分为两个关卡。第1关任务是将键盘输入的字符(以#结束)存入`file1.txt`并显示输出;第2关任务是从键盘输入若干行文本(每行不超过80个字符,用-1作为结束标志),写入`file2.txt`后再读取并显示。文中详细讲解了文件的打开、读取(使用`fgetc()`和`fgets()`)、写入(使用`fputc()`和`fputs()`)及关闭操作,并提供了示例代码和测试说明。
34 5
|
1月前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
64 9
|
1月前
|
存储 数据管理 C语言
C 语言中的文件操作:数据持久化的关键桥梁
C语言中的文件操作是实现数据持久化的重要手段,通过 fopen、fclose、fread、fwrite 等函数,可以实现对文件的创建、读写和关闭,构建程序与外部数据存储之间的桥梁。
|
2月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
180 3
|
2月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
129 16
|
2月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
172 7
|
2月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
143 8
|
2月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
181 8
|
2月前
|
C语言 Windows
C语言课设项目之2048游戏源码
C语言课设项目之2048游戏源码,可作为课程设计项目参考,代码有详细的注释,另外编译可运行文件也已经打包,windows电脑双击即可运行效果
46 1

热门文章

最新文章