C语言小项目实战之通讯录功能

2025-01-30 04:50

本文主要是介绍C语言小项目实战之通讯录功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下...

功能介绍:

1.联系人信息:姓名+年龄+性别+地址+电话

2.通讯录中可以存放100个人的信息

3.功能:

1>增加联系人

2>删除指定联系人

3>查找指定联系人的信息

4>修改指定联系人的信息

5显示所有联系人的信息

6>排序(名字)

  我们将采用模块化设计,分为三个模块:

1. test.c         ———        测试通讯录

2. contact.c   ———        通讯录的声明

3. contact.h    ———       函数的声明

  首先,我们需要一个菜单,以及主函数选择功能的部分。

```test.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
 void menu()
{
    printf("*******************欢迎来到你的通讯录*******************\n");
    printf("**********1.添加联系人************2.删除联系人**********\n");
    printf("**********3.查找联系人************4.修改联系人**********\n");
    printf("**********5.显示联系人************6.联系人排序**********\n");
    printf("**********0.退出通讯录*********************************\n");
    printf("*******************欢迎来到你的通讯录*******************\n");
}
int main()
{
    int input = 0;
    do {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
 
    switch (input)
    {
    case 1:
        break;
    case 2:
        break;
    case 3:
        break;
    case 4:
        break;
    case 5:
        break;
    case 6:
        break;
    case 0:
        printf("退出通讯录China编程");
        break;
    default:
        printf("选择错误\n");
        break;
    }
   } while (input);
	return 0;
}
```
  • main函数中,使用do-while循环不断调用menu函数显示菜单,并使用scanf函数获取用户的输入。然后通过switch语句根据用户的输入执行相应的操作,如果输入为0,则输出 “退出通讯录” 并结束程序;如果输入为其他值,则输出 “选择错误”。

那么通讯录中肯定需要人的信息,那么这些人的信息,我们可以把他封装成一个结构体。我们将这些类型信息放进头文件中,在这里我们将它放进已经创建好的contact.h中。

```contact.h
//人的信息
typedef struct Peopleinform
{
	char name[40];
	int age;
	char sex[5];
	char addr[20];
	char telephone[20];
}Peopleinform;
```

在这里,我们定义了一个名为Peopleinform的结构体类型,用于存储人员的信息。我们通过使用typedef关键字,可以为这个结构体类型定义一个别名Peopleinform,这样在后续的代码中就可以直接使用Peopleinform来声明该类型的变量,而不需要每次都写完整的struct Peopleinform

我们继续往下写,接下来我们创建通讯录,我们可以声明一个名为data的数组,数组的元素类型为Peopleinform结构体,当然我们在这里必须添加contact.h头文件才能使用它。代码部分如下:

```test.c
#include<stdio.h>
#include"contact.h"
/* --省略void menu部分--*/
int main()
{
    int input = 0;
    //创建通讯录
    Peopleinform data[100];
    do {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
    
    /*-- 省略switch部分 --*/
        } while (input);
    return 0;
}
```

 在我们的项目中,自己创建的头文件通常用双引号''"包裹起来,这样我们就可以在.c文件中使用它啦!当然,我们如果把这个通讯录存放人的信息的容量,以及可以显示的当前创建的人的信息封装起来,变成一个结构体。为了方便之后修改我们增加一个预处理器指令#define MAX 100

```contact.h
#define MAX 100
typedef struct Contact
{
	Peopleinform data[MAX];//存放人的信息的容量
	int sz;//当前存放信息的个数
}Contact;
```

那么我们如果想在主函数中使用,只需要创建一个名为conContact类型的对象。这样就可以使用con来存储和操作通讯录相关的数据了。接着我们初始化通讯录。并在contact.h头文件中为他声明。

```test.c
int main()
{
    int input = 0;
    //创建通讯录
    Contact con;//创建一个名为con的Contact对象
    //初始化通讯录
    InitContact(&con);
    do {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        /*-- 省略switch部分 --*/
    } while (input);
```
```contact.h
void InitContact(Contact *pc);
```

接下来,contact.c文件终于出场了,由于我们在contact.h头文件声明过void InitContact(Contact *pc); 接下来,我们就可以在contact.c直接使用它。

我们给这个Contact结构体的函数初始化一下:

```contact.c
#include"contact.h"
void InitContact(Contact* pc)
{
    pc->sz=0;
   memset(pc->data,0,sizeof(pc->data));
}
```

这里我们选择使用memset函数进行初始化,

(注:memset函数是 C 标准库中的一个函数,定义在<string.h>头文件中。它的作用是将一段内存区域设置为指定的值。函数原型为void *memset(void *s, int c, size_t n),其中s是指向要填充的内存块的指针,c是要设置的值,n是要填充的字节数。) 

紧接着,我们在Switch()语句中,加入我们目前需要的语句,我们现在来写添加联系人模块。

添加联系人模块

```contact.h
//增加联系人
void AddContact(Contact* pc);
```
```contact.c
void AddContact(Contact* pc)
{
	if (pc->sz == MAX)
	{
		printf("通讯录已满!无法添加!\n");
		return;
	}
	//增加一个人的信息
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%s", &pc->data[pc->sz].age);
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].telephone);
	pc->sz++;
}
```

(注:在InitContactAddContact函数中,pc用于访问和修改调用者提供的Contact结构体实例。当调用这些函数时,传递的是Contact结构体变量的地址,函数内部通过这个指针来操作外部传入的结构体。)

pc-sz++; 的作用 

假设 sz 初始值为 5,执行 pc->sz++; 后,sz 的值变为 6 ,表示通讯录中联系人的数量从 5 个增加到了 6 个。 这对于跟踪通讯录中实际存储的联系人数量非常重要,在后续的操作,如显示联系人列表、检查通讯录是否已满等功能中,都依赖这个计数。

显示联系人模块

显示联系人模块也是一样,我们现将函数声明放在头文件当中,

void ShowContact(const Contact* pc);

然后我们用一个for循环遍历pc->data数组,其中,pc->data数组存储了所有联系人的信息,循环条件i < pc->sz确保只遍历已存储联系人的部分,pc->sz表示当前通讯录中实际存储的联系人数量。

```contact.c
void ShowContact(Contact* pc)
{
	int i = 0;
    printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名","年齡","性別","地址","電話");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%s\t%d\t%s\tChina编程%s\t%s\t",pc->data[i].name,
									  pc->data[i].age,
									  pc->data[i].sex,
									  pc->data[i].addr,
									  pc->data[i].telephone);
	}
}
```

这样,我们就基本完成了这个显示联系人的设计啦!那么紧接着,我们如果想要删除这个指定的联系人,该怎么办呢?和上面一样,我们先在头文件中添加一个声明。

void DelContact(Contact* pc);

接着,我们在contact.c文件中去构建这个删除联系人模块,

删除联系人模块

void DelContact(Contact* pc)
{
	if (pc->sz ==0)
China编程	{
		printf("无可删除的联系人");
		return;
	}
	//删除
	//找到要删除的人
	printf("请输入要删除的人的名字:");
	scanf("%s", name);
	int i = 0;
	int del = 0;
	for (i = 0; i<pc->sz; i++)
	{
		if(strcmp(pc->data[i].name,name))==0
		{
			del = i;
			break;
		}
		//删除
		for (i = 0; i < pc->sz; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		pc->sz--;
		printf("删除成功");
	}
}

当我们通讯录里面没有联系人的时候,我们输出“无可删除的联系人”。接着,我们使用 for 循环遍历联系人列表,使用 strcmp 函数比较每个联系人的名字和输入的名字,如果相等则将该元素的索引存储在 del 中并跳出循环。

那么接下来,我们可以发现的是,无论是删除模块、查找模块和修改模块中,我们都需要先找到联系人,才能进行下一步的操作,既然都需要,我们似乎就可以把这个查找部分封装成一个函数,

// 根据姓名查找联系人在数组中的位置,如果找到返回对应索引,未找到返回 -1
int FINDBY_NAME(const Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0) // 比较字符串是否相等
		{
			return i; // 找到返回索引
		}
	}
	return -1; // 未找到返回 -1
}

 替换后代码如下:

// 删除联系人的函数
void DelContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("没有联系人可删除\n");
		return;
	}
	// 输入要删除的联系人姓名
	char name[40] = { 0 };
	printf("请输入要删除的联系人姓名: ");
	scanf("%s", name);

	int ret = FINDBY_NAME(pc, name);
	if (ret == -1)
	{
		printf("要删除的联系人不存在\n");
		return;
	}

	// 删除操作,将后面的元素依次往前移
	for (int i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

我们来看查找联系人模块,

查找联系人模块

void SerContact(const Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找人的名字:");
		scanf("%s",name);
	int pos = FINDBY_NAME(pc, name);
	if (-1 == pos)
	{
		printf("要查找的人不存在\n");
		return;
	}
	//打印信息
	printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名", "年齡", "性別", "地址", "電話");
	printf("%s\t%-5d\t%-5s\t%-5s\t%-5s\t\n", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].addr,
		pc->data[pos].telephone);
}

 我们查找模块,首先输入要查找的联系人姓名,调用 FINDBY_NAME 函数查找该联系人的位置,如果不存在则提示联系人不存在并返回。否则,打印该联系人的详细信息。

紧接着,我们再来看修改联系人,

修改联系人模块

//修改联系人
void ReviContact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	printf("请输入要修改人的名字:");
		scanf("%s", name);
	int pos = FINDBY_NAME(pc, name);
	if (-1 == pos)
	{
		printf("要修改的人不存在\n");
		return;
	}
	printf("请输入名字:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &pc->data[pos].age);
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].telephone);

	printf("修改完成\n");
}

我们想要修改联系人,首先肯定需要找到这个联系人,所以我们首先要查找,输入要修改的联系人姓名,调用FINDBY_NAME函数可以查找该联系人的位置,如果不存在则提示联系人不存在并返回。否则,依次输入新的联系人信息。

排序联系人模块

最后一个模块,就是对这些联系人进行排序,这里使用使用冒泡排序算法,比较相邻联系人的姓名,如果前一个联系人的姓名大于后一个联系人的姓名,则交换它们的位置。

// 排序联系人(按姓名升序排序)
void SortContact(Contact* pc)
{
	int i, j;
	Peopleinform temp;
	for (i = 0; i < pc->sz - 1; i++)
	{
		for (j = 0; j < pc->sz - i - 1; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				temp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = temp;
			}
		}
	}
	printf("联系人已按姓名排序\n");
}

strcmp 是 C 语言标准库 <string.h> 中的一个字符串比较函数,用于比较两个字符串的大小。

函数原型int strcmp(const char *str1, const char *str2);

在 SortContact 函数中,strcmp(pc->data[j].name, pc->data[j + 1].name) > 0 表示如果 pc->data[j].name 这个字符串在字典序上大于 pc->data[j + 1].name 这个字符串,就交换这两个联系人的信息,从而实现按姓名排序的功能。

源代码如下:

```contact.h
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME_MAX 40  // 定义 NAME_MAX 的大小
// 个人信息
typedef struct Peopleinform
{
    char name[40];
    int age;
    char sex[5];
    char addr[20];
    char tehttp://www.chinasem.cnlephone[20];
}Peopleinform;
typedef struct Contact
{
    Peopleinform data[MAX];// 存储个人信息的数组
    int sz;// 当前存储信息的数量
}Contact;

// 初始化通讯录
void InitContact(Contact* pc);
// 添加联系人
void AddContact(Contact* pc);
// 删除联系人
void DelContact(Contact* pc);
// 查找联系人
void SerContact(const Contact* pc);
// 修改联系人
void ReviContact(Contact* pc);
// 排序联系人
void SortContact(Contact* pc);
// 显示联系人
void ShowContact(const Contact* pc);
```
```contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void InitContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == MAX)
	{
		printf("通讯录已满!无法添加!\n");
		return;
	}
	//增加一个人的信息
	printf("请输入名字:>");
	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].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].telephone);
	pc->sz++;
}

// 根据姓名查找联系人在数组中的位置,如果找到返回对应索引,未找到返回 -1
int FINDBY_NAME(const Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0) // 比较字符串是否相等
		{
			return i; // 找到返回索引
		}
	}
	return -1; // 未找到返回 -1
}

// 删除联系人的函数
void DelContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("没有联系人可删除\n");
		return;
	}
	// 输入要删除的联系人姓名
	char name[40] = { 0 };
	printf("请输入要删除的联系人姓名: ");
	scanf("%s", name);

	int ret = FINDBY_NAME(pc, name);
	if (ret == -1)
	{
		printf("要删除的联系人不存在\n");
		return;
	}

	// 删除操作,将后面的元素依次往前移
	for (int i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}
//查找联系人
void SerContact(const Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找人的名字:");
		scanf("%s",name);
	int pos = FINDBY_NAME(pc, name);
	if (-1 == pos)
	{
		printf("要查找的人不存在\n");
		return;
	}
	//打印信息
	printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名", "年齡", "性別", "地址", "電話");
	printf("%s\t%-5d\t%-5s\t%-5s\t%-5s\t\n", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].addr,
		pc->data[pos].telephone);
}
//修改联系人
void ReviContact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	printf("请输入要修改人的名字:");
		scanf("%s", name);
	int pos = FINDBY_NAME(pc, name);
	if (-1 == pos)
	{
		printf("要修改的人不存在\n");
		return;
	}
	printf("请输入名字:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &pc->data[pos].age);
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].telephone);

	printf("修改完成\n");
}
// 排序联系人(按姓名升序排序)
void SortContact(Contact* pc)
{
	int i, j;
	Peopleinform temp;
	for (i = 0; i < pc->sz - 1; i++)
	{
		for (j = 0; j < pc->sz - i - 1; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				temp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = temp;
			}
		}
	}
	printf("联系人已按姓名排序\n");
}

// 显示所有联系人信息的函数
void ShowContact(const Contact* pc)
{
	int i = 0;
	printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名", "年龄", "性别", "地址", "电话");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%s\t%d\t%s\t%s\t%s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].addr,
			pc->data[i].telephone);
	}
}
```
```test.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
 void menu()
{
    printf("*******************欢迎来到你的通讯录*******************\n");
    printf("**********1.添加联系人************2.删除联系人**********\n");
    printf("**********3.查找联系人************4.修改联系人**********\n");
    printf("**********5.显示联系人************6.联系人排序**********\n");
    printf("**********0.退出通讯录**********************************\n");
    printf("*******************欢迎来到你的通讯录*******************\n");
}
int main()
{
    int input = 0;
    //创建通讯录
    Contact con;//创建一个名为con的Contact对象
    //初始化通讯录
    InitContact(&con);
    do {
        menu();
        printf("请选择:>");
        if (scanf("%d", &input) != 1) {
            // 处理输入错误的情况,例如提示用户重新输入
            printf("输入错误,请重新输入\n");
            // 清空输入缓冲区
            while (getchar() != '\n'www.chinasem.cn);
            
            continue;
        }
    
    switch (input)
    {
    case 1:
        AddContact(&con);
        break;
   case 2:
        DelContact(&con);
        break;
   case 3:
        SerContact(&con);
        break;
    case 4:
        ReviContact(&con);
        break;
    case 5:
        ShowContact(&con);
        break;
    case 6:
        SortContact(&con);
    case 0:
        printf("退出通讯录");
        break;
    default:
        printf("选择错误\n");
        break;
    }
   } while (input!=0);
}
```

总结

到此这篇关于C语言小项目实战之通讯录功能的文章就介绍到这了,更多相关C语言通讯录内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于C语言小项目实战之通讯录功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153276

相关文章

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

sky-take-out项目中Redis的使用示例详解

《sky-take-out项目中Redis的使用示例详解》SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现... 目录Spring Cache主要特性核心注解1.@Cacheable2.@CachePut3.@Ca

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶