黑马程序员C++学习总结【基础篇】

  • 笔者有C和C++的基础,但学无止境,多多少少存在一些知识盲区;并且距初学已久矣,究竟把知识还给老师多少,也无从得知。所以选择重新学习一遍,既当复习,也当查缺补漏。
  • 推荐哔哩哔哩 黑马程序员 的视频,对初学者可谓保姆级教学,有基础的话,两天时间完全可以把前83集看完(大多是C的基础 , 比较简单),把基础篇总结如下,包含了一些新的感悟 (初学时未理解到位的一些东西)。

黑马程序员C++学习总结【基础篇】
黑马程序员C++学习总结【核心篇】
黑马程序员C++学习总结【进阶篇】

一、小细节

1.为什么数据类型的取值范围不对称?

在这里插入图片描述

以短整型short为例,其占两个字节byte,也就是16bit,刨去最高位-字符位(1为负数,0为非负数),还剩15位。按理说应该为正数 [ 1 , … , 2 15 − 1 ] \left[1,\ldots,2^{15}-1\right] [1,,2151],负数为 [ − 1 , … , − ( 2 15 − 1 ) ] \left[-1,\ldots,-(2^{15}-1)\right] [1,,2151],然后加上0,总体上为 [ − ( 2 15 − 1 ) , … , 2 15 − 1 ] [-(2^{15}-1),\ldots,2^{15}-1] [(2151),,2151]

问题就出在0上,+0(0 000 0000 0000 0000)、-0(1 000 0000 0000 0000)都是0,但为了不浪费空间,把进位和符号位重叠了(即1既看作符号位表负数,又看作数值位表数据).

2.转义字符 反斜杠 \ \ 是啥?

代表一个反斜线字符" \ "

3.带你真正搞懂 值传递 和 地址传递

函数的值传递是指参数为基本类型时,如整型、浮点型、字符型(特指单字符型)时,参数传递时是从实参拷贝一份值传给形参,形参的变化不会影响实参的值。
(1)函数的实参和形参占有不同的存储单元
(2)函数的实参复制一份值传给形参
(3)实参不会随着形参的变化而变化

//main函数外边定义函数格式
int fun(int x, int y) { ... }
//main函数中调用
fun(a,b);   //int a,b;

函数的地址传递是指参数是:数组或指针类型时,传递的是地址,而不是值。
(1)函数的实参和形参共享存储单元
(2)函数是将实参的地址传给形参
(3)实参会随着形参的变化而变化

//数组做参数
//main函数外边函数定义的格式
float getAverage(int a[],int len) { ... }  //数组做参数时,需要两个参数:数组名[具体维数可写可不写]和数组长度
//main函数中的调用格式
getAverage(a,5);  // int a[5]={97,85,88,95,92};
//数组做参数调用时只写数组名不写[ ],为数组的首地址
//指针做参数
//main函数外边函数定义的格式
void swap(int* p,int* q)  //指针做函数的参数
//main函数中的调用格式
swap(&a,&b);   //int a,b;
//函数调用传递的地址,所以需要加&
//结构体数组做参数(本质上还是数组)
//main函数外边函数定义的格式
float fun(struct phone p[],int len);
//main函数中的调用格式
fun(p,5);   
//结构体数组做参数只写数组名,并告知数组长度
// struct phone
//{
//     int id;           //编号
//     char name[20];   //名称
//      float price;       //单价
//     char loc[50];     //产地
// };
// struct phone p[5];
//结构体指针做参数(本质上还是指针)
//main函数外边函数定义的格式
void fun(struct phone *p)
//main函数中的调用格式
fun(&p);  //调用时传递的地址,因为参数是指针类型
// struct phone
//{
//     int id;           //编号
//     char name[20];   //名称
//      float price;       //单价
//     char loc[50];     //产地
// }p;

(附:参考自 该作者在( 结构体数组做参数 )标题下有一处笔误 )

二、基础语法

1.利用 sizeof 求出数据类型所占空间大小

// 语法格式:sizeof(数据类型 or 变量)
cout<<"bool占用内存空间为:"<<sizeof(bool)<<endl;
cout<<"char占用内存空间为:"<<sizeof(char)<<endl;
cout<<"short占用内存空间为:"<<sizeof(short)<<endl;
cout<<"int占用内存空间为:"<<sizeof(int)<<endl;
cout<<"long占用内存空间为:"<<sizeof(long)<<endl;
cout<<"long long占用内存空间为:"<<sizeof(long long)<<endl;
cout<<"float占用内存空间为:"<<sizeof(float)<<endl;
cout<<"double占用内存空间为:"<<sizeof(double)<<endl;
//windows下输出结果为1 1 2 4 4 8 4 8(单位为字节)

结论:bool = char < short < float = int <= long <= long long =double (考虑Linux下long和long long均占8个字节)

2.查看字符对应的ASCII码值

	char ch = 'a';
	cout << (int)ch << endl;  //查看字符a对应的ASCII码
  • a 的 ASCII 码值为97;A 的 ASCII 码值为65.
  • char ch = 97; //可以直接用ASCII给字符型变量赋值

3.C 和 C++风格的字符串

C风格:

char 变量名[] = "字符串值"

C++风格:

string  变量名 = "字符串值"

注意:C++风格字符串,需要加入头文件#include <string>

4.飞雷神之术 goto语句

语法格式:goto 标记,如果标记的名称存在,执行到 goto 语句时,会跳转到标记的位置。

int main() {
	cout << "1" << endl; 
	goto FLAG;
	cout << "2" << endl;
	cout << "3" << endl;
	cout << "4" << endl;
	FLAG:
	cout << "5" << endl;
	system("pause");
	return 0;
}

5.数组

(1)定义数组,必须有初始长度。
数据类型 数组名[ 数组长度 ];
数据类型 数组名[ 数组长度 ] = { 值1,值2 ...};
数据类型 数组名[ ] = { 值1,值2 ...};

(2)一维数组名的用途有哪些?

  • 可以统计整个数组在内存中的长度
int arr[5] = { 1,2,3,4,5 };
cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;
cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;
cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;

在这里插入图片描述

  • 可以通过数组名获取到数组首地址
//二者输出地址一样
cout << "数组首地址为: " << arr << endl;
cout << "数组中第一个元素地址为: " <<&arr[0] << endl;
  • 二维数组 arr [ 3 ] [ 4 ] 的 arr 和 arr [0]、arr[1]、arr[2] 均表示地址,且 arr 和 arr [0] 表示的地址值相同。但是cout << sizeof(arr[0])<<endl;输出的是第一行所占的内存,而cout << sizeof(arr)<<endl;输出的是整个数组所占内存。

(3)数组逆置
思路:交换首元素和尾元素,首元素start +1,尾元素end -1,直到start >= end停止

#include<iostream>
using namespace std;

int main() {
	int i;
	int arr[5] = { 1,2,3,4,5 };
	//便利逆置前的数组 
	for(i=0;i<5;i++)
	{
		cout<<arr[i]<<endl;
	}
	//逆置
	int start = 0;
	int end = sizeof(arr)/sizeof(arr[0]) -1;
	while(start<end)
	{
		int temp = arr[start];
		arr[start]=arr[end];
		arr[end] = temp;
		start++;
		end--; 
	 } 
	 //便利逆置后的数组 
	for(i=0;i<5;i++)
	{
		cout<<arr[i]<<endl;
	}
	system("pause");
	return 0;
}

(4)冒泡排序
思路:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,执行一轮后,找到第一个最大值。
  3. 重复以上的步骤,继续下一轮,每次比较次数-1,直到不需要比较
int main() {
	int arr[9] = { 4,2,8,0,5,7,1,3,9 };
	//总共排序轮数为=元素个数-1
	for (int i = 0; i < 9 - 1; i++)
	{
		//每轮对比次数=元素个数-当前轮数-1(总轮数-当前轮数)
		for (int j = 0; j < 9 - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
			
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
	for (int i = 0; i < 9; i++)
	{
		cout << arr[i] << endl;
	}
	system("pause")return 0;
}

在这里插入图片描述

6.函数的分文件编写

函数分文件编写一般有4个步骤

  1. 创建后缀名为.h的头文件
  2. 创建后缀名为.cpp的源文件(定义函数)
  3. 在头文件中写函数的声明
  4. 在源文件中写函数的定义 + include头文件
//swap.h文件
#include<iostream>
using namespace std;

//实现两个数字交换的函数声明
void swap(int a, int b);
//swap.cpp文件
//先写入头文件,告诉编译器这两个文件是一伙的
#include "swap.h"
//定义两个数字交换的函数
void swap(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;

	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}
//main函数文件
//只需要写入头文件,即可直接调用函数
#include "swap.h"
int main() {

	int a = 100;
	int b = 200;
	swap(a, b);

	system("pause");

	return 0;
}

7.指针

普通变量存放的是数据,指针变量存放的是地址。指针变量可以通过" * "操作符,操作指针变量指向内存空间的数据,这个过程称为解引用

(1)指针的定义

  //1.指针的定义
	int a = 10; //定义整型变量a
	
	//指针定义语法: 数据类型 * 变量名 ;
	int * p;

	//指针变量赋值
	p = &a; //指针指向变量a的地址
	cout << &a << endl; //打印数据a的地址
	cout << p << endl;  //打印指针变量p

  //2、指针的使用
	//通过*操作指针变量指向内存中的数据
	cout << "*p = " << *p << endl;

(2)指针也是一种数据类型,这种数据类型占用多少内存空间?

cout << sizeof(p) << endl;
	cout << sizeof(char *) << endl;
	cout << sizeof(float *) << endl;
	cout << sizeof(double *) << endl;

所有指针类型在32位操作系统下是4个字节,在64位操作系统下是8个字节.

(3)空指针

空指针指针变量指向内存中编号为0的空间,作用是初始化指针变量,空指针指向的内存是不可以访问的(0~255之间的内存编号是系统占用的,因此不可以访问)。

	//指针变量p指向内存地址编号为0的空间
	int * p = NULL;
	//访问空指针报错 
	//内存编号0 ~255为系统占用内存,不允许用户访问
	cout << *p << endl;

(4)野指针

野指针就是指针指向的位置不可知的。(随机的、不正确的、没有明确限制的).

//野指针的三种情况

//1、指针未定义
	#include <stdio.h>
	int main()
	{
		int* p; //局部变量指针未初始化,默认就是随机值
		*p=10;
		return 0;
	}
//2、指针越界访问
	#include <stdio.h>
	int main()
	{
		int arr[10]={0];
		int* p=arr;
		for(int i=0;i<12;i++)
		{
			*p++=i;  //执行顺序*(p++)=i;
		}            //这里数组的大小为10,就会发生数组越界。
		return 0;
	}
//3、指针指向的空间释放
	int* test()      //作用域仅在使用时开始,使用后结束
	{
		int a=10;
		return &a;
	}
	int main()
	{
		int* p=test();  //此时一切正常
		printf("%d\n",*p); //test()执行完毕被释放,p就变成野指针
		return 0;
	}
//4、黑马程序员举的例子
	int *p = (int *)0x1100;  //将一个十六进制数强转为地址
	cout << *p << endl;     //由于16进制数的地址随机分配,故指针也充满了随机性

总结:空指针和野指针都不是我们申请的空间,因此不要访问。

(5)const修饰指针

  1. const修饰指针 — 常量指针
  2. const修饰常量 — 指针常量
  3. const即修饰指针,又修饰常量
int main() {

	int a = 10;
	int b = 10;

	//常量指针:const修饰的是指针,指针指向可以改,指针指向的值不可以更改
	const int * p1 = &a; 
	p1 = &b; //正确
	*p1 = 100;  //报错
	

	//指针常量:const修饰的是常量,指针指向不可以改,指针指向的值可以更改
	int * const p2 = &a;
	p2 = &b; //错误
	*p2 = 100; //正确

    //const既修饰指针又修饰常量
	const int * const p3 = &a;
	p3 = &b; //错误
	*p3 = 100; //错误

	system("pause");
	return 0;
}
  • 区分名字:看 const 和 * 出现的顺序,const 代表常量,* 代表指针
  • 哪个可以修改:看const后跟的是啥,p(地址不能修改),int(内存数据不能修改)

(6)利用指针访问数组中元素

int main() {

	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	int * p = arr;  //指向数组的指针

	cout << "第一个元素: " << arr[0] << endl;
	cout << "指针访问第一个元素: " << *p << endl;

	for (int i = 0; i < 10; i++)
	{
		//利用指针遍历数组
		cout << *p << endl;
		p++;
	}

	system("pause");

	return 0;
}

(7)值传递和地址传递

//值传递
void swap1(int a ,int b)
{
	int temp = a;
	a = b; 
	b = temp;
}
//地址传递
void swap2(int *p1, int *p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

int main() {

	int a = 10;
	int b = 20;
	swap1(a, b); // 值传递不会改变实参

	swap2(&a, &b); //地址传递会改变实参

	cout << "a = " << a << endl;

	cout << "b = " << b << endl;

	system("pause");

	return 0;
}

总结:如果不想修改实参,就用值传递;如果想修改实参,就用地址传递

8.结构体

结构体属于用户自定义的数据类型,是不同的数据类型的集合体

(1)结构体定义,创建结构体变量

//结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
}stu3; //结构体变量创建方式3 


int main() {

	//结构体变量创建方式1
	struct student stu1; //C++中struct 关键字可以省略

	stu1.name = "张三";
	stu1.age = 18;
	stu1.score = 100;
	
	cout << "姓名:" << stu1.name << " 年龄:" << stu1.age  << " 分数:" << stu1.score << endl;

	//结构体变量创建方式2
	struct student stu2 = { "李四",19,60 };

	cout << "姓名:" << stu2.name << " 年龄:" << stu2.age  << " 分数:" << stu2.score << endl;

	//结构体变量创建方式3 
	stu3.name = "王五";
	stu3.age = 18;
	stu3.score = 80;
	

	cout << "姓名:" << stu3.name << " 年龄:" << stu3.age  << " 分数:" << stu3.score << endl;

	system("pause");

	return 0;
}
  • 定义结构体时的关键字是struct,不可省略;
  • 创建结构体变量时,C++可以省略关键字struct;
  • 结构体变量利用操作符 ‘’.‘’ 访问成员.

(2)结构体数组

//结构体数组语法格式
struct  结构体名 数组名[元素个数] = {  {} , {} , ... {} }
// struct 结构体名 ,整体可看作一个数据类型
struct student arr[3]=
	{
		{"张三",18,80 },
		{"李四",19,60 },
		{"王五",20,70 }
	};

	for (int i = 0; i < 3; i++)
	{
		cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << " 分数:" << arr[i].score << endl;
	}

(3)结构体指针

//结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};


int main() {
	
	struct student stu = { "张三",18,100, }; // C++中struct可以省略
	
	struct student * p = &stu;  //struct student为&stu返回地址对应的数据类型
	                           // C++中struct可以省略
	p->score = 80; //指针通过 -> 操作符可以访问成员

	cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl;
	
	system("pause");

	return 0;
}

总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员.

(4)结构体嵌套结构体

在结构体中可以定义另一个结构体作为成员,成员结构体需定义在父结构体前边.

//学生结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//教师结构体定义
struct teacher
{
    //成员列表
	int id; //职工编号
	string name;  //教师姓名
	int age;   //教师年龄
	struct student stu; //子结构体 学生
};


int main() {

	struct teacher t1;
	t1.id = 10000;
	t1.name = "老王";
	t1.age = 40;

	t1.stu.name = "张三";
	t1.stu.age = 18;
	t1.stu.score = 100;

	cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;
	
	cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl;

	system("pause");

	return 0;
}

(5)结构体做函数参数

//学生结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//值传递
void printStudent(student stu )
{
	stu.age = 28;
	cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;
}

//地址传递
void printStudent2(student *stu)
{
	stu->age = 28;
	cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age  << " 分数:" << stu->score << endl;
}

int main() {

	student stu = { "张三",18,100};
	//值传递
	printStudent(stu);
	cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;

	cout << endl;

	//地址传递
	printStudent2(&stu);
	cout << "主函数中 姓名:" << stu.name << " 年龄: " << stu.age  << " 分数:" << stu.score << endl;

	system("pause");

	return 0;
}

如果不想修改主函数中的数据,用值传递,反之用地址传递

(6)结构体中const使用场景

结构体做函数参数,可以进行值传递和地址传递。值传递每次调用都需要拷贝整个结构体参数作副本,空间占用率高;地址传递不拷贝整个结构体参数,只保存地址,只占4个字节,但地址传递在函数内修改形参值,实参值也随之改变。为了在使用地址传递,而不改变实参值,需要引入const.

//学生结构体定义
struct student
{
	//成员列表
	string name;  //姓名
	int age;      //年龄
	int score;    //分数
};

//const使用场景
void printStudent(const student *stu) //加const防止函数体中的误操作
{
	//stu->age = 100; //操作失败,因为加了const修饰
	cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;

}

int main() {

	student stu = { "张三",18,100 };

	printStudent(&stu);

	system("pause");

	return 0;
}

三、通讯录管理系统

1.思路及代码注释

//通讯录管理系统

//1、系统需求

//通讯录是一个可以记录亲人、好友信息的工具。

//本教程主要利用C++来实现一个通讯录管理系统

//系统中需要实现的功能如下:

* 添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000* 显示联系人:显示通讯录中所有联系人信息
* 删除联系人:按照姓名进行删除指定联系人
* 查找联系人:按照姓名查看指定联系人信息
* 修改联系人:按照姓名重新修改指定联系人
* 清空联系人:清空通讯录中所有信息
* 退出通讯录:退出当前使用的通讯录

//2、创建项目

//创建项目步骤如下:

* 创建新项目
* 添加文件

//3、菜单功能

//功能描述: 用户选择功能的界面

//步骤:

* 封装函数显示该界面  如 void showMenu()
* 在main函数中调用封装好的函数

//代码:
#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;
}

int main() {

	showMenu();

	system("pause");

	return 0;
}

//4、退出功能

//功能描述:退出通讯录系统

//思路:根据用户不同的选择,进入不同的功能,可以选择switch分支结构,将整个架构进行搭建

//当用户选择0时候,执行退出,选择其他先不做操作,也不会退出程序

//代码:

int main() {

	int select = 0;

	while (true)
	{
		showMenu();

		cin >> select;
		
		switch (select)
		{
		case 1:  //添加联系人
			break;
		case 2:  //显示联系人
			break;
		case 3:  //删除联系人
			break;
		case 4:  //查找联系人
			break;
		case 5:  //修改联系人
			break;
		case 6:  //清空联系人
			break;
		case 0:  //退出通讯录
			cout << "欢迎下次使用" << endl;
			system("pause");
			return 0;
			break;
		default:
			break;
		}
	}

	system("pause");

	return 0;
}

//5、添加联系人

//功能描述:

//实现添加联系人功能,联系人上限为1000人,联系人信息包括(姓名、性别、年龄、联系电话、家庭住址)

//添加联系人实现步骤:

* 设计联系人结构体
* 设计通讯录结构体
* main函数中创建通讯录
* 封装添加联系人函数
* 测试添加联系人功能

//5.1 设计联系人结构体

//联系人信息包括:姓名、性别、年龄、联系电话、家庭住址

//设计如下:

#include <string>  //string头文件
//联系人结构体
struct Person
{
	string m_Name; //姓名
	int m_Sex; //性别:1男 2女
	int m_Age; //年龄
	string m_Phone; //电话
	string m_Addr; //住址
};

//5.2 设计通讯录结构体

//设计时候可以在通讯录结构体中,维护一个容量为1000的存放联系人的数组,并记录当前通讯录中联系人数量

//设计如下

#define MAX 1000 //最大人数

//通讯录结构体
struct Addressbooks
{
	struct Person personArray[MAX]; //通讯录中保存的联系人数组
	int m_Size; //通讯录中人员个数
};

//5.3 main函数中创建通讯录

//添加联系人函数封装好后,在main函数中创建一个通讯录变量,这个就是我们需要一直维护的通讯录

//mian函数起始位置添加:

	//创建通讯录
	Addressbooks abs;
	//初始化通讯录中人数
	abs.m_Size = 0;

//5.4 封装添加联系人函数

//思路:添加联系人前先判断通讯录是否已满,如果满了就不再添加,未满情况将新联系人信息逐个加入到通讯录

//添加联系人代码:

//1、添加联系人信息
void addPerson(Addressbooks *abs)
{
	//判断电话本是否满了
	if (abs->m_Size == MAX)
	{
		cout << "通讯录已满,无法添加" << endl;
		return;
	}
	else
	{
		//姓名
		string name;
		cout << "请输入姓名:" << endl;
		cin >> name;
		abs->personArray[abs->m_Size].m_Name = name;

		cout << "请输入性别:" << endl;
		cout << "1 -- 男" << endl;
		cout << "2 -- 女" << endl;

		//性别
		int sex = 0;
		while (true)
		{
			cin >> sex;
			if (sex == 1 || sex == 2)
			{
				abs->personArray[abs->m_Size].m_Sex = sex;
				break;
			}
			cout << "输入有误,请重新输入";
		}

		//年龄
		cout << "请输入年龄:" << endl;
		int age = 0;
		cin >> age;
		abs->personArray[abs->m_Size].m_Age = age;

		//联系电话
		cout << "请输入联系电话:" << endl;
		string phone = "";
		cin >> phone;
		abs->personArray[abs->m_Size].m_Phone = phone;

		//家庭住址
		cout << "请输入家庭住址:" << endl;
		string address;
		cin >> address;
		abs->personArray[abs->m_Size].m_Addr = address;

		//更新通讯录人数
		abs->m_Size++;

		cout << "添加成功" << endl;
		system("pause");
		system("cls");
	}
}

//5.5 测试添加联系人功能

//选择界面中,如果玩家选择了1,代表添加联系人,我们可以测试下该功能

//在switch case 语句中,case1里添加:

case 1:  //添加联系人
	addPerson(&abs);
	break;
//6、显示联系人

//功能描述:显示通讯录中已有的联系人信息

//显示联系人实现步骤:

* 封装显示联系人函数
* 测试显示联系人功能

//6.1 封装显示联系人函数

//思路:判断如果当前通讯录中没有人员,就提示记录为空,人数大于0,显示通讯录中信息

//显示联系人代码:

//显示所有联系人信息
void showPerson(Addressbooks * abs)
{
	if (abs->m_Size == 0)
	{
		cout << "当前记录为空" << endl;
	}
	else
	{
		for (int i = 0; i < abs->m_Size; i++)
		{
			cout << "姓名:" << abs->personArray[i].m_Name << "\t";
			cout << "性别:" << (abs->personArray[i].m_Sex == 1 ? "男" : "女") << "\t";
			cout << "年龄:" << abs->personArray[i].m_Age << "\t";
			cout << "电话:" << abs->personArray[i].m_Phone << "\t";
			cout << "住址:" << abs->personArray[i].m_Addr << endl;
		}
	}
	
	system("pause");
	system("cls");

}

//6.2 测试显示联系人功能switch case语句中,case 2 里添加

case 2:  //显示联系人
	showPerson(&abs);
	break;

//7、删除联系人

//功能描述:按照姓名进行删除指定联系人

//删除联系人实现步骤:

* 封装检测联系人是否存在
* 封装删除联系人函数
* 测试删除联系人功能

//7.1  封装检测联系人是否存在

//设计思路:

//删除联系人前,我们需要先判断用户输入的联系人是否存在,如果存在删除,不存在提示用户没有要删除的联系人

//因此我们可以把检测联系人是否存在封装成一个函数中,如果存在,返回联系人在通讯录中的位置,不存在返回-1

//检测联系人是否存在代码:

//判断是否存在查询的人员,存在返回在数组中索引位置,不存在返回-1
int isExist(Addressbooks * abs, string name)
{
	for (int i = 0; i < abs->m_Size; i++)
	{
		if (abs->personArray[i].m_Name == name)
		{
			return i;
		}
	}
	return -1;
}

//7.2 封装删除联系人函数

//根据用户输入的联系人判断该通讯录中是否有此人

//查找到进行删除,并提示删除成功

//查不到提示查无此人。

//删除指定联系人信息
void deletePerson(Addressbooks * abs)
{
	cout << "请输入您要删除的联系人" << endl;
	string name;
	cin >> name;

	int ret = isExist(abs, name);
	if (ret != -1)
	{
		for (int i = ret; i < abs->m_Size; i++)
		{
			abs->personArray[i] = abs->personArray[i + 1];
		}
         abs->m_Size--;
		cout << "删除成功" << endl;
	}
	else
	{
		cout << "查无此人" << endl;
	}

	system("pause");
	system("cls");
}

//7.3 测试删除联系人功能switch case 语句中,case3里添加:

case 3:  //删除联系人
	deletePerson(&abs);
	break;

//8、查找联系人

//功能描述:按照姓名查看指定联系人信息

//查找联系人实现步骤

* 封装查找联系人函数
* 测试查找指定联系人

//8.1 封装查找联系人函数

//实现思路:判断用户指定的联系人是否存在,如果存在显示信息,不存在则提示查无此人。

//查找联系人代码:

//查找指定联系人信息
void findPerson(Addressbooks * abs)
{
	cout << "请输入您要查找的联系人" << endl;
	string name;
	cin >> name;

	int ret = isExist(abs, name);
	if (ret != -1)
	{
		cout << "姓名:" << abs->personArray[ret].m_Name << "\t";
		cout << "性别:" << abs->personArray[ret].m_Sex << "\t";
		cout << "年龄:" << abs->personArray[ret].m_Age << "\t";
		cout << "电话:" << abs->personArray[ret].m_Phone << "\t";
		cout << "住址:" << abs->personArray[ret].m_Addr << endl;
	}
	else
	{
		cout << "查无此人" << endl;
	}

	system("pause");
	system("cls");

}

 //8.2 测试查找指定联系人

//在switch case 语句中,case4里添加:

case 4:  //查找联系人
	findPerson(&abs);
	break;

//9、修改联系人

//功能描述:按照姓名重新修改指定联系人

//修改联系人实现步骤

* 封装修改联系人函数
* 测试修改联系人功能

//9.1 封装修改联系人函数

实现思路:查找用户输入的联系人,如果查找成功进行修改操作,查找失败提示查无此人

//修改联系人代码:

//5、修改指定联系人信息
void modifyPerson(Addressbooks * abs)
{
	cout << "请输入您要修改的联系人" << endl;
	string name;
	cin >> name;

	int ret = isExist(abs, name);
	if (ret != -1)
	{
		//姓名
		string name;
		cout << "请输入姓名:" << endl;
		cin >> name;
		abs->personArray[ret].m_Name = name;

		cout << "请输入性别:" << endl;
		cout << "1 -- 男" << endl;
		cout << "2 -- 女" << endl;

		//性别
		int sex = 0;
		while (true)
		{
			cin >> sex;
			if (sex == 1 || sex == 2)
			{
				abs->personArray[ret].m_Sex = sex;
				break;
			}
			cout << "输入有误,请重新输入";
		}

		//年龄
		cout << "请输入年龄:" << endl;
		int age = 0;
		cin >> age;
		abs->personArray[ret].m_Age = age;

		//联系电话
		cout << "请输入联系电话:" << endl;
		string phone = "";
		cin >> phone;
		abs->personArray[ret].m_Phone = phone;

		//家庭住址
		cout << "请输入家庭住址:" << endl;
		string address;
		cin >> address;
		abs->personArray[ret].m_Addr = address;

		cout << "修改成功" << endl;
	}
	else
	{
		cout << "查无此人" << endl;
	}

	system("pause");
	system("cls");

}

//9.2 测试修改联系人功能switch case 语句中,case 5里添加:

case 5:  //修改联系人
	modifyPerson(&abs);
	break;

//10、清空联系人

//功能描述:清空通讯录中所有信息

//清空联系人实现步骤

* 封装清空联系人函数
* 测试清空联系人

//10.1 封装清空联系人函数

//实现思路: 将通讯录所有联系人信息清除掉,只要将通讯录记录的联系人数量置为0,做逻辑清空即可。

//清空联系人代码:

//清空所有联系人
void cleanPerson(Addressbooks * abs)
{
	abs->m_Size = 0;
	cout << "通讯录已清空" << endl;
	system("pause");
	system("cls");
}

//10.2  测试清空联系人

//在switch case 语句中,case 6 里添加:

case 6:  //清空联系人
	cleanPerson(&abs);
	break;
	
//至此,通讯录管理系统完成!

2.完整源码

#include <iostream>
#include <string>
using namespace std;
#define MAX 1000//最大可存入联系人人数

//-菜单界面
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;
}

//设计联系人结构体
struct Person {
	//姓名
	string m_Name;
	//性别  1 男  2 女
	int m_Sex;
	//年龄
	int m_Age;
	//电话
	string m_Phone;
	//地址
	string m_Addr;
};

//设计通讯录结构体
struct Addressbooks {
	//通讯录中保存的联系人数组
	struct Person personArray[MAX];

	//通讯录中当前记录联系人个数
	int m_Size;
};

void addPerson(Addressbooks& abs);//1、添加联系人 函数声明
void showPerson(Addressbooks& abs);//2、显示联系人 函数声明
void deletePerson(Addressbooks& abs);//3、删除联系人 函数声明
void findPerson(Addressbooks& abs); //4、查找联系人 函数声明
void renewPerson(Addressbooks& abs); //5、修改联系人 函数声明
void cleanPerson(Addressbooks& abs); //6、清空联系人 函数声明

int main() {
	int select = 0;

	Addressbooks abs;//创建通讯录

	abs.m_Size = 0;//初始化通讯录中人数

	while (true) {
		showMenu();//-菜单界面

		cin >> select;//选择指令

		switch (select)
		{
		case 1://1、添加联系人
			addPerson(abs);
			break;
		case 2://2、显示联系人
			showPerson(abs);
			break;
		case 3://3、删除联系人
			deletePerson(abs);
			break;
		case 4://4、查找联系人
			findPerson(abs);
			break;
		case 5://5、修改联系人
			renewPerson(abs);
			break;
		case 6://6、清空联系人
			cleanPerson(abs);
			break;
		case 0://0、退出通讯录
			cout << "欢迎下次使用!" << endl;
			system("pause");
			return 0;
			break;
		default:
			break;
		}
	}

	system("pause");
	return 0;
}

void addPerson(Addressbooks& abs) { //1、添加联系人 函数定义
	if (abs.m_Size == MAX) {
		cout << "通讯录已满,无法添加!" << endl;
		return;
	}
	else {
		//姓名
		string name;
		cout << "请输入名字:" << endl;
		cin >> name;
		abs.personArray[abs.m_Size].m_Name = name;

		//性别
		cout << "请输入性别:" << endl;
		cout << "1————男" << endl;
		cout << "2————女" << endl;
		int sex = 0;
		while (true) {
			cin >> sex;
			if (sex == 1 || sex == 2) {
				abs.personArray[abs.m_Size].m_Sex = sex;
				break;
			}
			else
				cout << "输入有误,请重新输入!" << endl;
		}

		//年龄
		cout << "请输入年龄:" << endl;
		int age = 0;
		while (true) {
			cin >> age;
			if (age > 0 && age < 150) {
				abs.personArray[abs.m_Size].m_Age = age;
				break;
			}
			else {
				cout << "输入有误,请重新输入!" << endl;
			}
		}

		//联系电话
		cout << "请输入联系电话:" << endl;
		string phone="";
		cin >> phone;
		abs.personArray[abs.m_Size].m_Phone = phone;

		//家庭住址
		cout << "请输入家庭住址:" << endl;
		string address;
		cin >> address;
		abs.personArray[abs.m_Size].m_Addr = address;

		abs.m_Size++;//通讯录更新

		cout << "添加完成" << endl;
		system("pause");//"请按任意键继续",暂停界面
		system("cls");  //清屏
	}
}

void showPerson(Addressbooks& abs) { //2、显示联系人 函数定义
	//判断通讯录中人数是否为0,如果为0,提示记录为空
	//如果不为0,显示记录的联系人信息
	if (abs.m_Size == 0) {
		cout << "当前记录为空" << endl;
	}
	else {
		cout << "通讯录联系人信息列表:" << endl;
		for (int i = 0; i < abs.m_Size; i++) {
			cout << "姓名:" << abs.personArray[i].m_Name << "\t"
				<< "性别:" << (abs.personArray[i].m_Sex==1?"男":"女") << "\t"
				<< "年龄:" << abs.personArray[i].m_Age << "\t"
				<< "电话:" << abs.personArray[i].m_Phone << "\t"
				<< "地址:" << abs.personArray[i].m_Addr << "\t" << endl;
		}
	}
	system("pause");
	system("cls");
}

//检测联系人是否存在,如果存在,返回联系人所在数组中的具体位置,不存在返回-1
//参数1-abs 通讯录    参数2-name 对比姓名
int isExist(Addressbooks& abs, string name) {
	for (int i = 0; i < abs.m_Size; i++) {
		//找到用户输入的姓名了
		if (abs.personArray[i].m_Name == name) {
			return i; //找到了,返回这个人在数组中的下标编号
		}
	}
	return -1; //如果遍历结束的没有找到,返回-1
}

void deletePerson(Addressbooks& abs) { //3、删除联系人 函数定义
	cout << "请输入联系人姓名:" << endl;
	string name;
	cin >> name;
	int ret = isExist(abs, name);
	if (ret == -1) {
		cout << "查无此人" << endl;
	}
	else {
		for (int i = ret; i < abs.m_Size; i++) {
			abs.personArray[i] = abs.personArray[i + 1];//结构体之间是支持=号直接赋值的
		}
		abs.m_Size--;
		cout << "删除成功" << endl;
	}
	system("pause");
	system("cls");
}

void findPerson(Addressbooks& abs) { //4、查找联系人 函数定义
	cout << "请输入联系人姓名:" << endl;
	string name;
	cin >> name;
	int ret = isExist(abs, name);
	if (ret == -1) {
		cout << "查无此人" << endl;
	}
	else {
		cout << "该联系人信息如下:" << endl;
		cout << "姓名:" << abs.personArray[ret].m_Name << "\t"
			<< "性别:" << (abs.personArray[ret].m_Sex == 1 ? "男" : "女") << "\t"
			<< "年龄:" << abs.personArray[ret].m_Age << "\t"
			<< "电话:" << abs.personArray[ret].m_Phone << "\t"
			<< "地址:" << abs.personArray[ret].m_Addr << "\t" << endl;
	}
	
	system("pause");
	system("cls");
}

void renewPerson(Addressbooks& abs) { //5、修改联系人 函数定义
	cout << "请输入联系人姓名:" << endl;
	string name;
	cin >> name;
	int ret = isExist(abs, name);
	if (ret == -1) {
		cout << "查无此人" << endl;
	}
	else
	{
		cout << "正在修改该联系人信息..." << endl;
		//姓名
		string name;
		cout << "请输入名字:" << endl;
		cin >> name;
		abs.personArray[ret].m_Name = name;

		//性别
		cout << "请输入性别:" << endl;
		cout << "1————男" << endl;
		cout << "2————女" << endl;
		int sex = 0;
		while (true) {
			cin >> sex;
			if (sex == 1 || sex == 2) {
				abs.personArray[ret].m_Sex = sex;
				break;
			}
			else
				cout << "输入有误,请重新输入!" << endl;
		}

		//年龄
		cout << "请输入年龄:" << endl;
		int age = 0;
		while (true) {
			cin >> age;
			if (age > 0 && age < 150) {
				abs.personArray[ret].m_Age = age;
				break;
			}
			else {
				cout << "输入有误,请重新输入!" << endl;
			}
		}

		//联系电话
		cout << "请输入联系电话:" << endl;
		string phone = "";
		cin >> phone;
		abs.personArray[ret].m_Phone = phone;

		//家庭住址
		cout << "请输入家庭住址:" << endl;
		string address;
		cin >> address;
		abs.personArray[ret].m_Addr = address;

		cout << "修改完成" << endl;
	}

	system("pause");
	system("cls");
}

void cleanPerson(Addressbooks& abs) { //6、清空联系人 函数定义
	cout << "确定要清空所有联系人信息嘛?" << endl
		<< "1————确定清空" << endl
		<< "2————放弃清空" << endl;
	int flag = 0;
	while (true) {
		cin >> flag;
		if (flag != 1 && flag != 2) {
			cout << "请输入正确指令" << endl;
		}
		else if (flag == 1) {
			abs.m_Size = 0;
			cout << "通讯录已经清空" << endl;
			break;
		}
		else {
			cout << "通讯录放弃清空" << endl;
			break;
		}
	}
	system("pause");
	system("cls");
}

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值