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

embedded/2025/3/29 6:18:07/
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代码 解析

 

  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                         北京市


http://www.ppmy.cn/embedded/176478.html

相关文章

如何让Go 的regexp包支持 (?!...) 这样的 Perl 语法?

Go 的标准库 regexp 包基于 RE2 引擎&#xff0c;而 RE2 故意设计为不支持 Perl 风格的复杂特性&#xff08;如 (?!...) 负向前瞻、后顾断言等&#xff09;&#xff0c;这是为了保证正则表达式的执行时间是线性的&#xff08;O(n)&#xff09;&#xff0c;避免潜在的性能问题&…

AI日报 - 2025年3月25日

&#x1f31f; 今日概览&#xff08;60秒速览&#xff09; ▎&#x1f916; AGI突破 | Nebula&#xff08;Google Gemini 2.0 Pro&#xff09;破解复杂数学谜题 编码与推理能力再上新台阶 ▎&#x1f4bc; 商业动向 | Sesame AI开源10亿参数语音模型CSM-1B 语音AI进入普惠时代 …

PTA 1105-链表合并(C++)

给定两个单链表&#x1d43f;1&#x1d44e;1→&#x1d44e;2→⋯→&#x1d44e;&#x1d45b;−1→&#x1d44e;&#x1d45b;L1​a1​→a2​→⋯→an−1​→an​和&#x1d43f;2&#x1d44f;1→&#x1d44f;2→⋯→&#x1d44f;&#x1d45a;−1→&#x1d44f;&#…

使用Python开发自动驾驶技术:车道线检测模型

友友们好! 我是Echo_Wish,我的的新专栏《Python进阶》以及《Python!实战!》正式启动啦!这是专为那些渴望提升Python技能的朋友们量身打造的专栏,无论你是已经有一定基础的开发者,还是希望深入挖掘Python潜力的爱好者,这里都将是你不可错过的宝藏。 在这个专栏中,你将会…

深入理解Linux网络随笔(五):深度理解本机网络I/O

深入理解Linux网络随笔&#xff08;五&#xff09;&#xff1a;深度理解本机网络I/O 文章目录 深入理解Linux网络随笔&#xff08;五&#xff09;&#xff1a;深度理解本机网络I/O本机发送过程本机接收过程总结 分析本机网络I/O部分源码需要知道本机I/O是什么&#xff1f;扮演什…

大数据学习(77)-Hive详解

&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一…

Modbus协议编程读写流程图大全

读离散量输入 读保持寄存器 读输入寄存器 写单个线圈 写单个寄存器 写多个线圈 写多个寄存器 (0x14) 读文件记录 写文件记录 (0x16) 屏蔽写寄存器 (0x17) 读/写多个寄存器

从零手撕C++ string类:详解实现原理与优化技巧

&#x1f4cc; 引言 C标准库中的std::string是日常开发中最常用的类之一&#xff0c;但你是否好奇它的底层实现&#xff1f;本文将带你从零实现一个简化版string类&#xff08;命名空间tyx&#xff09;&#xff0c;覆盖构造、拷贝、动态扩容、运算符重载等核心功能&#xff0c…