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 <stdio.h>
#include <string.h>
#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;//当前联系人数量:0memset(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 <stdio.h>
#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 <stdio.h>
#include <string.h>
#include "contact.h"注意:库的头文件和自己定义的头文件引用方式不一样,一个<>,一个""。
- 定义函数:
- 作用:定义后面要用的函数,比如,添加联系人函数,初始化通讯录函数
- 示例://初始化通讯录函数
void InitContact(struct Contact* con)
{
con->sz=0;//当前联系人数量:0
memset(con->data, 0, MAX * sizeof(struct PeopleInfo));//清空数组
}注意:和之前定义函数没什么区别,只不过调用在其他源代码文件里。
主程序文件test.c代码 解析
menu()
函数
作用:打印菜单界面,提示用户操作。
注意:菜单中的数字对应
enum Option
的枚举值。枚举类型
enum Option
作用:用有意义的名称代替数字,增强代码可读性。
示例:
enum Option { EXIT, ADD, DEL, ... }; // EXIT=0, ADD=1, DEL=2...在
switch
语句中使用case ADD
比case 1
更直观。
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
}
分步解析:
con->sz
:当前联系人数量(例如,如果已有3个联系人,con->sz
是3)。
con->data[con->sz]
:访问数组的第con->sz
个位置(即第4个位置,因为数组从0开始)。
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 北京市