通讯录的实现(C语言)

目录

前言

一、C语言知识点回顾

1.枚举

1.1枚举类型的定义

1.2 枚举的优点 

为什么使用枚举?

1.3 通讯录中枚举的使用

2. return 与 return 0

3. qsort

参数

用法:

4. fopen

C 库函数 – fopen() | 菜鸟教程 (runoob.com)

 5. fread

二、VS项目创建与文件的引入

三、框架-设计思路(套用模板)

3.1 目录

 3.2 main函数

 3.3 结构体创建

3.3.1 联系人结构体

3.3.2 通讯录结构体

 四、功能函数的实现 

4.1 初始化通讯录

4.2 容量检查 

4.3  加载通讯录(从文件中)

 4.4 增加联系人

4.5 删除联系人

4.6 展示通讯录

 4.7 修改联系人

 4.8 对通讯录进行排序(名字)

 4.9 查找联系人

4.10 保存通讯录 

4.11 销毁 

五、全部代码 

5.1 game.c

5.2 game.h

5.3 test.c

总结

前言

简易版通讯录代码要实现的内容:

1.可以存放1000个人的信息

2.人的信息包括:名字、年龄、电话、住址、性别

3.可以进行如下操作:

        增加联系人、删除联系人、查找联系人、修改联系人

4. 可以进行排序,比如通过名字或者年龄进行对于通讯录的排序


一、C语言知识点回顾

1.枚举

        把可能的取值一一列举。
        比如我们现实生活中:
                一周的星期一到星期日是有限的7天,可以一一列举。性别有:男、女、保密,也可以一一列举。月份有12个月,也可以一一列举。

1.1枚举类型的定义

enum Day//星期
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};
enum Sex//性别
{
    MALE,
    FEMALE,
    SECRET
};
enum Color//颜色
{
    RED,
    GREEN,
    BLUE
};

 以上定义的enum Day , enum Sex , enum Color 都是枚举类型。
{ }中的内容是枚举类型的可能取值,也叫枚举常量。
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
例如:

enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};

1.2 枚举的优点 

为什么使用枚举?

(2条消息) C语言-枚举(enum)奇妙的使用、联合体(共用体union)对空间节省的巧妙_This is iNEvitable的博客-CSDN博客_c语言 enum 嵌套

我们可以使用#define 定义常量,为什么非要使用枚举?(面试题)
        1. 增加代码的可读性和可维护性
        2. 和#define定义的标识符比较,枚举有类型检查,更加严谨。
        3. 防止了命名污染(封装)。把名字设置为常量,即枚举常量,不能够再次创建同名字的变量,而且常量不能够改变,特殊的在于这个枚举常量是属于一个枚举类型的,例如RED属于Color
        4. 便于调试。其实代码的执行过程是通过:预处理-》编译-》链接-》可执行文件(.exe),在预处理阶段如果使用了#define定义的标识符常量会直接替换成值,调试的时候不便于观察,但是我们的枚举常量是不会替换成值的,相比于#define定义的标识符常量更易于观察。
        5. 使用方便,一次可以定义多个常量

1.3 通讯录中枚举的使用

1.3.1 枚举出相关功能

enum Option
{
	EXIT,
	Add,
	Del,
	Search,
	Modify,
	Sort,
	Show
};

 1.3.2 比较常见的枚举的用法,与switch-case结合使用

#include
int main()
{
    enum color{red,yellow,green,blue,black} user_color;    /*定义枚举类型color,声明变量user_color*/
    int i=1;  /*定义循环条件变量i */
    while(i)
    {   printf("\n有五种颜色:red,yellow,green,blue,black \n ");
        printf("请输入你选择颜色的序号(0~4,其他数值退出): ");
        scanf("%d",&user_color);
        switch(user_color)
        {
            case red: printf("\n你选择的是:红色\n"); break;
            case yellow: printf("\n你选择的是:黄色\n"); break;
            case green: printf("\n你选择的是:绿色\n"); break;
            case blue: printf("\n你选择的是:蓝色\n"); break;
            case black: printf("\n你选择的是:黑色\n"); break;
            default: i=0;
        }
        printf("\n");
    }
    return 0;
}

2. return 与 return 0

        return;的作用相当于break;用于中断循环的作用;常常运用在比如链表的节点的删除,如果是空链表,那就直接return,就行,因为尾删函数是没有返回值的,返回值void,所以return大概就是相当于break的作用。

        return 0;则是return的另一种用法,专用于返回值非void的函数返回其值。比如int main,main函数都有return 0,其实也不一定要写return 0,其实别的也可以,比如return -1,她只是需要一个返回值,main函数的返回值是int类型就可以。

3. qsort

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))

参数

  • base -- 指向要排序的数组的第一个元素的指针。
  • nitems -- 由 base 指向的数组中元素的个数。
  • size -- 数组中每个元素的大小,以字节为单位。
  • compar -- 用来比较两个元素的函数。

用法:

#include 
#include 

int values[] = { 88, 56, 100, 2, 25 };

int cmpfunc (const void * a, const void * b)
{
   return ( *(int*)a - *(int*)b );
}

int main()
{
   int n;

   printf("排序之前的列表:\n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }

   qsort(values, 5, sizeof(int), cmpfunc);

   printf("\n排序之后的列表:\n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }
 
  return(0);
}

4. fopen

描述
C 库函数 FILE *fopen(const char *filename, const char *mode) 
使用给定的模式 mode 打开 filename 所指向的文件。

声明
下面是 fopen() 函数的声明。

FILE *fopen(const char *filename, const char *mode)
参数
filename -- 字符串,表示要打开的文件名称。
返回值
该函数返回一个 FILE 指针。否则返回 NULL,且设置全局变量 errno 来标识错误。
mode -- 字符串,表示文件的访问模式,可以是以下表格中的值:
模式 描述
"r" 打开一个用于读取的文件。该文件必须存在。
"w" 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
"a" 追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
"r+" 打开一个用于更新的文件,可读取也可写入。该文件必须存在。
"w+" 创建一个用于读写的空文件。
"a+" 打开一个用于读取和追加的文件。

 mode 有下列几种形态字符串:

  • r 以只读方式打开文件,该文件必须存在。
  • r+ 以可读写方式打开文件,该文件必须存在。
  • rb+ 读写打开一个二进制文件,允许读数据。
  • rw+ 读写打开一个文本文件,允许读和写。
  • w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
  • w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
  • a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
  • a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
  • wb 只写打开或新建一个二进制文件;只允许写数据。
  • wb+ 读写打开或建立一个二进制文件,允许读和写。
  • ab+ 读写打开一个二进制文件,允许读或在文件末追加数据。
  • at+ 打开一个叫string的文件,a表示append,就是说写入处理的时候是接着原来文件已有内容写入,不是从头写入覆盖掉,t表示打开文件的类型是文本文件,+号表示对文件既可以读也可以写。

C 库函数 – fopen() | 菜鸟教程 (runoob.com)

 5. fread

读文件fread函数的用法 - C语言教程 - C语言网 (dotcpp.com)

通讯录的实现(C语言)_第1张图片

二、VS项目创建与文件的引入

  1. game.c里面进行实现函数
  2. game.h声明函数头文件宏定义等
  3. Test.c写程序的主体部分

通讯录的实现(C语言)_第2张图片

         4.其工程目录如下所示,其中contact.txt用来存放以修改的通讯录:在之后的第四节会有提到和其详细用途的介绍。

通讯录的实现(C语言)_第3张图片

三、框架-设计思路(套用模板)

3.1 目录

        可以进行如下操作:

                增加联系人、删除联系人、查找联系人、修改联系人

        可以进行排序,比如通过名字或者年龄进行对于通讯录的排序

void menu()
{
	printf("**********************\n");
	printf("****  1 EXIT   *******\n");
	printf("****  2 ADD    *******\n");
	printf("****  3 Delete *******\n");
	printf("****  4 Search *******\n");
	printf("****  5 Modify *******\n");
	printf("****  6 Sort   *******\n");
	printf("****  7 Show   *******\n");
	printf("**********************\n");
}

 3.2 main函数

        关于Contact con = {0};//通讯录 InitContact(&con);//初始化通讯录会在 四、功能函数的实现 中进行详细说明。

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include
enum Option
{
	EXIT = 1,
	Add,
	Delete,
	Search,
	Modify,
	Sort,
	Show
}input;
void menu()
{
	printf("**********************\n");
	printf("****  1 EXIT   *******\n");
	printf("****  2 ADD    *******\n");
	printf("****  3 Del    *******\n");
	printf("****  4 Search *******\n");
	printf("****  5 Modify *******\n");
	printf("****  6 Sort   *******\n");
	printf("****  7 Show   *******\n");
	printf("**********************\n");
}
int main()
{
	Contact con = {0};//通讯录
	//初始化通讯录
	InitContact(&con);
	do
	{
		menu();
		printf("请选择->\n");
		scanf("%d", &input);
		switch (input)
		{
		case EXIT:
			//SaveContact(&con);
			//DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		case Add:
			printf("增加联系人:\n");
			//AddContact(&con); 
			break;
		case Delete:
			printf("删除联系人:\n");
			//DeleteContact(&con); 
			break;
		case Search:
			printf("查找联系人:\n");
			//SearchContact(&con); 
			break;
		case Modify:
			printf("修改联系人:\n");
			//ModifyContact(&con); 
			break;
		case Sort:
			printf("给通讯录排序:\n");
			//SortContact(&con); 
			break;
		case Show:
			printf("显示通讯录成员:\n");
			//ShowContact(&con); 
			break;
		default:
			printf("选择错误,请重新选择->\n");
			break;
		}
	} while (input);
	return 0;
}

 3.3 结构体创建

3.3.1 联系人结构体

        人的信息包括:名字、年龄、电话、住址、性别

        其中Name_MAX、Sex_MAX等均是为了方便管理。详情请见game.h

typedef struct ConInfo
{
	char name[Name_MAX];
	int age;
	char sex[Sex_MAX];
	char addr[Addr_MAX];
	char tele[Tele_MAX];
}ConInfo;

3.3.2 通讯录结构体

        动态通讯录,其中data动态管理存放联系人信息,sz为通讯录中有效信息的个数,capacity记录当前通讯录的容量。

通讯录的实现(C语言)_第4张图片

typedef struct Contact
{
	ConInfo* data;
	int sz;
	int capacity;
}Contact;

 四、功能函数的实现 

        下面将依据一下game.h的代码进行详细的展开说明。

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
#include 
#include 


#define MAX 1000//通讯录1000人

#define Name_MAX 20
#define Sex_MAX 5
#define Addr_MAX 30
#define Tele_MAX 12

#define DEFAULT_SZ 3//默认大小

typedef struct ConInfo
{
	char name[Name_MAX];
	int age;
	char sex[Sex_MAX];
	char addr[Addr_MAX];
	char tel[Tele_MAX];
}ConInfo;

//通讯录的结构体
typedef struct Contact
{
	ConInfo* data;
	int sz;
	int capacity;
}Contact;


void ContactInit(Contact* pc);//初始化通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(const Contact* pc);//显示通讯录
void DeleteContact(Contact* pc);//删除联系人
int FindConInfo(char* name, Contact* pc);//查找联系人,返回其下标
void SearchContact(Contact* pc);//查找联系人
void ModifyContact(Contact* pc);//修改联系人
void SortContact(Contact* pc);//对联系人进行排序(按名字)
void Check_capacity(Contact* pc);//增容
void DestroyContact(Contact* pc);//销毁通讯录
void LoadContact(Contact* pc);//加载通讯录
void SaveContact(Contact* pc);//保存通讯录

4.1 初始化通讯录

通讯录的实现(C语言)_第5张图片

         先为通讯录开辟默认的空间大小,最后加上LoadContact(pc);其目的是因为如果不是第一次使用这个程序,则之前的联系人信息就会在启动该程序时自动加载进来,方便查看和修改。

void ContactInit(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	ConInfo* tmp = (ConInfo*)malloc(DEFAULT_SZ * sizeof(ConInfo));
	if (tmp != NULL)
	{
		pc->data = tmp;
	}
	else
	{
		printf("error data\n");
		return;
	}
	//开辟通讯录默认空间大小成功或者说初始化成功
	pc->capacity = DEFAULT_SZ;
	LoadContact(pc);
}

4.2 容量检查 

        因为通讯录存放联系人的空间是动态管理的,当有效联系人个数等于当前通讯录的最大容量的时候需要进行容量检查,或者说需要进行realloc扩容处理。每次扩容将pc->capacity+2,是为了不浪费不必要的空间。

void Check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->capacity == pc->sz)//需要扩容
	{
		ConInfo* tmp = (ConInfo*)realloc(pc->data, sizeof(ConInfo) * (pc->capacity + 2));
		if (tmp != NULL)
		{
			//扩容成功
			printf("扩容成功\n");
			pc->data = tmp;
			pc->capacity = pc->capacity + 2;
		}
		else
		{
			printf("realloc error\n");
		}
	}
}

4.3  加载通讯录(从文件中)

        在启动程序时,需要对程序进行初始化,之前4.1初始化加上了LoadContact(pc);,即将本来通讯录就有的联系人加载进来,当通讯录现有容量不够时,同样需要进行扩容操作。

void LoadContact(Contact* pc)//将保存的联系人信息加载出来,方便对于联系人信息的查看
{
	FILE* pf = fopen("contact.txt", "rb");//从文件中读取数据
	if (pf == NULL)//打开失败
	{
		printf("%s\n",strerror(errno));
		return;
	}
	//打开成功就读取文件加载数据
	ConInfo buf = { 0 };//创建临时空间
	while (fread(&buf, sizeof(ConInfo), 1, pf))
	//每次从pf中读取一个字节大小为sizeof(ConInfo)的元素放在buf中
	{
		Check_capacity(pc);
		pc->data[pc->sz] = buf;
		pc->sz++;
	}
	fclose(pf);
	/*文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
	在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
	ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。*/
	pf = NULL;
}

 4.4 增加联系人

        无脑增加就行,增加前记得检查容量,不够就进行扩容。

void AddContact(Contact* pc)
{
	assert(pc);
	Check_capacity(pc);
	printf("请输入联系人信息:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("增加联系人成功\n");
}

4.5 删除联系人

        先通过名字找要删除的联系人的下标,再进行删除,删除实际上就是pc->data[j] = pc->data[j + 1];//删除联系人其实就是对被删除的联系人进行覆盖

int FindConInfo(char* name, Contact* pc)//通过名字找下标
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
		
	}
	return -1;
}
void DeleteContact(Contact* pc)//删除联系人
{
	char name[Name_MAX];
	printf("请输入要删除的联系人姓名->");
	scanf("%s", name);
	//查找联系人
	int flag = FindConInfo(name, pc);
	if (flag != -1)
	{
		int j = 0;
		for (j = flag; j < pc->sz - 1; j++)
		{
			pc->data[j] = pc->data[j + 1];//删除联系人其实就是对被删除的联系人进行覆盖
		}
		pc->sz--;
		printf("删除成功!\n");
	}
	else
	{
		printf("没有找到您需要删除的人!\n");
	}
}

4.6 展示通讯录

        注意间隔即可。

void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-7s\t%-5s\t%-5s\t%-13s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-7s\t%-5d\t%-5s\t%-13s\t%-20s\n",
			pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}

 4.7 修改联系人

      其原理与4.5类似。

void ModifyContact(Contact* pc)//修改联系人
{
	char name[Name_MAX];
	printf("请输入要修改的联系人姓名->");
	scanf("%s", name);
	int flag = FindConInfo(name, pc);
	if (flag == -1)
	{
		printf("查无此人\n");
		return;
	}
	else
	{
		int input = 0;
		do
		{
			printf("1.姓名  2.年龄  3.性别  4.住址  5.电话  0.退出\n");
			printf("请输入要修改的选项(按0退出修改)->");
			scanf("%d", &input);
			switch (input)
			{
			case 1:
				printf("请输入改正后的姓名->");
				scanf("%s", pc->data[flag].name);
				break;
			case 2:
				printf("请输入改正后的年龄->");
				scanf("%d", &(pc->data[flag].age));
				break;
			case 3:
				printf("请输入改正后的性别->");
				scanf("%s", pc->data[flag].sex);
				break;
			case 4:
				printf("请输入改正后的住址->");
				scanf("%s", pc->data[flag].addr);
				break;
			case 5:
				printf("请输入改正后的电话->");
				scanf("%s", pc->data[flag].tele);
				break;
			case 0:
				break;
			default:
				printf("输入错误,请重新输入!\n");
				break;
			}
		} while (input);
		printf("修改成功!\n");
	}

}

 4.8 对通讯录进行排序(名字)

         注意对于strcmp的使用C 库函数 – strcmp() | 菜鸟教程 (runoob.com)

int compar(const void* e1, const void* e2)
{
	return strcmp(((ConInfo*)e1)->name, ((ConInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), compar);
	printf("对通讯录进行了对于名字的排序!\n");
	ShowContact(pc);
}

 4.9 查找联系人

        找下标,同4.5。


void SearchContact(Contact* pc)//查找联系人
{
	char name[Name_MAX] = "0";
	printf("要查找的联系人姓名->");
	scanf("%s", name);
	int pos = FindConInfo(name, pc);
	if (pos != -1)
	{
		printf("%-7s\t%-7s\t%-6s\t%-20s\t%-15s\n", "姓名", "性别", "年龄", "住址", "电话");

		printf("%-7s\t%-7s\t%-6d\t%-20s\t%-15s\n",
			pc->data[pos].name, pc->data[pos].sex, pc->data[pos].age,
			pc->data[pos].addr, pc->data[pos].tele);
	}
	else
	{
		printf("找不到\n");
	}
}

4.10 保存通讯录 

        这个函数的功能就是将此次对通讯录里面信息以二进制的形式写入文件,方便下次启动此程序时查看。

void SaveContact(Contact* pc)//退出后保存通讯录
{
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		printf("Save error::%s\n", strerror(errno));
		return;
	}
	//写文件
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(ConInfo), 1, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}

4.11 销毁 

        防止内存泄露。

void DestroyContact(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

五、全部代码 

5.1 game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void ContactInit(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	ConInfo* tmp = (ConInfo*)malloc(DEFAULT_SZ * sizeof(ConInfo));
	if (tmp != NULL)
	{
		pc->data = tmp;
	}
	else
	{
		printf("error data\n");
		return;
	}
	//开辟通讯录默认空间大小成功或者说初始化成功
	pc->capacity = DEFAULT_SZ;
	LoadContact(pc);
}
void Check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->capacity == pc->sz)//需要扩容
	{
		ConInfo* tmp = (ConInfo*)realloc(pc->data, sizeof(ConInfo) * (pc->capacity + 2));
		if (tmp != NULL)
		{
			//扩容成功
			printf("扩容成功\n");
			pc->data = tmp;
			pc->capacity = pc->capacity + 2;
		}
		else
		{
			printf("realloc error\n");
		}
	}
}

/*当启动此程序时,我们要先对通讯录进行初始化,然后再将之前保存的联系人信息加载进来,
这样方便此次用此程序对之前的联系人信息进行修改查看。
读文件信息时也相当于是添加联系人了,因此在读入之前也需要判断通讯录容量,看是否需要对其增容。
*/
void LoadContact(Contact* pc)//将保存的联系人信息加载出来,方便对于联系人信息的查看
{
	FILE* pf = fopen("contact.txt", "rb");//从文件中读取数据
	if (pf == NULL)//打开失败
	{
		printf("%s\n",strerror(errno));
		return;
	}
	//打开成功就读取文件加载数据
	ConInfo buf = { 0 };//创建临时空间
	while (fread(&buf, sizeof(ConInfo), 1, pf))
	//每次从pf中读取一个字节大小为sizeof(ConInfo)的元素放在buf中
	{
		Check_capacity(pc);
		pc->data[pc->sz] = buf;
		pc->sz++;
	}
	fclose(pf);
	/*文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
	在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
	ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。*/
	pf = NULL;
}



void AddContact(Contact* pc)
{
	assert(pc);
	Check_capacity(pc);
	printf("请输入联系人信息:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("增加联系人成功\n");
}






int FindConInfo(char* name, Contact* pc)//通过名字找下标
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
		
	}
	return -1;
}
void DeleteContact(Contact* pc)//删除联系人
{
	char name[Name_MAX];
	printf("请输入要删除的联系人姓名->");
	scanf("%s", name);
	//查找联系人
	int flag = FindConInfo(name, pc);
	if (flag != -1)
	{
		int j = 0;
		for (j = flag; j < pc->sz - 1; j++)
		{
			pc->data[j] = pc->data[j + 1];//删除联系人其实就是对被删除的联系人进行覆盖
		}
		pc->sz--;
		printf("删除成功!\n");
	}
	else
	{
		printf("没有找到您需要删除的人!\n");
	}
}

void DestroyContact(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-7s\t%-5s\t%-5s\t%-13s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-7s\t%-5d\t%-5s\t%-13s\t%-20s\n",
			pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}


//这个函数的功能就是将此次对通讯录里面信息以二进制的形式写入文件,方便下次启动此程序时查看。
void SaveContact(Contact* pc)//退出后保存通讯录
{
	//打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		printf("Save error::%s\n", strerror(errno));
		return;
	}
	//写文件
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(ConInfo), 1, pf);
	}

	//关闭文件
	fclose(pf);
	pf = NULL;
}


void ModifyContact(Contact* pc)//修改联系人
{
	char name[Name_MAX];
	printf("请输入要修改的联系人姓名->");
	scanf("%s", name);
	int flag = FindConInfo(name, pc);
	if (flag == -1)
	{
		printf("查无此人\n");
		return;
	}
	else
	{
		int input = 0;
		do
		{
			printf("1.姓名  2.年龄  3.性别  4.住址  5.电话  0.退出\n");
			printf("请输入要修改的选项(按0退出修改)->");
			scanf("%d", &input);
			switch (input)
			{
			case 1:
				printf("请输入改正后的姓名->");
				scanf("%s", pc->data[flag].name);
				break;
			case 2:
				printf("请输入改正后的年龄->");
				scanf("%d", &(pc->data[flag].age));
				break;
			case 3:
				printf("请输入改正后的性别->");
				scanf("%s", pc->data[flag].sex);
				break;
			case 4:
				printf("请输入改正后的住址->");
				scanf("%s", pc->data[flag].addr);
				break;
			case 5:
				printf("请输入改正后的电话->");
				scanf("%s", pc->data[flag].tele);
				break;
			case 0:
				break;
			default:
				printf("输入错误,请重新输入!\n");
				break;
			}
		} while (input);
		printf("修改成功!\n");
	}

}


//对联系人进行排序(按名字)
int compar(const void* e1, const void* e2)
{
	return strcmp(((ConInfo*)e1)->name, ((ConInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), compar);
	printf("对通讯录进行了对于名字的排序!\n");
	ShowContact(pc);
}
//void qsort(void* base, size_t nitems, size_t size, int (*compar)(const void*, const void*))
//参数
//base -- 指向要排序的数组的第一个元素的指针。
//nitems -- 由 base 指向的数组中元素的个数。
//size -- 数组中每个元素的大小,以字节为单位。
//compar -- 用来比较两个元素的函数。



void SearchContact(Contact* pc)//查找联系人
{
	char name[Name_MAX] = "0";
	printf("要查找的联系人姓名->");
	scanf("%s", name);
	int pos = FindConInfo(name, pc);
	if (pos != -1)
	{
		printf("%-7s\t%-7s\t%-6s\t%-20s\t%-15s\n", "姓名", "性别", "年龄", "住址", "电话");

		printf("%-7s\t%-7s\t%-6d\t%-20s\t%-15s\n",
			pc->data[pos].name, pc->data[pos].sex, pc->data[pos].age,
			pc->data[pos].addr, pc->data[pos].tele);
	}
	else
	{
		printf("找不到\n");
	}
}

5.2 game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
#include 
#include 
#include 


#define MAX 1000

#define Name_MAX 20
#define Sex_MAX 5
#define Addr_MAX 30
#define Tele_MAX 12

#define DEFAULT_SZ 3

typedef struct ConInfo
{
	char name[Name_MAX];
	int age;
	char sex[Sex_MAX];
	char addr[Addr_MAX];
	char tele[Tele_MAX];
}ConInfo;

//通讯录的结构体
typedef struct Contact
{
	ConInfo* data;
	int sz;
	int capacity;
}Contact;



void ContactInit(Contact* pc);//初始化通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(const Contact* pc);//显示通讯录
void DeleteContact(Contact* pc);//删除联系人
int FindConInfo(char* name, Contact* pc);//查找联系人,返回其下标
void SearchContact(Contact* pc);//查找联系人
void ModifyContact(Contact* pc);//修改联系人
void SortContact(Contact* pc);//对联系人进行排序(按名字)
void Check_capacity(Contact* pc);//增容
void DestroyContact(Contact* pc);//销毁通讯录
void LoadContact(Contact* pc);//加载通讯录
void SaveContact(Contact* pc);//保存通讯录


5.3 test.c

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include"game.h"
enum Option
{
	EXIT = 1,
	Add,
	Delete,
	Search,
	Modify,
	Sort,
	Show
}input;
void menu()
{
	printf("**********************\n");
	printf("****  1 EXIT   *******\n");
	printf("****  2 ADD    *******\n");
	printf("****  3 Delete *******\n");
	printf("****  4 Search *******\n");
	printf("****  5 Modify *******\n");
	printf("****  6 Sort   *******\n");
	printf("****  7 Show   *******\n");
	printf("**********************\n");
}
int main()
{
	Contact con = { 0 };//建立通讯录
	ContactInit(&con);//通讯录初始化
	do
	{
		menu();
		printf("请选择->\n");
		scanf("%d", &input);
		switch (input)
		{
		case Add:
			AddContact(&con);
			break;
		case Delete:
			DeleteContact(&con);
			break;
		case Search:
			SearchContact(&con);
			break;
		case Modify:
			ModifyContact(&con);
			break;
		case Sort:
			SortContact(&con);
			break;
		case Show:
			ShowContact(&con);
			break;
		case EXIT:
			//保存信息到文件
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}




总结

道路还很长!

你可能感兴趣的:(C语言,c语言)