(C语言)静态通讯录(测试版)(C语言小项目)

1.首先是头文件:
//头文件
//contact.h

//防止头文件被重复包含
#pragma once
//定义符号常亮,方便维护和修改
//联系人基本信息容量
#define NAME_MAX 20
#define AGE_MAX 5
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 30
//联系人最大容量100
#define MAX 100

//定义联系人结构体
struct PeopleInfo
{
	char name[NAME_MAX];
	char age[AGE_MAX];
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
};
//定义通讯录结构体
struct Contact
{
	struct PeopleInfo data[MAX];
	int sz;
};

//声明函数
void AddContact(struct Contact* con);
void DelContact(struct Contact* con);
void ShowContact(struct Contact* con);
int FindContact(const struct Contact* con,char name[]);
void InitContact(struct Contact* con);
void menu();
2. 然后是功能函数contact.c文件
//功能函数文件
//contact.c	

#include 
#include 
#include "contact.h"

//定义菜单函数
void menu()
{
	printf("*********************************************\n");
	printf("******** 1.添加        2.删除  **************\n");
	printf("******** 3.查询        4.修改  **************\n");
	printf("******** 5.查看        6.排序  **************\n");
	printf("******** 7.清空        0.退出  **************\n");
	printf("*********************************************\n");
}

//初始化通讯录函数
void InitContact(struct Contact* con)
{
	con->sz=0;//当前联系人数量:0
	memset(con->data, 0, MAX * sizeof(struct PeopleInfo));//清空数组
}

//添加联系人函数
void AddContact(struct Contact* con) {
	//检查通讯录是否溢出
	if (con->sz == MAX) {
		printf("通讯录已满!\n");
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", con->data[con->sz].name);
		printf("请输入年龄:");
		scanf("%s", con->data[con->sz].age);
		printf("请输入性别:");
		scanf("%s", con->data[con->sz].sex);
		printf("请输入电话:");
		scanf("%s", con->data[con->sz].tele);
		printf("请输入地址:");
		scanf("%s", con->data[con->sz].addr);
		printf("添加成功!\n");
		(con->sz)++;
	}
}

//查找当前联系人函数
int  FindContact(const struct Contact* con,char name[]) {
	for (int i = 0; i < con->sz; i++) {
		//利用比较函数strcmp判断姓名是否相等
		if (strcmp(con->data[i].name, name) == 0) {
			return 0;
		}
		else
		{
			printf("用户不存在!\n");
			return -1;
		}
	}
}

//删除联系人函数
void DelContact(struct Contact* con) {
	if (con->sz == 0) {
		printf("通讯录为空!\n");
	}
	else
	{
		printf("请输入要删除的联系人姓名:\n");
		char name[NAME_MAX];
		int ret = FindContact(con, name);
		if (ret != -1) {
			printf("删除成功!\n");
			for (int i = 0; i < con->sz-1; i++) {
				con->data[i] = con->data[i + 1];
			}
			(con->sz)--;
		}
	}
}

//查询当前通讯录
void ShowContact(struct Contact* con) {
	if (con->sz == 0) {
		printf("该通讯录为空\n");
	}
	else {
		printf("%15s\t%5s\t%8s\t%15s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");

		for (int i = 0; i < con->sz; i++) {
			printf("%15s\t%5s\t%8s\t%15s\t%30s\n", con->data[i].name,
				con->data[i].age, con->data[i].sex, con->data[i].tele, con->data[i].addr);
		}
	}
	
}
3.最后是主程序test.c文件:
//主程序文件
//test.c

#include 
#include "contact.h"

//枚举条件选择定义(搭配switch使用)
enum Option
{
	EXIT,//0,对应退出通讯录
	ADD,//1,对应添加联系人
	DEL,//2,对应删除联系人
	SEARCH,//3,对应查询联系人
	MODIFY,//4,对应修改联系人
	SHOW,//5,对应查看通讯录
	SORT,//6,对应排序通讯录
	CLEAR,//7,对应清空通讯录
};

//主函数
int main()
{
	int input = 0;
	//创建通讯录
	struct Contact con;
	//调用函数初始化通讯录
	InitContact(&con);//传递参数地址
	do
	{
		//打印菜单
		menu();
		printf("请选择对应模式(0-7):\n");
		if (scanf("%d", &input) != 1 && input < 0 && input>7) {
			printf("输入不合法,请输入整数0-7\n");
			return 1;
		}
		switch (input)
		{
		case EXIT: {
			printf("退出通讯录!\n");
			break;
		}
		case ADD: {
			AddContact(&con);
			break;
		}
		case DEL: {
			DelContact(&con);
			break;
		}
		case SHOW: {
			ShowContact(&con);
			break;
		}
		default:
			break;
		}

	} while (input);
}

整个项目只有三个文件,头文件和两个源代码

本次测试版只有4个模块,添加联系人,删除联系人,查询整个通讯录,退出通讯录

里面有很多一开始我们没见过的东西下面来逐步讲解一下代码:

头文件contact.h代码解析:
  • #pragma once

    • 作用:防止头文件被重复包含。如果多个源文件包含同一个头文件,可能会导致结构体重复定义的错误。

    • 示例:如果 test.c 和 contact.c 都包含 contact.h,编译器只会处理一次。

 

  • #define 定义常量

    • 作用:定义符号常量,方便修改和维护。

    • 示例

      #define NAME_MAX 20 // 姓名最大长度(含结尾的'\0')
    • 表示定义一个常量NAME_MAX,它的值是20
    • 如果未来需要扩展姓名字段,只需修改此处的值。

  • 结构体定义

    • 作用:将多个不同类型的数据组合成一个整体。

    • 示例

      struct PeopleInfo { ... }; // 存储一个人的信息
      struct Contact { ... };     // 存储整个通讯录

注意:结构体的成员可以是数组(如 char name[NAME_MAX]),也可以是基本类型(如 int age)。其实就是个特殊的类型,你可以把它理解成一个函数型(需要提前定义)的数据类型

例如:struct Contact就相当于int,struct Contact con就相当于int i,表示形式差不多,这样理解比较方便。

  • 源代码函数的声明 
    • 作用:提前告诉编译器你自己定义的函数,安全,快速
    • 示例

                void InitContact(struct Contact* con);
                void menu();

注意:就是相当于平常写函数的时候的声明,只不过,函数在源文件中定义和调用,声明在头文件中。

 功能函数文件contact.c代码解析
  •  调用头文件
    • 作用:调用库的头文件和自己定义的头文件
    • 示例

                #include
                #include
                #include "contact.h"

注意:库的头文件和自己定义的头文件引用方式不一样,一个<>,一个""。

  •  定义函数:
    • 作用:定义后面要用的函数,比如,添加联系人函数,初始化通讯录函数
    • 示例://初始化通讯录函数
      void InitContact(struct Contact* con)
      {
          con->sz=0;//当前联系人数量:0
          memset(con->data, 0, MAX * sizeof(struct PeopleInfo));//清空数组
      }

注意:和之前定义函数没什么区别,只不过调用在其他源代码文件里。

主程序文件test.c代码 解析

 

  1. menu() 函数

    • 作用:打印菜单界面,提示用户操作。

    • 注意:菜单中的数字对应 enum Option 的枚举值。

  2. 枚举类型 enum Option

    • 作用:用有意义的名称代替数字,增强代码可读性。

    • 示例

      enum Option { EXIT, ADD, DEL, ... }; // EXIT=0, ADD=1, DEL=2...

      在 switch 语句中使用 case ADD 比 case 1 更直观。

  3. main 函数逻辑

    • do...while 循环:至少执行一次循环,直到用户输入0(EXIT)。

    • switch 分支:根据用户输入调用不同的功能函数。

    • 结构体参数传递:所有函数都通过指针(&con)操作通讯录,避免复制整个结构体。

 部分关键代码解析

1. con->sz

  • 是什么con 是一个指向 struct Contact 结构体的指针,sz 是 struct Contact 中的一个成员变量。

  • 作用:记录通讯录中当前存储的联系人数量。

  • 代码示例

    struct Contact {
        struct PeopleInfo data[MAX]; // 存储联系人信息的数组
        int sz; // 当前通讯录中的联系人数量
    };

    • 假设 con 是 struct Contact 类型的指针:

      struct Contact con; // 创建一个通讯录
      struct Contact* p = &con; // 指向通讯录的指针
      p->sz = 0; // 初始化联系人数量为0

    • con->sz 等价于 (*con).sz,表示通过指针访问结构体成员。


2. con->data

  • 是什么data 是 struct Contact 中的一个成员,它是一个数组,类型是 struct PeopleInfo,用于存储所有联系人的信息。

  • 作用:存储通讯录中的联系人数据。

  • 代码示例

    struct PeopleInfo {
        char name[NAME_MAX];
        int age;
        char sex[SEX_MAX];
        // ...其他字段
    };

    struct Contact {
        struct PeopleInfo data[MAX]; // 最多存储MAX个联系人
        int sz;
    };

    • con->data 表示访问通讯录中的联系人数组。

    • con->data[0] 表示第一个联系人,con->data[1] 表示第二个联系人,以此类推。


3. con->data[con->sz].name

  • 是什么:访问通讯录中第 con->sz 个联系人的 name 字段。

  • 作用:修改或读取指定位置的联系人姓名。

  • 代码示例

    // 添加一个新联系人
    void AddContact(struct Contact* con) {
        if (con->sz == MAX) {
            printf("通讯录已满!\n");
            return;
        }
        // 输入姓名并存储到第 con->sz 个位置
        printf("请输入姓名:");
        scanf("%s", &(con->data[con->sz].name)); 
        con->sz++; // 联系人数量+1
    }

    • 分步解析

      1. con->sz:当前联系人数量(例如,如果已有3个联系人,con->sz 是3)。

      2. con->data[con->sz]:访问数组的第 con->sz 个位置(即第4个位置,因为数组从0开始)。

      3. con->data[con->sz].name:修改第 con->sz 个联系人的姓名。

 部分运行结果如下:

*********************************************
******** 1.添加        2.删除  **************
******** 3.查询        4.修改  **************
******** 5.查看        6.排序  **************
******** 7.清空        0.退出  **************
*********************************************
请选择对应模式(0-7):
1
请输入姓名:张三
请输入年龄:18
请输入性别:男
请输入电话:12345678901
请输入地址:北京市
添加成功!
*********************************************
******** 1.添加        2.删除  **************
******** 3.查询        4.修改  **************
******** 5.查看        6.排序  **************
******** 7.清空        0.退出  **************
*********************************************
请选择对应模式(0-7):
5
           姓名  年龄       性别                   电话                           地址
           张三    18         男            12345678901                         北京市

你可能感兴趣的:(C语言小项目,C语言基础教学,c语言,c++,开发语言,算法,游戏,数据结构)