C++ 控制台通讯录管理系统 —— 从零实现到完整解析(附可运行代码)

文章目录

关键词 :C++ 入门|结构体实战|数组操作|控制台项目|课程设计
适合人群 :刚学完 C++ 基础语法,想动手做点东西的同学
一句话总结:100 行核心逻辑 + 8 个功能模块 = 你的第一个完整 C++ 项目!


一、为什么这个"小破项目"值得你认真做?

很多初学者学完 iffor、函数、结构体后,总觉得"好像会了,又好像不会"。问题出在哪?------缺少一个能把知识点串起来的载体

而"通讯录管理系统"正是这样一个天然的教学项目

  • 它有真实场景(谁不用通讯录?)
  • 它需要数据组织(姓名、电话怎么存?)
  • 它涉及基本操作(增删改查是所有系统的基石)
  • 它能暴露常见 Bug(比如搜索只查第一个?)
  • 它可逐步扩展(从控制台到文件,再到图形界面)

更重要的是:它不依赖任何外部库,纯标准 C++,编译即运行!


二、系统要实现哪些功能?

我们设计一个菜单驱动的控制台程序,用户通过输入数字选择操作:

复制代码
****** 1、添加联系人 *****
****** 2、显示联系人 *****
****** 3、删除联系人 *****
****** 4、查找联系人 *****
****** 5、修改联系人 *****
****** 6、清空联系人 *****
****** 0、退出通讯录 *****

这 7 个选项,覆盖了数据管理的全部生命周期。看似简单,但每一步都藏着细节。


三、如何组织数据?------ 结构体是关键

第一步:定义"一个人"

我们需要把姓名、性别、年龄、电话、地址打包在一起。C++ 的 struct 正好胜任:

cpp 复制代码
struct Person {
    string name;
    int sex;      // 1=男,2=女
    int age;
    string phone;
    string Addr;  // 注意字段名是 Addr
};

💡 为什么用 int 表示性别?

为了简化输入校验。如果用字符串(如 "male"/"female"),还要处理大小写、拼写错误等问题。用 1/2,判断只需 if (sex == 1 || sex == 2),干净利落。


第二步:定义"通讯录本"

一个人不够,我们要存很多人。于是再套一层结构体:

cpp 复制代码
const int MAX = 1000;

struct Addressbook {
    Person PersonArry[MAX]; // 最多存 1000 人
    int size;               // 当前有多少人
};

这里用静态数组 而非 vector,是为了降低初学者的理解门槛。虽然牺牲了动态扩容能力,但换来的是内存布局清晰、访问速度快、无需手动管理内存的优势。

size 是灵魂!它告诉我们哪些数据是有效的,避免遍历整个 1000 元素的数组。


四、核心难点解析:搜索函数为何容易出错?

很多同学在实现"删除"或"查找"时发现:只能操作第一个联系人,后面的全找不到!

问题就出在 search 函数的逻辑上。

❌ 错误写法(典型陷阱):

cpp 复制代码
for (...) {
    if (匹配) return i;
    else return -1;   // ← 一旦不匹配,立刻退出!
}

这个逻辑意味着:只检查第一个人 。如果不匹配,函数直接返回 -1,后面的人根本没机会被看一眼。

✅ 正确思路:

  • 遍历所有有效联系人
  • 只要找到就立即返回索引
  • 全部遍历完还没找到,才返回 -1
cpp 复制代码
int search(Addressbook* a, string name) {
    for (int i = 0; i < a->size; i++) {
        if (a->PersonArry[i].name == name)
            return i;
    }
    return -1; // 循环结束才返回 -1
}

🔑 记住return -1 必须在循环外面

这个函数是"删除"、"查找"、"修改"三大功能的共同依赖,必须万无一失。


五、其他功能的设计巧思

1. 添加联系人:不只是"存数据"

  • 先检查是否已满(size >= MAX
  • 对性别、年龄做合法性校验 (用 while(true) 循环保证输入正确)
  • 成功后 size++,并提示用户

🌟 这里体现了防御性编程思想:不信任用户输入,主动拦截非法值。


2. 删除联系人:数组元素如何"移除"?

C++ 数组不能真正"删除"中间元素,但我们可以通过前移覆盖实现逻辑删除:

复制代码
[张三][李四][王五][赵六]  ← 删除"李四"
       ↑
       idx=1

步骤:
1. 将"王五"复制到 idx=1 的位置
2. 将"赵六"复制到 idx=2 的位置
3. size 减 1 → 变成 3

最终效果:[张三][王五][赵六][赵六],但因为我们只看前 size=3 个,最后一个"赵六"被忽略。

⚠️ 注意循环边界i < size - 1,否则会越界访问 PersonArry[size]


3. 清空联系人:最高效的操作

很多人以为要 for 循环把每个元素设为空,其实只需 size = 0

因为后续的"添加"操作会从 PersonArry[0] 开始覆盖,旧数据自然失效。这是逻辑清空 vs 物理清空的经典案例。


4. 用户交互体验

  • 每次操作后调用 system("cls") 清屏,保持界面整洁
  • 使用 ✅❌🔍 等符号增强提示可读性
  • 无效输入时给出明确错误信息(如"年龄应在1~109之间")

这些细节让程序从"能用"变成"好用"。


六、主程序:一切的调度中心

main() 函数非常简洁,就是一个无限循环 + 菜单分发

cpp 复制代码
Addressbook book = {0}; // 初始化 size=0

while (true) {
    showMenu();
    cin >> choice;
    switch(choice) {
        case 1: input1(&book); break;
        case 2: output2(&book); break;
        // ... 其他 case
        case 0: return 0; // 优雅退出
    }
}

这种事件驱动的结构,是几乎所有交互式程序的基础模型。


七、你能从中学到什么?

能力 具体体现
结构化思维 把复杂问题拆解为"人"和"通讯录"两个结构体
算法意识 理解线性查找的时间复杂度 O(n)
内存观念 知道静态数组的优缺点
工程习惯 函数命名清晰(即使叫 def 也比 f1 好)、加注释、做校验
调试能力 学会用"打印中间变量"定位逻辑错误

📌 重点不是代码多炫酷,而是你是否理解每一行为什么存在。


八、下一步可以怎么玩?

完成基础版后,不妨挑战这些扩展:

  1. 持久化存储

    程序退出时把联系人写入 contacts.txt,启动时读取。这样数据就不会丢失!

  2. 防止重复姓名

    添加前先搜索,如果已存在,提示"该联系人已存在"。

  3. 模糊搜索

    输入"张",能查出"张三"、"张伟"等所有姓张的人(用 string::find())。

  4. 按年龄排序显示

    实现一个简单的冒泡排序,让联系人按年龄从小到大排列。

  5. 图形界面(GUI)

    用 EasyX 库画个窗口,点击按钮操作,告别黑乎乎的控制台!


九、结语:小项目,大成长

别小看这个只有几百行的通讯录。它可能是你第一个真正"属于你"的程序------从需求分析、设计、编码、调试到优化,全程由你掌控。

当你看到自己写的程序成功添加、删除、查找联系人时,那种成就感,远胜于刷十道选择题。

编程的真谛,不在语法,而在创造。

👉 现在,就去敲下第一行代码吧!


十、完整代码(可运行)

c 复制代码
#include<iostream>
using namespace std;
void showMenu() {
	cout << "**************************" << endl;
	cout << "****** 1、添加联系人 *****" << endl;
	cout << "****** 2、显示联系人 *****" << endl;
	cout << "****** 3、删除联系人 *****" << endl;
	cout << "****** 4、查找联系人 *****" << endl;
	cout << "****** 5、修改联系人 *****" << endl;
	cout << "****** 6、清空联系人 *****" << endl;
	cout << "****** 0、退出通讯录 *****" << endl;
	cout << "**************************" << endl;
}
const int MAX = 1000;

struct Person {
	string name;  //姓名
	int sex;      //性别:1男,2女
	int age;      //年龄
	string phone; //电话
	string Addr;  //地址
};
struct Addressbook {
	struct Person PersonArry[MAX]; //通讯录保存的成员数组
	int size;     //通讯录成员个数
};

//添加联系人
void input1(Addressbook* a)
{
	if (a->size >= MAX)
	{
		cout << "通讯录已满" << endl;
		return;
	}
	else
	{
		cout << "请输入姓名" << endl;
		cin >> a->PersonArry[a->size].name;
		cout << "请输入性别:   1表示男,2表示女" << endl;
		int sex = 0;
		while (true)
		{
			cin >> sex;
			if (sex == 1 || sex == 2)
			{
				a->PersonArry[a->size].sex = sex;
				break;
			}
			else {
				cout << "请重新输入" << endl;
			}
		}
		cout << "请输入年龄" << endl;
		int age = 0;

		while (true)
		{
			cin >> age;
			if (age > 0 && age < 110)
			{
				a->PersonArry[a->size].age = age;
				break;
			}
			else
			{
				cout << "请重新输入" << endl;
			}
		}

		cout << "请输入电话" << endl;
		cin >> a->PersonArry[a->size].phone;
		cout << "请输入地址" << endl;
		cin >> a->PersonArry[a->size].Addr;
		cout << "添加成功" << endl;
		a->size++;
		system("pause");  //按任意键继续
		system("cls");   //清屏
	}
}

//显示联系人
void output2(Addressbook* a) {
	if (a->size == 0)
	{
		cout << "联系人为空" << endl;
		system("pause");
		system("cls");
	}
	else
	{
		for (int i = 0; i < a->size; i++)
		{
			cout << "姓名:" << a->PersonArry[i].name << "\t";
			cout << "性别:" << (a->PersonArry[i].sex == 1 ? "男" : "女") << "\t";
			cout << "年龄:" << a->PersonArry[i].age << "\t";
			cout << "电话:" << a->PersonArry[i].phone << "\t";
			cout << "地址:" << a->PersonArry[i].Addr << "\t";
			cout << endl;
		}
		system("pause");
		system("cls");
	}
}

//搜索联系人
int search(Addressbook* a, string name)
{
	for (int i = 0; i < a->size; i++)
	{
		if (a->PersonArry[i].name == name)
		{
			return i;
		}

	}
	return -1;
}

//删除联系人
void def(Addressbook* a)
{
	cout << "请输入查找的联系人" << endl;
	string name;
	cin >> name;
	int x = search(a, name);
	if (x != -1)
	{
		for (int i = x; i < a->size; i++)
		{
			a->PersonArry[i] = a->PersonArry[i + 1];
		}
		cout << "删除成功" << endl;
		a->size--;
	}
	else if (x == -1)
	{
		cout << "查无此人" << endl;
	}
	system("pause");
	system("cls");
}

//查找联系人
void cha(Addressbook* a) {
	cout << "请输入查找的联系人" << endl;
	string name;
	cin >> name;
	int x = search(a, name);
	if (x != -1)
	{
		cout << "姓名:" << a->PersonArry[x].name << "\t";
		cout << "性别:" << (a->PersonArry[x].sex == 1 ? "男" : "女") << "\t";
		cout << "年龄:" << a->PersonArry[x].age << "\t";
		cout << "电话:" << a->PersonArry[x].phone << "\t";
		cout << "地址:" << a->PersonArry[x].Addr << "\t";
		system("pause");
		system("cls");
	}
	else {
		cout << "无此联系人 " << endl;
	}
}

//修改联系人
void xiu(Addressbook* a) {
	cout << "请输入修改的联系人" << endl;
	string name;
	cin >> name;
	int x = search(a, name);
	if (x != -1)
	{
		cout << "请输入姓名" << endl;
		string name;
		cin >> name;
		a->PersonArry[x].name = name;
		cout << "请输入性别:   1表示男,2表示女" << endl;
		int sex;
		while (true)
		{
			cin >> sex;
			if (sex == 1 || sex == 2)
			{
				a->PersonArry[x].sex = sex;
				break;
			}
			else {
				cout << "请重新输入" << endl;
			}
		}
		cout << "请输入年龄" << endl;
		int age = 0;

		while (true)
		{
			cin >> age;
			if (age > 0 && age < 110)
			{
				a->PersonArry[x].age = age;
				break;
			}
			else
			{
				cout << "请重新输入" << endl;
			}
		}

		cout << "请输入电话" << endl;
		string phone;
		cin >> phone;
		a->PersonArry[x].phone = phone;
		cout << "请输入地址" << endl;
		string addr;
		cin >> addr;
		a->PersonArry[x].Addr = addr;
	}
	else {
		cout << "无此联系人" << endl;
	}
	system("pause");
	system("cls");
}

//清空联系人
void ddd(Addressbook* a) {
	a->size = 0;
	cout << "已全部清除" << endl;
	system("pause");
	system("cls");
}
int main()
{
	Addressbook aaa;
	aaa.size = 0;
	int select = 0;//定义一个用户输入的变量
	while (true)
	{
		showMenu();
		cin >> select;
		switch (select)
		{
		case 1:                     //1、添加联系人
			input1(&aaa);
			break;
		case 2:                     //2、显示联系人
			output2(&aaa);
			break;
		case 3:                     //3、删除联系人
			def(&aaa);
			break;
		case 4:                     //4、查找联系人
			cha(&aaa);
			break;
		case 5:                     //5、修改联系人
			xiu(&aaa);
			break;
		case 6:                     //6、清空联系人
			ddd(&aaa);
			break;
		case 0:                     //0、退出通讯录
			cout << "欢迎下次使用" << endl;
			system("pause");
			return 0;
			break;
		default:
			break;
		}
	}
	system("pause");
	return 0;
}
相关推荐
炘爚2 小时前
C++(普通指针和成员的区别、指针的使用场景和存储内容)
数据结构·c++·算法
wjs20242 小时前
Python 3 输入和输出
开发语言
炘爚2 小时前
C++(在Mystring类中碰到的构造函数和析构函数以及深拷贝和浅拷贝的问题)
开发语言·c++·算法
hz_zhangrl2 小时前
CCF-GESP 等级考试 2026年3月认证C++四级真题解析
c++·程序设计·gesp·c++四级·gesp2026年3月·gesp c++四级
liulilittle2 小时前
eBPF 中的 `__sk_buff`
网络
Chasing Aurora2 小时前
Python后端开发之旅(五)——DL
开发语言·pytorch·python·深度学习
Fang fan2 小时前
Java集合
java·开发语言·算法
2301_794799512 小时前
35_简单快捷不可靠的_UDP ## 网络协议那些事儿
网络·网络协议·udp
左手厨刀右手茼蒿2 小时前
Flutter for OpenHarmony:Flutter 三方库 udp — 实现极速底层异步通信(适配鸿蒙 HarmonyOS Next ohos)
网络·网络协议·flutter·华为·udp·harmonyos