[学习笔记] 1. C++ / CPP基础入门

课程链接:https://www.bilibili.com/video/BV1et411b73Z

在这里插入图片描述

文章目录

1. 初识C++

1.1 第一个C++程序

#include <iostream>
using namespace std;


int main() {

	cout << "Hello World" << endl;

	system("pause");

	return 0;
}

1.2 注释

作用:在代码中加一些说明和解释,方便自己或其他程序员阅读代码

CPP的注释有两种格式:

  1. 单行注释: // 描述信息
    • 通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明
  2. 多行注释: /* 描述信息 */
    • 通常放在一段代码的上方,对该段代码做整体说明

Note: 编译器在编译代码时,会忽略注释的内容

#include <iostream>
using namespace std;

/*
	main是一个程序的入口,每个程序都必须有这么一个函数,
	有且仅有一个
*/
int main() {
	
	// 在屏幕中输出Hello World
	cout << "Hello World" << endl;

	system("pause");

	return 0;
}

1.3 变量

作用:给一段指定的内存空间起名,方便操作这段内存
语法:数据类型 变量名 = 初始值;

示例:

#include<iostream>
using namespace std;


int main() {
	// 变量创建的语法:数据类型 变量名 = 变量初始值
	int a = 10;

	cout << "a = " << a << endl; // a = 10

	system("pause");
	return 0;
}

1.4 常量

作用:用于记录程序中不可更改的数据

C++定义常量的两种方式:

  1. #define 宏常量: #define 常量名 常量值
    • 通常在文件上方定义,表示一个常量
  2. const修饰的变量: const 数据类型 常量名 = 常量值
    • 通常在变量定义前加关键字const,修饰该变量为常量,不可修改

const: 英['kɒnst] 美['kɑnst]
adj. 恒定的; 不变的;
n. 常数; 恒量;

示例:

#include <iostream>
using namespace std;


/*
常量的定义方式
	1. #define 宏常量 -> #define 常量名 常量值
	2. const修饰的变量 -> const 数据类型 常量名 = 常量值
*/


#define Day 7


int main() {
	cout << "一周总共有: " << Day << " 天" << endl;  // 一周总共有: 7 天

	const int month = 12;  // const修饰的变量也称为常量

	cout << "一年总共有: " << month << " 个月份" << endl;	  // 一年总共有: 12 个月份

	system("pause");
	return 0;
}

1.5 关键字

作用:关键字是C++中预先保留的单词(标识符)

  • 在定义变量或常量时,不要覆盖关键字!

C++关键字如下:

关键字
asmdoifreturntypedef
autodoubleinlineshorttypeid
booldynamic_castintsignedtypename
breakelselongsizeofunion
caseenummutablestaticunsigned
catchexplicitnamespacestatic_castusing
charexportnewstructvirtual
classexternoperatorswitchvoid
constfalseprivatetemplatevolatile
const_castfloatprotectedthiswchar_t
continueforpublicthrowwhile
defaultfriendregistertrue
deletegotoreinterpret_casttry

Note:在给变量或常量起名时,不要使用CPP的关键字,否则会产生歧义。

示例:

#include <iostream>
using namespace std;


int main() {

	// 创建变量时变量名使用关键字
	int true = 10;  // 不能使用关键字作为变量名,会报错!

	system("pause");
	return 0;
}

在这里插入图片描述

1.6 标识符命名规则

作用:C++规定给标识符(变量、常量)命名时,有一套自己的规则:

  • 标识符不能是关键字
  • 标识符只能由字母、数字、下划线组成
  • 第一个字符必须为字母或下划线(不能以数字开头)
  • 标识符中字母区分大小写

建议:给标识符命名时,争取做到见名知意的效果,方便自己和他人阅读

#include <iostream>

using namespace std;


/*
	1. 标识符不能是关键字
	2. 标识符只能由字母、数字、下划线组成
	3. 第一个字符必须为字母或下划线(不能以数字开头)
	4. 标识符中字母区分大小写
*/
int main() {

	// 1. 标识符不能是关键字
	// int int = 10;  // 错误

	// 2. 标识符只能由字母、数字、下划线组成
	int abc = 10;
	int _abc = 20;
	int _123abc = 30;

	// 3. 第一个字符必须为字母或下划线(不能以数字开头)
	// int 123abc = 40;  // 错误

	// 4. 标识符中字母区分大小写
	int aaa = 100;
	int AAA = 300;
	cout << aaa << endl;  // 100
	cout << AAA << endl;  // 300

	system("pause");
	return 0;
}

2. 数据类型

C++规定在创建一个变量或常量时,必须要指定出相应的数据类型,否则无法给变量分配内存。

数据类型存在的意义是给变量分配合适的内存空间。

2.1 整型

作用:整型变量表示的是整数类型的数据。C++中能够表示整型的类型有以下几种方式,区别在于所占用的空间不同:

数据类型占用空间取值范围
short(短整型)2字节 ( − 2 15 ∼ 2 15 − 1 ) (-2^{15} \sim 2^{15} - 1) (2152151) ⇔ \Leftrightarrow ( − 32 , 768 ∼ 32 , 767 ) (-32,768 \sim 32,767) (32,76832,767)
int(整型)4字节 ( − 2 31 ∼ 2 31 − 1 ) (-2^{31} \sim 2^{31} - 1) (2312311) ⇔ \Leftrightarrow ( − 2 , 147 , 483 , 648 ∼ 2 , 147 , 483 , 647 ) (-2,147,483,648 \sim 2,147,483,647) (2,147,483,6482,147,483,647)
long(长整型)Windows为4字节,Linux为4字节(32位),8字节(64位) ( − 2 31 ∼ 2 31 − 1 ) (-2^{31} \sim 2^{31} - 1) (2312311) ⇔ \Leftrightarrow ( − 2 , 147 , 483 , 648 ∼ 2 , 147 , 483 , 647 ) (-2,147,483,648 \sim 2,147,483,647) (2,147,483,6482,147,483,647)
long long(长长整型)8字节 ( − 2 63 ∼ 2 63 − 1 ) (-2^{63} \sim 2^{63} - 1) (2632631) ⇔ \Leftrightarrow ( − 9 , 223 , 372 , 036 , 854 , 775 , 808 ∼ 9 , 223 , 372 , 036 , 854 , 775 , 807 ) (-9,223,372,036,854,775,808 \sim 9,223,372,036,854,775,807) (9,223,372,036,854,775,8089,223,372,036,854,775,807)

在日常开发中,int是最常用的。

#include<iostream>
using namespace std;


/*
	整型:
		1. 短整型 short
		2. 整型 int
		3. 长整型 long
		4. 长长整型 long long
*/
int main() {

	// 1. 短整型 short (−32,768 ~ 32,767)
	short num1 = 10;

	// 2. 整型 int (−2,147,483,648 ~ 2,147,483,647)
	int num2 = 10;

	// 3. 长整型 long (−2,147,483,648 ~ 2,147,483,647)
	long num3 = 10;

	// 4. 长长整型 long long ((−9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807))
	long long num4 = 10;  

	cout << "num1: " << num1 << endl;  // num1: 10
	cout << "num2: " << num2 << endl;  // num2: 10
	cout << "num3: " << num3 << endl;  // num3: 10
	cout << "num4: " << num4 << endl;  // num4: 10

	cout << "-------------------" << endl;

	/*
		探讨越界的情况:当出现超过表示范围的数时,会自动轮到开头
	*/

	// 1. 短整型 short (−32768~32767)
	short ol_num1 = 32768;
	cout << "ol_num1: " << ol_num1 << endl;  // ol_num1: -32768

	// 2. 整型 int (−2,147,483,648 ~ 2,147,483,647)
	int ol_num2 = 2147483648;
	cout << "ol_num2: " << ol_num2 << endl;  // ol_num2: -2147483648

	// 3. 长整型 long (−2,147,483,648 ~ 2,147,483,647)
	long ol_num3 = 2147483648;
	cout << "ol_num3: " << ol_num3 << endl;  // ol_num3: -2147483648

	// 4. 长长整型 long long ((−9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807))
	long long ol_num4 = 9223372036854775808;
	cout << "ol_num4: " << ol_num4 << endl;  // ol_num4: -9223372036854775808
	
	
	system("pause");
	return 0;
}

2.2 sizeof关键字

作用:利用sizeof关键字可以统计数据类型所占内存的大小。

语法:sizeof(数据类型 / 变量)

示例:

#include<iostream>
using namespace std;


int main() {
	/*
		使用sizeof求出数据类型/变量所占的内存大小
	*/

	short num1 = 10;
	cout << "short占用的内存空间为: " << sizeof(short) << endl;  // short占用的内存空间为: 2
	cout << "num1变量对应的数据类型所占用的内存空间为: " << sizeof(num1) << endl;  // num1变量对应的数据类型所占用的内存空间为: 2

	int num2 = 10;
	cout << "int占用的内存空间为: " << sizeof(int) << endl;  // int占用的内存空间为: 4

	long num3 = 10;
	cout << "long占用的内存空间为: " << sizeof(long) << endl;  // long占用的内存空间为: 4
	
	long long num4 = 10;
	cout << "long long占用的内存空间为: " << sizeof(long long) << endl;  // long long占用的内存空间为: 8

	system("pause");
	return 0;
}

2.3 实型(浮点型)

作用:用于表示小数。

浮点型变量分为两种:

  1. 单精度: float
  2. 双精度: double

两者的区别在于表示的有效数字范围不同。

数据类型占用空间有效数字范围
float4字节7位有效数字
double8字节15~16位有效数字

有效数字包含小数点前面和后面的!

需要注意的是,在写float类型时,数字后面加上一个f,例如:

float num = 3.14f;

因为如果不加f,那么编译器看到小数会默认它为double,还需要将double转为为float,这样会多一步。

示例:

#include <iostream>
using namespace std;


int main() {
	// 1. 单精度 float
	// 2. 双精度 double

	float f1 = 3.1415126f;  // 一般会在后面写一个f
	// endl = end line
	cout << "f1: " << f1 << endl;  // f1: 3.14151

	double d1 = 3.1415126;
	cout << "d1: " << d1 << endl;  // d1: 3.14151

	// 统计float和double占用的内存空间
	cout << "float占用的内存空间: " << sizeof(float) << "字节" << endl;  // float占用的内存空间: 4字节
	cout << "double占用的内存空间: " << sizeof(double) << "字节" << endl;  // double占用的内存空间: 8字节

	// 科学计数法
	float f2 = 3e2f;  // 3 * 10^2
	double d2 = 3e-2;  // 3 * 10^-2

	cout << "f2: " << f2 << endl;  // f2: 300
	cout << "d2: " << d2 << endl;  // d2: 0.03

	system("pause");
	return 0;
}

默认情况下,CPP显示一个小数最多显示6位小数。

2.4 字符型

作用:字符型变量用于显示单个字符

语法:char ch = 'a';

注意:

  1. 在显示字符型变量时,用单引号将字符括起来,不要使用双引号
  2. 单引号内只能有一个字符,不可以是字符串
  3. 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元
  4. C和CPP中字符型变量只占用1个字节

字节字节,就是一个英文单词所占用的大小,所以对于char,在空间中就是占一个字节。

需要记忆的ASCII码值:

  • a: 97
  • A: 65

示例:

#include<iostream>
using namespace std;


int main() {
	// 1. 字符型变量的创建方式
	char ch = 'a';
	cout << "ch: " << ch << endl;  // ch: a

	// 2. 字符型变量所占用内存大小
	cout << "char所占的内存大小: " << sizeof(char) << "字节" << endl;  // char所占的内存大小: 1字节

	// 3. 字符型变量常见错误
	// char ch2 = "a";  // 不能用双引号创建字符
	// char ch3 = 'abcdef';  // 不能用单引号创建字符串

	// 4. 字符型变量对应的ASCII编码
	// (数据类型)变量: 将变量强转为指定的数据类型
	cout << (int)ch << endl;  // 97
	cout << (int)'A' << endl;  // 65

	system("pause");
	return 0;
}

ASCII码表格

ASCII值(十进制)控制字符解释ASCII值(十进制)控制字符解释ASCII值(十进制)控制字符解释ASCII值(十进制)控制字符解释
0NUT (null)空字符32(space)空格64@电子邮件符号96`开单引号
1SOH (start of headline)标题开始33!叹号65A大写字母A97a小写字母
2STX (start of text)正文开始34"双引号66B大写字母B98b小写字母b
3ETX (end of text)正文结束35#井号67C大写字母C99c小写字母c
4EOT (end of transmission)传输结束36$美元符号68D大写字母D100d小写字母d
5ENQ (enquiry)请求37%百分号69E大写字母E101e小写字母e
6ACK (acknowledge)收到通知38&和号70F大写字母F102f小写字母f
7BEL (bell)响铃39闭单引号71G大写字母G103g小写字母g
8BS (backspace)退格40(开括号72H大写字母H104h小写字母h
9HT (horizontal tab)水平制表符41)闭括号73I大写字母I105i小写字母i
10LF (NL line feed, new line)换行键42*星号74J大写字母J106j小写字母j
11VT (vertical tab)垂直制表符43+加号75K大写字母K107k小写字母k
12FF (NP form feed, new page)换页键44,逗号76L大写字母L108l小写字母l
13CR (carriage return)回车键45-减号/破折号77M大写字母M109m小写字母m
14SO (shift out)不用切换46.句号/点78N大写字母N110n小写字母n
15SI (shift in)启用切换47/斜杠79O大写字母O111o小写字母o
16DLE (data link escape)数据链路转义480数字080P大写字母P112p小写字母p
17DC1 (device control 1)设备控制1491数字181Q大写字母Q113q小写字母q
18DC2 (device control 2)设备控制2502数字282R大写字母R114r小写字母r
19DC3 (device control 3)设备控制3513数字383S大写字母S115s小写字母s
20DC4 (device control 4)设备控制4524数字484T大写字母T116t小写字母t
21NAK (negative acknowledge)拒绝接收535数字585U大写字母U117u小写字母u
22SYN (synchronous idle)同步空闲546数字686V大写字母V118v小写字母v
23ETB (end of trans. block)结束传输块557数字787W大写字母W119w小写字母w
24CAN (cancel)取消568数字888X大写字母X120x小写字母x
25EM (end of medium)媒介结束579数字989Y大写字母Y121y小写字母y
26SUB (substitute)代替58:冒号90Z大写字母Z122z小写字母z
27ESC (escape)换码(溢出)59;分号91[开方括号123{开花括号
28FS (file separator)文件分隔符60<小于92\反斜杠124``
29GS (group separator)分组符61=等号93]闭方括号125}闭花括号
30RS (record separator)记录分隔符62>大于94^脱字符126~波浪号
31US (unit separator)单元分隔符63?问号95_下划线127DEL (delete)删除

ASCII码大致由以下两部分组成:

  1. ASCII非打印控制字符:ASCII表上的数字0 ~ 31分配给了控制字符,用于控制像打印机等一些外围设备。
  2. ASCII打印字符:数字32 ~ 126分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。

2.5 转义字符

作用:用于表示一些不能显示出来的ASCII字符

现阶段我们常用的转义字符有:

  • \n
  • \\
  • \t
重要转义字符含义ASCII码值(十进制)
\a报警007
\b退格(BS), 将当前位置移到前一列008
\f换页(FF), 将当前位置移到下页开头012
⭐️\n换行(LF), 将当前位置移到下一行开头010
⭐️\r回车(CR), 将当前位置移到本行开头013
⭐️\t水平制表(HT), 跳到下一个TAB位置009
\v垂直制表(VT)011
⭐️\\代表一个反斜线字符\092
\'代表一个单引号(撇号)字符039
\"代表一个双引号字符034
\0代表一个问号000
\ddd8进制转义字符, d范围0~73位8进制
\xhh16进制转义字符, h范围0~9, a~f, A~F3位16进制

注意:

  • \r表示将当前的光标移动到行首,但不会移动到下一行;
  • \n表示将光标移动到下一行,但不会移动到行首。

单独一个\r\n都不是一般意义上的回车,\r\n放在一起才是。通常在写程序的时候只要一个\n就可以了,这是因为编译器会自动将\n替换成\r\n

示例:

#include<iostream>
using namespace std;


int main() {

	// 1. 换行符 \n
	cout << "hello wolrd";
	/*
		hello wolrd请按任意键继续. . .
	*/
	
	cout << "hello wolrd\n";
	/*
		hello wolrd
		请按任意键继续. . .
	*/


	// 2. 反斜杠 \\ 
	cout << "\\" << endl;  // \


	// 3. 水平制表符 \t	 可以整齐地输出数据
	cout << "aaa\thello world" << endl;
	/*
		aaa     hello world
		请按任意键继续. . .
	*/
	cout << "a\thello world" << endl;
	cout << "ab\thello world" << endl;
	cout << "abc\thello world" << endl;
	cout << "abcd\thello world" << endl;
	/*
		a       hello world
		ab      hello world
		abc     hello world
		abcd    hello world
		请按任意键继续. . .
	*/

	// 4. 回车 \r
	cout << "abc\rdefg" << endl;  // defg(回车会让光标回到行首,因此defg覆盖掉了abc)
	cout << "123\r\n";  // \r\n表示开始下一行并回到行首

	/*
		\r表示将当前的光标移动到行首,但不会移动到下一行;
		\n表示将光标移动到下一行,但不会移动到行首。
		
		单独一个\r或\n都不是一般意义上的回车,\r\n放在一起才是。
		
		通常在写程序的时候只要一个\n就可以了,这是因为编译器会自动将\n替换成\r\n。
	*/

	system("pause");
	return 0;
}

2.6 字符串型

作用:用于表示一串字符。

两种风格:

  1. C语言风格字符串:char 变量名[] = "字符串值"; -> char str1[] = "Hello World";
  2. C++语言风格字符串: string 变量名 = "字符串值";

注意:

  • C语言风格的字符串要用双引号括起来
  • C++风格字符串需要加入头文件#include <string>

示例:

#include <iostream>
using namespace std;
#include <string>


int main() {
	
	// 1. C风格字符串
	char str_c[] = "Hello World";
	cout << str_c << endl;  // Hello World

	// 2. C++风格字符串(使用的时候需要引入string头文件)
	string str_cpp = "Hello World";
	cout << str_cpp << endl;  // Hello World

	// 看一下两种字符串占空间的大小
	cout << "C大小: " << sizeof(str_c) << "字节" << endl;  // C大小: 12字节
	cout << "C++大小: " << sizeof(string) << "字节" << endl;  // C++大小: 40字节

	system("pause");
	return 0;
}

2.7 布尔类型

作用:布尔数据类型表示真或假的值

bool类型只有两个值:

  • true: 真 (本质是1)
  • false: 假 (本质是0)

bool类型占1个字节大小(因为本质上是数字1/0)

语法:bool 变量名 = true/false;

示例:

#include <iostream>, <string>
using namespace std;


int main() {

	// 1. 创建bool数据类型
	bool flag_1 = true;
	bool flag_2 = false;

	cout << flag_1 << endl;  // 1
	cout << flag_2 << endl;  // 0

	// 2. 查看bool数据类型占用空间大小
	cout << "bool数据类型占 " << sizeof(bool) << " 字节" << endl;  // bool数据类型占 1 字节

	system("pause");
	return 0;
}

2.8 数据的输入

作用:用于从键盘获取数据

关键字:cin

语法:cin >> 变量

示例:

#include<iostream>
#include<string>
using namespace std;


int main() {

	// 1. 整型
	int a = 0;
	cout << "请给整型变量a赋值: " << endl;
	cin >> a;  // 请给整型变量a赋值:
	cout << "整型变量a = " << a << endl;  // 整型变量a = 100

	// 2. 浮点型
	float f = 3.14f;
	cout << "请给浮点型变量f赋值: " << endl;
	cin >> f;  // 请给浮点型变量f赋值:
	cout << "浮点型变量f = " << f << endl;  // 浮点型变量f = 7.77

	// 3. 字符型
	char ch = 'a';
	cout << "请给字符型变量ch赋值: " << endl;
	cin >> ch;  // 请给字符型变量ch赋值:
	cout << "字符型变量ch = " << ch << endl;  // 字符型变量ch = b

	// 4. 字符串型
	string str = "None";
	cout << "请给字符串型变量str赋值: " << endl;
	cin >> str;
	cout << "字符串型变量str = " << str << endl;  // 字符串型变量str = "Hello"

	// 5. bool
	bool flag = false;
	cout << "请给bool型变量flag赋值: " << endl;
	cin >> flag;  // bool类型只要是非0的值都代表真
	cout << "bool型变量flag = " << flag << endl;  // bool型变量bool = "1"

	/*
		请给整型变量a赋值:
		100
		整型变量a = 100
		请给浮点型变量f赋值:
		3.3
		浮点型变量f = 3.3
		请给字符型变量ch赋值:
		b
		字符型变量ch = b
		请给字符串型变量str赋值:
		Hello
		字符串型变量str = Hello
		请给bool型变量flag赋值:
		1
		bool型变量flag = 1
	*/

	system("pause");
	return 0;
}

3. 运算符

作用:用于执行代码的运算。

本章主要讲以下几类运算符:

运算符类型作用
算术运算符用于处理四则运算
赋值运算符用于将表达式的值赋给变量
比较运算符用于表达式的比较,并返回一个真值或假值
逻辑运算符用于根据表达式的值返回真值或假值

3.1 算术运算符

作用:用于处理四则运算。

算术运算符包含以下符号:

运算符术语示例结果注意
+正号+33
-负号-3-3
+10+515
-10-55
*10*550
/10/52除数不能是0
%取模(取余数)10%31必须是两个整数, 除数不能是0
++前置递增a=2; b=++a;a=3; b=3;
++后置递增a=2; b=a++;a=3; b=2;
前置递减a=2; b=–a;a=1; b=1;
后置递减a=2; b=a–;a=1; b=2;
  • 前置先执行再表达式运算;后置先表达式运算再执行
  • 取余操作的被除数可以是0,且结果恒等于0;但除数不能是0,会报错!

取余操作的本质就是除法

#include<iostream>
using namespace std;


int main() {

	// 1. 加减乘除
	int a1 = 10;
	int b1 = 3;

	/*
		因为a和b都是int,因此两个整数做除法仍然是int
	*/
	cout << a1 + b1 << endl;  // 13
	cout << a1 - b1 << endl;  // 7
	cout << a1 * b1 << endl;  // 30
	cout << a1 / b1 << endl;  // 3
	
	int a2 = 10;
	int b2 = 20;
	cout << a2 / b2 << endl;  // 0

	int a3 = 10;
	int b3 = 0;
	// cout << a3 / b3 << endl;  // 0  // Integer division by zero。

	// 两个小数相除
	double d1 = 0.5;
	double d2 = 0.22;
	cout << d1 / d2 << endl;  // 2.27273

	// 整数和小数相除
	int a4 = 2;
	double d3 = 3.14;
	cout << a4 / d3 << endl;  // 0.636943

	// 两个不同数据类型的小数相除
	float f1 = 3.14f;
	float d4 = 7.11;
	cout << f1 / d4 << endl;  // 0.441632

	// 2. 取模(取余)
	int aa1 = 10;
	int aa2 = 3;
	cout << aa1 % aa2 << endl;  // 1
	cout << aa2 % aa1 << endl;  // 3
	
	// 两个小数是不可以取余
	float ff1 = 3.14f;
	float ff2 = 1.01f;
	// cout << ff1 % ff2 << endl;  // 表达式必须具有整数或未区分范围的枚举类型(取余操作必须是整数!)

	// 取余操作的除数不能是0,被除数可以是0且结果恒等于0
	int aaa1 = 0;
	int aaa2 = 3;
	cout << aaa1 % aaa2 << endl;  // 0
	// cout << aaa2 % aaa1 << endl;  // Integer division by zero

	// 3. 前置递增、后置递增、前置递减、后置递减
	// 3.1 前置递增: 先让变量+1,然后进行表达式运算
	int a = 10;
	++a;  // 让变量+1
	cout << "a = " << a << endl;  // 11

	// 3.2 后置递增: 先进行表达式运算,再让变量+1
	int b = 10;
	b++;
	cout << "b = " << b << endl;  // 11

	// 前置和后置的区别
	a = 10;
	b = ++a * 10;
	cout << "a = " << a << endl;  // a = 11
	cout << "b = " << b << endl;  // b = 110

	a = 10;
	b = a++ * 10;
	cout << "a = " << a << endl;  // a = 11
	cout << "b = " << b << endl;  // b = 100

	// 3.3 前置递减
	a = 10;
	--a;
	cout << "a = " << a << endl;  // 9

	// 3.4 后置递减
	b = 10;
	b--;
	cout << "b = " << b << endl;  // 9

	// 前置和后置的区别
	a = 10;
	b = --a * 10;
	cout << "a = " << a << endl;  // a = 9
	cout << "b = " << b << endl;  // b = 90

	a = 10;
	b = a-- * 10;
	cout << "a = " << a << endl;  // a = 9
	cout << "b = " << b << endl;  // b = 100

	system("pause");
	return 0;
}

3.2 赋值运算符

作用:用于将表达式的值赋给变量。

赋值运算符包括以下几个符号:

运算符术语示例结果
=赋值a=2; b=3;a=2; b=3;
+=加等于a=0; a+=2;a=2;
-=减等于a=0; a-=2;a=-2;
*=乘等于a=2; a*=3;a=6;
/=除等于a=6; a/=3;a=2;
%=模等于a=6; a%=4;a=2;
#include<iostream>
using namespace std;


int main() {

	// =
	int a = 10;
	cout << "a = " << a << endl;  // a = 10
	a = 20;
	cout << "a = " << a << endl;  // a = 20

	// +=
	a = 10;
	a += 2;  // a = a + 2
	cout << "a = " << a << endl;  // a = 12

	// -=
	a = 10;
	a -= 2;
	cout << "a = " << a << endl;  // a = 8

	// *=
	a = 10;
	a *= 2;
	cout << "a = " << a << endl;  // a = 20

	// /=
	a = 10;
	a /= 2;
	cout << "a = " << a << endl;  // a = 5

	// %=
	a = 10;
	a %= 2;
	cout << "a = " << a << endl;  // a = 0

	system("pause");
	return 0;
}

3.3 比较运算符

作用:用于表达式的比较,并返回一个真值或假值。

比较运算符有以下符号:

运算符术语示例结果
==相等于4==30
!=不等于4!=31
<小于4<30
>大于4>31
<=小于等于4<=30
>=大于等于4>=31
#include<iostream>
using namespace std;


int main() {

	int a = 10;
	int b = 20;

	// ==
	cout << (a == b) << endl;  // 0

	// !=
	cout << (a != b) << endl;  // 1

	// >
	cout << (a > b) << endl;  // 0

	// <
	cout << (a < b) << endl;  // 1

	// >=
	cout << (a >= b) << endl;  // 0

	// <=
	cout << (a <= b) << endl;  // 1

	system("pause");
	return 0;
}

3.4 逻辑运算符

作用:用于根据表达式的值返回真值或假值

逻辑运算符有以下符号:

运算符术语示例结果
!!a如果a为假,则!a为真;如果a为真,则!a为假
&&a && b如果a和b都为真,则结果为真,否则为假
||a || b如果a或b有一个为真,则结果为真
#include<iostream>
using namespace std;


int main() {

	// 逻辑非 !
	int a = 10;
	cout << "!a: " << !a << endl;  // !a: 0
	cout << "!!a: " << !!a << endl;  // !!a: 1

	// 逻辑与 &&
	int b = 20;
	cout << "a && b: " << (a && b) << endl;  // a && b: 1
	bool c = false;
	cout << "b && c: " << (b && c) << endl;  // b && c: 0
	int d = 0;
	cout << "c && d: " << (c && d) << endl;  // c && d: 0
	

	// 逻辑或 ||
	cout << "a || b: " << (a || b) << endl;  // a || b: 1
	cout << "b || c: " << (b || c) << endl;  // b || c: 1
	cout << "c || d: " << (c || d) << endl;  // c || d: 0

	system("pause");
	return 0;
}

4. 程序流程结构

C/C++支持最基本的三种程序执行结构:顺序结构、选择结构、循环结构:

  • 顺序结构:程序按顺序执行,不发生跳转
  • 选择结构:依据条件是否满足,有选择的执行相应功能
  • 循环结构:依据条件是否满足,循环多次执行某段代码

4.1 选择结构

4.1.1 if语句

作用:执行满足条件的语句

if语句的三种形式:

  1. 单行格式if语句:if(条件){满足条件时执行的语句}
  2. 多行格式if语句:if(条件){满足条件时执行的语句}else{不满足条件时执行的语句}
  3. 多条件的if语句:if(条件){条件1满足时执行的语句}else if(条件2){条件2满足时执行的语句}... else{所有条件都不满足时执行的语句}

补充:嵌套if语句:在if语句中,可以嵌套使用if语句,达到更精确的条件判断。

示例:

#include<iostream>
using namespace std;


int main() {

	// 用户输入分数,如果分数大于600,视为考上一本大学,在屏幕上输出
	int score = 0;
	cout << "请输入分数: ";
	cin >> score;
	cout << "输入的分数为: " << score << "\r\n" << endl;

	// 1. 单行格式if语句:if(条件){满足条件时执行的语句}
	cout << "---------1. 单行格式if语句---------" << endl;
	if (score >= 600) {
		cout << "恭喜您考上了一本大学!" << endl;
	}


	// 2. 多行格式if语句:if(条件){满足条件时执行的语句}else{不满足条件时执行的语句}
	cout << "\r\n---------2. 多行格式if语句---------" << endl;
	if (score >= 600) {
		cout << "恭喜您考上了一本大学!" << endl;
	}
	else
	{
		cout << "很可惜,您没考上一本大学" << endl;
	}
	

	// 3. 多条件的if语句:if(条件){条件1满足时执行的语句}else if(条件2){条件2满足时执行的语句}... else{所有条件都不满足时执行的语句}
	cout << "\r\n---------3. 多条件的if语句---------" << endl;
	if (score >= 600) {
		cout << "恭喜您考上了一本大学!" << endl;
	}
	else if (score >= 450) {
		cout << "恭喜您考上了二本大学!" << endl;
	}
	else if (score >= 300) {
		cout << "恭喜您考上了三本大学!" << endl;
	}
	else if (score >= 200) {
		cout << "恭喜您考上了专科大学!" << endl;
	}
	else {
		cout << "很可惜,您的分数不足以考上大学" << endl;
	}


	// 补充:在一本分数中,如果大于700分,考入北大,大于650分,考入清华,大于600考入人大。
	cout << "\r\n---------补充---------" << endl;
	if (score >= 600) {
		cout << "恭喜您考上了一本大学!" << endl;
		if (score > 700) {
			cout << "您能考上北大!" << endl;
		}
		else if (score > 650) {
			cout << "您能考上清华!" << endl;
		}
		else {
			cout << "您能考上人大!" << endl;
		}
	}

	/*
		请输入分数: 620
		输入的分数为: 620

		---------1. 单行格式if语句---------
		恭喜您考上了一本大学!

		---------2. 多行格式if语句---------
		恭喜您考上了一本大学!

		---------3. 多条件的if语句---------
		恭喜您考上了一本大学!

		---------补充---------
		恭喜您考上了一本大学!
		您能考上人大!
	*/


	system("pause");
	return 0;
}

练习案例:有三只小猪ABC,请分别输入三只小猪的体重,并判断哪只小猪最重。

#include<iostream>
using namespace std;


int main() {
	
	// 1. 创建三只小猪的体重
	int a = 0;
	int b = 0;
	int c = 0;

	// 2. 用户输入三只小猪的重量
	cout << "请输入小猪A的体重: ";
	cin >> a;
	cout << "请输入小猪B的体重: ";
	cin >> b;
	cout << "请输入小猪C的体重: ";
	cin >> c;
	cout << endl;

	// 3. 显示三只小猪的体重
	cout << "小猪A的体重 = " << a << endl;
	cout << "小猪B的体重 = " << b << endl;
	cout << "小猪C的体重 = " << c << endl;
	cout << endl;

	// 4. 判断哪只最重
	if (a > b) {
		if (a > c) {
			cout << "小猪A最重!" << endl;
		}
	}
	else { // b > a
		if (b > c) {
			cout << "小猪B最重!" << endl;
		}
		else
		{
			cout << "小猪C最重!" << endl;
		}
	}

	/*
		请输入小猪A的体重: 10
		请输入小猪B的体重: 20
		请输入小猪C的体重: 30

		小猪A的体重 = 10
		小猪B的体重 = 20
		小猪C的体重 = 30

		小猪C最重!
	*/


	system("pause");
	return 0;
}

4.1.2 三目运算符

作用:通过三目运算符实现简单的判断

语法:表达式1 ? 表达式2 : 表达式3

解释:

  • 如果表达式1的值为真,执行表达式2,并返回表达式2的结果;
  • 如果表达式1的值为假,执行表达式3,并返回表达式3的结果。

Python中是 num = 值1 if 表达式1 else 值2

#include<iostream>
using namespace std;


int main() {

	// 创建三个变量 abc
	// 将a和b作比较,将变量大的值赋值给变量c

	int a = 10;
	int b = 20;
	int c = 0;

	c = a > b ? a : b;

	cout << "c = " << c << endl;  // c = 20

	// 在C++中,三目运算符返回的是变量,可以继续赋值
	(a > b ? a : b) = 100;
	cout << "a = " << a << endl;  // a = 10
	cout << "b = " << b << endl;  // b = 100

	system("pause");
	return 0;
}

4.1.3 switch语句

作用:执行多条件分支语句

语法:

switch (表达式) {
case 结果1: 
	执行语句;
	break;
case 结果2: 
	执行语句;
	break;
	
	...

default: 
	执行语句;
	break;
}

注意:

  1. switch语句中表达式类型只能是整型或字符型
  2. case里如果没有break,那么程序会一直向下执行
  3. switch相对if的优缺点:
    • 优点:结构清晰,执行效率高
    • 缺点:判断时候只能是整数或字符型,不可以是一个区间

示例:

#include<iostream>
using namespace std;


// switch语句
int main() {
	
	/*
		给电影打分:
			10 ~ 9:经典
			8 ~ 7:非常好
			6 ~ 5:一般
			5以下:烂片
	*/

	// 1. 提出用户给电影打分
	int score = 0;
	cout << "请给电影打分: ";
	cin >> score;

	// 2. 根据用户输入的分数来提出最终结果
	cout << "\r\nswitch语句结果:" << endl;
	switch (score)
	{
	case 10:
		cout << "您认为是经典电影" << endl;
		break;
	case 9:
		cout << "您认为是经典电影" << endl;
		break;
	case 8:
		cout << "您认为是一般电影" << endl;
		break;
	case 7:
		cout << "您认为是一般电影" << endl;
		break;
	case 6:
		cout << "您认为是一般电影" << endl;
		break;
	case 5:
		cout << "您认为是一般电影" << endl;
		break;
	default:
		cout << "您认为是烂片" << endl;
		break;
	}

	/*
	switch相对if的优缺点:
		优点:结构清晰,执行效率高
		缺点:判断时候只能是整数或字符型,不可以是一个区间
	*/

	cout << "\r\nif语句结果:" << endl;
	if (score >= 9) {
		cout << "您认为是经典电影" << endl;
	}
	else if (score >= 5) {
		cout << "您认为是一般电影" << endl;
	}
	else {
		cout << "您认为是烂片" << endl;
	}

	system("pause");
	return 0;
}

4.2 循环结构

4.2.1 while循环

作用:满足循环条件,执行循环语句

语法:while (循环条件) {循环语句}

解释:只要循环条件的结果为真,就执行循环语句。

注意:在执行循环语句时,程序必须提供跳出循环的出否,否则出现死循环

#include<iostream>
using namespace std;


int main() {

	// 在屏幕中打印 0~9 这10个数字
	int num = 0;

	while (num < 10) {
		cout << num << endl;
		num += 1;
	}

	system("pause");
	return 0;
}

while循环练习案例:猜数字

案例描述:系统随机生成一个1到100之间的数字,玩家进行猜测,如果猜错,提示玩家数字过大或过小,如果猜对恭喜玩家胜利,并且退出游戏。

#include<iostream>
using namespace std;
// 引入time头文件
#include<ctime>


int main() {

	// 添加随机数种子,利用当前系统时间生成随机数,防止每次随机数都一样(随机本质上是伪随机)
	srand((unsigned int)time(NULL));

	// 1. 生成随机数
	int num = rand() % 100 + 1;  // 生成0~99的随机数

	// 2. 让玩家进行猜测
	int val = 0;  // 玩家输入的数据

	//限定猜测次数(5次)
	int times = 5;

	while (1) {
		cout << "猜个数: ";
		cin >> val;
		times -= 1;

		// 3. 判断玩家的猜测并给出提示
		if (times != 0) {
			if (val > num) {
				cout << "猜的大了" << endl;
			}
			else if (val < num) {
				cout << "猜的小了" << endl;
			}
			else  // 猜对了 -> 退出游戏
			{
				cout << "猜对了!游戏结束..." << endl;
				break;
			}
			cout << "剩余次数" << times << "\n" << endl;
		}
		else
		{
			cout << "次数用尽,游戏失败..." << endl;
			break;
		}
	}

	system("pause");
	return 0;

	/*
		猜个数: 50
		猜的大了
		剩余次数4

		猜个数: 30
		猜的小了
		剩余次数3

		猜个数: 35
		猜对了!游戏结束...
	*/
}

4.2.2 do…while循环语句

作用:满足循环条件,执行循环语句

语法:do{循环语句} while(循环条件);

注意:与while的区别在于do...while先执行一次循环语句,再判断循环条件

在这里插入图片描述

do...whilewhile循环的区别在于: do...while会先执行一次循环语句,再判断循环条件。

#include<iostream>
using namespace std;


/*
	do...while和while的区别:
		do...while会先执行一次循环(不管条件是否满足)
*/
int main() {
	
	// 在屏幕中输出0~9这10个数字
	int num = 0;

	cout << "---------do while---------" << endl;
	do
	{
		cout << num << endl;
		num += 1;
	} while (num < 10);

	cout << "\r\n---------while---------" << endl;
	num = 0;
	while (num < 10) {
		cout << num << endl;
		num += 1;
	}

	system("pause");
	return 0;
}

练习案例:水仙花数

案例描述:水仙花数是指: 一个3位数,它的每个位上的数字的3次幂之和等于它本身

例如: 1 3 + 5 3 + 3 3 = 153 1^3 + 5^3 + 3^3 = 153 13+53+33=153

请利用do...while语句,求出所有3位数中的水仙花数

#include<iostream>
using namespace std;
#include<math.h>  // 引入数学头文件


int main() {

	// 1. 将所有的三位数进行输出(100 ~ 999)
	int num = 100;
	int a = 0;  // 个位
	int b = 0;  // 十位
	int c = 0;  // 百位
	int times = 1;

	// 2. 在所有三位数中找到水仙花数
	/*
		e.g. 153
		获取个位: 153 % 10 = 3 
		获取十位: 153 / 10 = 15; 15 % 10 = 5 
		获取百位: 153 / 100 = 1

		判断:个位**3 + 十位**3 + 百位**3 == 本身
	*/
	do
	{
		a = num % 10;
		b = num / 10 % 10;
		c = num / 100;

		if ((pow(a, 3) + pow(b, 3) + pow(c, 3)) == num) {
			cout << "水仙花数" << times << ": " << num << endl;
			times += 1;
		}
		num += 1;
	} while (num < 1000);


	/*
		水仙花数1: 153
		水仙花数2: 370
		水仙花数3: 371
		水仙花数4: 407
	*/


	system("pause");
	return 0;
}

4.2.3 for循环语句

作用: 满足循环条件,执行循环语句

语法: for(起始表达式; 条件表达式; 末尾循环体) {循环语句;}

示例:

#include<iostream>
using namespace std;


int main() {

	// 打印数字0~9
	for (int i = 0; i < 10; i++)
	{
		cout << i << endl;
	}

	system("pause");
	return 0;
}

注意:

  • for循环中的表达式,要用分号进行分割
  • while, do...while, for都是开发中常用的循环语句,for循环结构比较清晰,比较常用。

练习案例:敲桌子

案例描述:从1开始数的数字100,如果数字个位含有7,或者数字十位含有7,或者该数字是7的倍数,我们打印敲桌子,其余数字直接打印输出。

#include<iostream>
using namespace std;


int main() {

	// 1. 先输出1到100
	for (int i = 1; i <= 100; i++)
	{
		/*2. 从这100个数字中找到特殊数字,改为"敲桌子"
		*	特殊数字
		*	7的倍数: num % 7 == 0
		*	个位有7: num % 10 == 7
		*	十位有7: num / 10 == 7
		*/
		if (i % 7 == 0 || i % 10 == 7 || i / 10 == 7) {
			cout << "敲桌子" << endl;
		}
		else {
			cout << i << endl;
		}
	}

	system("pause");
	return 0;
}

4.2.4 嵌套循环

作用: 在循环体中再嵌套一层循环,解决一些实际问题

例如我们想在屏幕中打印如下图片,就需要利用嵌套循环。

在这里插入图片描述

#include<iostream>
using namespace std;


int main() {

	for (int i = 0; i < 11; i++)  // 行
	{
		for (int j = 0; j < 11; j++)  // 列
		{
			cout << "* ";
		}
		cout << endl;
	}

	system("pause");
	return 0;
}

练习案例:乘法口诀表

案例描述:利角嵌套循环,实现九九乘法表

#include<iostream>
using namespace std;


int main() {

	/*
	* 列数 * 行数 = 计算结构
	* 
	* 条件: 列数 <= 当前行数
	*/

	for (int i = 1; i < 10; i++)  // 行
	{
		for (int j = 1; j <= i; j++)  // 列
		{
			cout << i << "*" << j << "=" << i * j << "\t";
		}
		cout << endl;
	}

	/*
	1*1=1
	2*1=2   2*2=4
	3*1=3   3*2=6   3*3=9
	4*1=4   4*2=8   4*3=12  4*4=16
	5*1=5   5*2=10  5*3=15  5*4=20  5*5=25
	6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36
	7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49
	8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64
	9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81
	请按任意键继续. . .
	*/

	system("pause");
	return 0;
}

4.3 跳转语句

4.3.1 break语句

作用: 用于跳出选择结构或者循环结构

break使用的时机:

  • 出现在switch条件语句中,作用是终止case并跳出switch
  • 出现在循环语句中,作用是跳出当前的循环语句
  • 出现在嵌套循环中,跳出最近的内层循环语句

示例1:

#include<iostream>
using namespace std;

// break的使用时机
int main() {

	// 1. 出现在switch语句中	
	cout << "------ 1. 出现在switch语句中 ------" << endl;
	cout << "请选择副本的难度" << endl;
	cout << "1. 普通" << endl;
	cout << "2. 中等" << endl;
	cout << "3. 困难" << endl;

	int select = 0;  // 用户选择结果变量
	cout << "请选择(1 2 3): ";
	cin >> select;  // 等待用户输入

	switch (select)
	{
	case 1:
		cout << "您选择的是普通难度" << endl;
		break;
	case 2:
		cout << "您选择的是中等难度" << endl;
		break;
	case 3:
		cout << "您选择的是困难难度" << endl;
		break;
	default:
		cout << "您选择的选择有误!" << endl;
		break;
	}

	// 2. 出现在循环语句中
	cout << "\r\n------ 2. 出现在循环语句中 ------" << endl;
	for (int i = 0; i < 10; i++)
	{
		cout << i << endl;
		// 如果i==5, 退出循环
		if (i == 5) {
			break;
		}
	}

	// 3. 出现在嵌套循环语句中
	cout << "\r\n------ 3. 出现在嵌套循环语句中 ------" << endl;
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			// 如果j == 5,退出循环
			if (j == 5) {
				break;
			}
			cout << "* ";
		}
		cout << endl;
	}

	system("pause");
	return 0;
}

4.3.2 continue语句

作用: 在循环语句中,跳过本次循环,继续执行下一次循环。

#include<iostream>
using namespace std;


int main() {

	for (int i = 0; i <= 100; i += 1) {
		if (i % 10 == 0) {
			cout << endl;
		}

		// 如果是奇数则输出
		if (i % 2 == 1) {
			cout << i << "\t";
		}
		else
		{
			continue;
		}
	}

	/*
		1       3       5       7       9
		11      13      15      17      19
		21      23      25      27      29
		31      33      35      37      39
		41      43      45      47      49
		51      53      55      57      59
		61      63      65      67      69
		71      73      75      77      79
		81      83      85      87      89
		91      93      95      97      99
	*/

	system("pause");
	return 0;
}

4.3.3 goto语句

作用: 可以无条件跳转语句

跳转语法: goto 标记; -> goto FLAG; // 跳到标记
标记语法: 标记: -> FLAG: // 表明标记的位置

示例:

int main3() {

	goto FLAG; // 跳转到标记FLAG的位置

	FLAG:  // 确认标记的位置

	return 0;
}

解释: 如果标记的名称存在,当程序执行到goto语句时,会跳转到标记的位置

在这里插入图片描述

注意:学习goto语句只是为了明白这个语句的作用,我们写代码的时候不要用,跳来跳去的,麻烦且容易出bug.

#include<iostream>
using namespace std;

// goto语句
int main() {

	cout << "1. xxxxxxxx" << endl;
	cout << "2. xxxxxxxx" << endl;

	goto FLAG; // 跳转到标记FLAG的位置
	
	cout << "3. xxxxxxxx" << endl;
	cout << "4. xxxxxxxx" << endl;

	FLAG:  // 确认标记的位置
	cout << "5. xxxxxxxx" << endl;

	/*
		1. xxxxxxxx
		2. xxxxxxxx
		5. xxxxxxxx
		请按任意键继续. . .
	*/

	system("pause");
	return 0;
}

5. 数组 (array)

5.1 概述

所谓数组(array),就是一个集合,里面存放了相同类型的数据元素,数组有两个特点:

  1. 数组中的每个数据元素都是相同的数据类型
  2. 数组是由连续的内存位置组成的

在Python中,list是列表不是数组;Python的list里面可以存放不一样的数据类型,但数组中一定要存放相同的数据类型

5.2 一维数组

5.2.1 一维数组的定义方式

一维数组定义的三种方式:

  1. 数据类型 数组名 [数组长度];
  2. 数据类型 数组名[数组长度] = {值1, 值2, ...}; (如果在初始化数据时没有全部填写,则会用0来填补)
  3. 数据类型 数组名[] = {值1, 值2, ...];

C++中没有built-in的方法求数组的长度

总结:

  1. 数组名的命名规范与变量名命名规范一致,不要和变量重名
  2. 数组的下标从0开始索引

示例:

#include<iostream>
using namespace std;


// 一维数组的定义
int main() {
	// ----- 1. 数据类型 数组名 [数组长度]; -----
	cout << "----- 1. 数据类型 数组名 [数组长度]; -----" << endl;

	int arr1[5];  // 定义数组

	// 给数组中的元素赋值
	for (int i = 0; i < 5; i++)
	{
		arr1[i] = (i + 1) * 10;
	}

	// 访问数组元素
	for (int i = 0; i < 5; i++) {
		cout << "arr1[" << i << "]: " << arr1[i] << endl;
	}


	// ----- 2. 数据类型 数组名[数组长度] = { 值1, 值2, ... }; ----- 
	cout << "\r\n----- 2. 数据类型 数组名[数组长度] = {值1, 值2, ...}; ----- " << endl;

	int arr2[5] = { 10, 20, 30, 40, 50 };

	// 访问数组元素
	for (int i = 0; i < 5; i++) {
		cout << "arr2[" << i << "]: " << arr2[i] << endl;
	}


	// ----- 3. 数据类型 数组名[] = {值1, 值2, ...}; -----
	cout << "\r\n----- 3. 数据类型 数组名[] = {值1, 值2, ...}; -----" << endl;
	int arr3[] = { 90, 80, 60, 50, 40, 30, 20, 10, 0 };
	// 访问数组元素
	for (int i = 0; i < 9; i++)
	{
		cout << "arr3[" << i << "]: " << arr3[i] << endl;
	}

	/*
		----- 1. 数据类型 数组名 [数组长度]; -----
		arr1[0]: 10
		arr1[1]: 20
		arr1[2]: 30
		arr1[3]: 40
		arr1[4]: 50

		----- 2. 数据类型 数组名[数组长度] = {值1, 值2, ...}; -----
		arr2[0]: 10
		arr2[1]: 20
		arr2[2]: 30
		arr2[3]: 40
		arr2[4]: 50

		----- 3. 数据类型 数组名[] = {值1, 值2, ...}; -----
		arr3[0]: 90
		arr3[1]: 80
		arr3[2]: 60
		arr3[3]: 50
		arr3[4]: 40
		arr3[5]: 30
		arr3[6]: 20
		arr3[7]: 10
		arr3[8]: 0
	*/


	system("pause");
	return 0;
}

5.2.2 一维数组数组名

一维数组名称的用途:

  1. 可以统计整个数组在内存中的长度: sizeof(数组名)
  2. 可以获取数组在内存中的首地址: cout << arr << endl;

在这里插入图片描述

  • 单个元素的长度: sizeof(arr[0])
  • 整个数组的长度: sizeof(arr)

这样我们就可以得到数组中元素的个数,即数组的长度。

C++中没有built-in的方法求数组的长度

#include<iostream>
using namespace std;


int main() {

	// 1. 可以统计整个数组在内存中的长度: sizeof(数组名)
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	cout << "arr数组占用内存空间大小为: " << sizeof(arr) << "字节" << endl;  // arr数组占用内存空间大小为: 40字节
	cout << "arr数组每个元素占用内存空间大小为: " << sizeof(arr[0]) << "字节" << endl;  // arr数组每个元素占用内存空间大小为: 4字节
	cout << "arr数组的元素个数(数组长度)为: " << sizeof(arr) / sizeof(arr[0]) << endl;  // arr数组的元素个数(数组长度)为: 10
	
	// 2. 可以获取数组在内存中的首地址: cout << arr << endl; 
	cout << "arr数组的首地址为: " << arr << endl;  // 000000B955D0F748
	cout << "arr数组的首地址(十进制)为: " << (int) arr << endl;  // 1439758152
	cout << "arr数组的第一个元素的地址为: " << &arr[0] << endl;  // 000000B955D0F748
	cout << "arr数组的第一个元素的地址(十进制)为: " << (int)&arr[0] << endl;  // 1439758152
	cout << "arr数组的第二个元素的地址为: " << &arr[0] << endl;  // 000000B955D0F748
	cout << "arr数组的第二个元素的地址(十进制)为: " << (int)&arr[0] << endl;  // 1439758152

	/*
	* 1. &是取址符
	* 
	* 2. 可以看到,数组的首地址和数组第一个元素的地址是一样的!
	* 
	* 3. 因为数组在内存空间中是连续的,因此第一个元素的地址和第二个元素的地址差4(int的大小是4字节!)
	*/


	// 数组名是常量,不可以进行赋值
	// arr = 100;  // IDE报错: 表达式必须是可修改的左值

	system("pause");
	return 0;
}

练习案例1: 五只小猪称体重

案例描述: 在一个数组中记录了五只小猪的体重,如: int arr[5]= { 300, 350, 200, 400, 250 };

找出并打印最重的小猪体重。

#include<iostream>
using namespace std;


int main() {

	// 1. 创建5只小猪体重的数组
	int arr[5] = { 300, 350, 200, 400, 250 };

	// 2. 定义最大值
	int max_num = arr[0];
	int order = 0;
	
	// 3. 逻辑判断
	for (int i = 1; i < 5; i++)
	{
		if (arr[i] > max_num) {
			max_num = arr[i];
			order = i;
		}
	}

	// 4. 打印最大值
	cout << "最大值为: " << max_num;
	cout << "。是第 " << order + 1 << " 只小猪" << endl;  // 最大值为: 400。是第 4 只小猪
	
	system("pause");
	return 0;
}

练习案例2: 数组元素逆置

案例描述: 请声明一个5个元素的数组,并且将元素逆置.

(如原数组元素为: 1, 3, 2, 5, 4; 逆置后输出结果为: 4, 5, 2, 3, 1);

#include<iostream>
using namespace std;


int main() {

	// 1. 创建数组
	int arr[] = { 1, 3, 2, 5, 4 };
	
	// 2. 实现逆置
	/*
		 2.1 记录起始下标的位置
		 2.2 记录结束下标的位置
		 2.3 起始下标和结束下标的元素互换
		 2.4 起始位置++; 结束位置++
		 2.5 循环执行2.1操作,直到起始位置 >= 结束位置
	*/
	int start = 0;
	int end = sizeof(arr) / sizeof(arr[0]) - 1;
	int tmp = 0;

	while (start < end) {
		// 元素互换
		tmp = arr[start];
		arr[start] = arr[end];
		arr[end] = tmp;

		// 更新下标
		start += 1;
		end -= 1;
		
	}

	// 3. 打印逆置后的数组
	cout << "数组元素逆置后: " << endl;
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		cout << arr[i] << ", ";  // 4, 5, 2, 3, 1,
	}
	cout << endl;
	

	system("pause");
	return 0;
}

思路2:

#include<iostream>
using namespace std;


int main() {

	// 1. 创建数组
	int arr[] = { 1, 3, 2, 5, 4 };
	int length = sizeof(arr) / sizeof(arr[0]) - 1;

	// 2. 具体实现
	for (int i = 0; i <= length / 2; i++)  // 注意只走一半
	{	
		int tmp = arr[i];
		arr[i] = arr[length - i];
		arr[length - i] = tmp;
	}

	// 3. 打印逆置后的数组
	cout << "数组元素逆置后: " << endl;
	for (int i = 0; i <= length; i++)
	{
		cout << arr[i] << ", ";  // 4, 5, 2, 3, 1,
	}
	cout << endl;


	system("pause");
	return 0;
}

5.2.3 冒泡排序

作用: 最常用的排序算法,对数组内元素进行排序

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
  3. 重复以上的步骤,每次比较次数-1,直到不需要比较

示例: 将数组{ 4, 2, 8, 0, 5, 7, 1, 3, 9 }进行升序排序

#include<iostream>
using namespace std;


int main() {

	int arr[] = { 4, 2, 8, 0, 5, 7, 1, 3, 9 };
	int n = sizeof(arr) / sizeof(arr[0]) - 1;
	
	// 排序前
	cout << "排序前: " << endl;
	for (int i = 0; i <= n; i++)
	{
		cout << arr[i] << " ";  // 4 2 8 0 5 7 1 3 9
	}
	cout << endl;

	/*
		外层: n - 1
		内层: n - i - 1
	*/
	for (int i = 0; i < n - 1; i++)  // n - 1轮(外层)
	{
		for (int j = 0; j < n - i - 1; j++)  // 内层
		{
			if (arr[j] > arr[j + 1]) {
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}

	// 排序后
	cout << "排序后: " << endl;
	for (int i = 0; i <= n; i++)
	{
		cout << arr[i] << " ";  // 0 1 2 3 4 5 7 8 9
	}
	cout << endl;

	system("pause");
	return 0;
}

5.3 二维数组

二维数组就是在一维数组上,多加了一个维度。

5.3.1 二维数组定义方式

二维数组定义的四种方式:

  1. 数据类型 数组名[行数][列数];
  2. 数据类型 数组名[行数][列数] = { {数据1,数据2},{数据3,数据4 }};
  3. 数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};
  4. 数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};

建议: 以上4种定义方式,利用第二种更加直观,提高代码的可读性

在定义二维数组时,如果初始化了数据,可以省略行数

示例:

#include<iostream>
using namespace std;


int main() {

	// 1. 数据类型 数组名[行数][列数];
	int arr1[2][3];
	// 赋值
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++)
		{
			arr1[i][j] = (i + 1) * (j + 1) * 10;
		}
	}
	// 查看
	cout << "------ 1. 数据类型 数组名[行数][列数]; ------" << endl;
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++)
		{
			cout << arr1[i][j] << " ";
		}
		cout << endl;
	}


	// 2. 数据类型 数组名[行数][列数] = { {数据1,数据2},{数据3,数据4 }};
	int arr2[2][3] = { 
		{10, 20, 30}, 
		{40, 50, 60} 
	};
	// 查看
	cout << "\r\n------ 2. 数据类型 数组名[行数][列数] = { {数据1,数据2},{数据3,数据4 }}; ------" << endl;
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++)
		{
			cout << arr2[i][j] << " ";
		}
		cout << endl;
	}


	// 3. 数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};
	int arr3[2][3] = { 10, 20, 30, 40, 50, 60 };
	// 查看
	cout << "\r\n------ 3. 数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4}; ------" << endl;
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++)
		{
			cout << arr2[i][j] << " ";
		}
		cout << endl;
	}

	// 4. 数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4};
	int arr4[][3] = { 10, 20, 30, 40, 50, 60 };
	// 查看
	cout << "\r\n------ 4. 数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4}; ------" << endl;
	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 3; j++)
		{
			cout << arr2[i][j] << " ";
		}
		cout << endl;
	}

	/*
		------ 1. 数据类型 数组名[行数][列数]; ------
		10 20 30
		20 40 60

		------ 2. 数据类型 数组名[行数][列数] = { {数据1,数据2},{数据3,数据4 }}; ------
		10 20 30
		40 50 60

		------ 3. 数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4}; ------
		10 20 30
		40 50 60

		------ 4. 数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4}; ------
		10 20 30
		40 50 60
	*/

	system("pause");
	return 0;
}

5.3.2 二维数组数组名

  1. 查看二维数组所占内存空间
  2. 获取二维数组首地址
#include<iostream>
using namespace std;


int main() {

	// 1. 查看二维数组所占内存空间
	int arr[2][3] = {
		{1, 2, 3},
		{4, 5, 6}
	};

	cout << "二维数组占用的内存空间为: " << sizeof(arr) << "字节" << endl;  // 24字节
	cout << "二维数组第一行占用的内存空间为: " << sizeof(arr[0]) << "字节" << endl;  // 12字节
	cout << "二维数组第一个元素占用的内存空间为: " << sizeof(arr[0][0]) << "字节" << endl;  // 4字节

	// 统计出二维数组的行和列
	cout << "二维数组行数为: " << sizeof(arr) / sizeof(arr[0]) << endl;  // 2
	cout << "二维数组列数为: " << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;  // 3

	// 2. 获取二维数组首地址
	cout << "二维数组的首地址为: " << arr << endl;
	cout << "二维数组第一行的首地址为: " << arr[0] << endl;
	cout << "二维数组第二行的首地址为: " << arr[1] << endl;
	cout << "二维数组第一个元素的地址为: " << &arr[0][0] << endl;
	cout << "二维数组第二个元素的地址为: " << &arr[0][1] << endl;


	/*
		第一行和第二行首地址差12(因为列=3)

		二维数组的首地址为: 000000438C4FF648
		二维数组第一行的首地址为: 000000438C4FF648
		二维数组第二行的首地址为: 000000438C4FF654
		二维数组第一个元素的地址为: 000000438C4FF648
		二维数组第二个元素的地址为: 000000438C4FF64C
	*/

	system("pause");
	return 0;
}

5.3.3 二维数组应用案例

考试成绩统计:

案例描述: 有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩。

姓名语文数学英语
张三100100100
李四9050100
王五607080
#include<iostream>
using namespace std;
#include<string>


// 二维数组案例 —— 考试成绩统计
int main() {

	// 1. 创建二维数组
	int scores[3][3] = {
		{100, 100, 100},
		{90, 50, 100},
		{60, 70, 80},
	};

	string names[3] = {
		"张三",
		"李四",
		"王五",
	};

	// 2. 统计每个人的总和分数
	for (int i = 0; i < 3; i++)  // 行
	{
		int sum = 0;  // 统计分数总和
		for (int j = 0; j < 3; j++)  // 列
		{
			sum += scores[i][j];
		}

		// 打印每个人的总成绩
		cout << names[i] << "的总分为: " << sum << endl;
	}

	/*
		张三的总分为: 300
		李四的总分为: 240
		王五的总分为: 210
	*/

	system("pause");
	return 0;
}

6. 函数

6.1 概述

作用: 将一段经常使用的代码封装起来,减少重复代码。

一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能。

6.2 函数的定义

函数的定义一般主要有5个步骤:

  1. 返回值类型
  2. 函数名
  3. 参数表列
  4. 函数体语句
  5. return表达式

语法:

返回值类型 函数名 (形参1, 形参2, ...) {

	函数体语句;

	return表达式;
}
  • 返回值类型: 一个函数可以返回一个值。在函数定义中
  • 函数名: 给函数起个名称
  • 参数列表: 使用该函数时,传入的数据
  • 函数体语句: 花括号内的代码,函数内需要执行的语句
  • return表达式: 和返回值类型挂钩,函数执行完后,返回相应的数据

示例:

// 加法函数,实现两个整型相加并返回相加的结果
int add(int num1, int num2) {
	int sum = num1 + num2;

	return sum;
}

6.3 函数的调用

功能: 使用定义好的函数

语法: 函数名(参数1, 参数2, ...)

示例:

#include<iostream>
using namespace std;


// 加法函数,实现两个整型相加并返回相加的结果
int add(int num1, int num2) {
	
	int sum = num1 + num2;

	return sum;
}


int main() {

	int a = 10;
	int b = 20;

	int res = add(a, b);
	cout << "两数之和为: " << res << endl;  // 两数之和为: 30

	//system("pause");  // 按任意键继续的功能
	return 0;
}

6.4 值传递

  • 所谓值传递,就是函数调用时实参将数值传入给形参
  • 值传递时,如果形参发生,并不会影响实参
#include<iostream>
using namespace std;


// 如果函数不需要返回值,声明的时候可以写void
void swap(int num1, int num2) {

	cout << "交换前: " << "num1: " << num1 << "\t num2: " << num2 << endl;
	int tmp = num1;
	num1 = num2;
	num2 = tmp;
	cout << "交换后: " << "num1: " << num1 << "\t num2: " << num2 << endl;
}


int main() {

	int a = 10;
	int b = 20;

	swap(a, b);

	/*
		交换前: num1: 10         num2: 20
		交换后: num1: 20         num2: 10
	*/

	//system("pause");  // 按任意键继续的功能
	return 0;
}

void: 中文翻译为“无类型”。常用在程序编写中对定义函数的参数类型、返回值、函数中指针类型进行声明。void的字面意思是“无类型”。

6.5 函数的常见样式

常见的函数样式有4种:

  1. 无参无返
  2. 有参无返
  3. 无参有返
  4. 有参有返
#include<iostream>
using namespace std;


// 1. 无参无返
void test_01() {
	cout << "1. 无参无返" << endl;
}

// 2. 有参无返
void test_02(int a) {
	cout << "2. 有参无返: ";
	cout << a << endl;
}

// 3. 无参有返
float test_03() {
	cout << "3. 无参有返" << endl;
	return 3.14f;
}

// 4. 有参有返
double test_04(float a) {
	cout << "4. 有参有返" << endl;
	return a;
}


int main() {

	// 1. 无参无返
	test_01();  // 1. 无参无返

	// 2. 有参无返
	test_02(10086);  // 2. 有参无返 10086

	// 3. 无参有返
	float return_param_1 = test_03();  // 3. 无参有返
	cout << "return_param_1: " << return_param_1 << endl;  // return_param_1: 3.14
	
	// 4. 有参有返
	double return_param_2 = test_04(6.17f);  // 4. 有参有返
	cout << "return_param_2: " << return_param_2 << endl;  // return_param_2: 6.17

	return 0;
}

6.6 函数的声明

作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

  • 函数的声明可以多次,但是函数的定义只能有一次
#include<iostream>
using namespace std;  // 让我们可以使用cout在屏幕输出


// 提前告诉编译器函数的存在,可以利用函数的声明
// 函数的声明可以有多次,但函数的定义只能有一次!
int max(int a, int b);
int max(int a, int b);
int max(int a, int b);


int main() {

	int a = 10;
	int b = 20;
	int max_num = max(a, b);
	cout << "max_num: " << max_num;  // max_num: 20

	return 0;
}


// 函数的定义只能有一次!
int max(int a, int b) {
	return a > b ? a : b;
}

6.7 函数的分文件编写

作用: 让代码结构更加清晰

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

  1. 创建后缀名为.h的头文件 -> swap.h
  2. 创建后缀名为.cpp的源文件 -> swap.cpp
  3. 在头文件中写函数的声明
  4. 在源文件中写函数的定义

示例:

1. 创建后缀名为.h的头文件 -> swap.h

2. 创建后缀名为.cpp的源文件 -> swap.cpp

3. 在头文件中写函数的声明

#include<iostream>
using namespace std;


// 函数的声明
void swap(int num1, int num2);

4. 在源文件中写函数的定义

#include "swap.h"

// 函数的定义
void swap(int num1, int num2) {
	cout << "交换前: " << "num1: " << num1 << "\t num2: " << num2 << endl;
	int tmp = num1;
	num1 = num2;
	num2 = tmp;
	cout << "交换后: " << "num1: " << num1 << "\t num2: " << num2 << endl;
}

main函数所在.cpp文件

#include<iostream>
using namespace std;  // 让我们可以使用cout在屏幕输出
#include "swap.h"

/*
	函数分文件编写一般有4个步骤:
	1. 创建后缀名为.h的头文件: swap.h
	2. 创建后缀名为.cpp的源文件: swap.cpp
	3. 在头文件中写函数的声明
	4. 在源文件中写函数的定义
*/


int main() {

	int a = 10;
	int b = 20;

	swap(a, b);
	/*
		交换前: num1: 10         num2: 20
		交换后: num1: 20         num2: 10
	*/

	return 0;
}

7. 指针 (pointer)

7.1 指针的基本概念

指针的作用: 可以通过指针间接访问内存

  • 内存编号是从0开始记录的,一般用十六进制数字表示
  • 可以利用指针变量保存地址

简单来说,指针就是一个地址

7.2 指针变量的定义和使用

指针变量定义语法: 数据类型 *变量名;

示例:

#include<iostream>
using namespace std;


int main() {

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

	int* p;;  // 这就定义了一个指针

	// 让指针*p记录变量a的地址
	p = &a;

	cout << "a的地址为: " << &a << endl;  // 000000F62119F7C4
	cout << "指针p为: " << p << endl;  // 000000F62119F7C4
	cout << "*p为: " << *p << endl;  // 10


	// 2. 使用指针
	// 可以通过解引用的方式来找到指针指向的内存
	// 指针前加一个*代表解引用,即找到指针指向的内存中的数据
	*p = 1000;
	cout << "a = " << a << endl;  // 1000
	cout << "*p = " << *p << endl;  // 1000

	return 0;
}

7.3 指针所占的内存空间

提问:指针也是一种数据类型,那么这种数据类型占用多少内存空间?

回答:在32位操作系统下,指针都占用4个字节空间;在64位操作系统下,指针占用8个字节空间。

在IDE中可以修改系统环境的位数:

在这里插入图片描述

示例:

#include<iostream>
using namespace std;


int main() {

	int a = 10;

	int* p = &a;

	cout << "sizeof(int*) = " << sizeof(int*) << "字节" << endl;  // 8字节
	cout << "sizeof(int*) = " << sizeof(p) << "字节" << endl;  // 8字节
	cout << "sizeof(float*) = " << sizeof(float*) << "字节" << endl;  // 8字节
	cout << "sizeof(double*) = " << sizeof(double*) << "字节" << endl;  // 8字节
	cout << "sizeof(char*) = " << sizeof(char*) << "字节" << endl;  // 8字节
	cout << "sizeof(long*) = " << sizeof(long*) << "字节" << endl;  // 8字节
	cout << "sizeof(long long*) = " << sizeof(long long*) << "字节" << endl;  // 8字节

	/* 32位操作系统
		sizeof(int*) = 4字节
		sizeof(float*) = 4字节
		sizeof(double*) = 4字节
		sizeof(char*) = 4字节
		sizeof(long*) = 4字节
		sizeof(long long*) = 4字节
	*/

	/* 64位操作系统
		sizeof(int*) = 8字节
		sizeof(float*) = 8字节
		sizeof(double*) = 8字节
		sizeof(char*) = 8字节
		sizeof(long*) = 8字节
		sizeof(long long*) = 8字节
	*/

	return 0;
}

7.4 空指针和野指针

  • 空指针: 指针变量指向内存中编号为0的空间
    • 用途: 初始化指针变量
    • 注意: 空指针指向的内存是不可以访问的

指针不知道指向哪里好,就指向空
0 ~ 255这块内存是系统占用的,我们不可以访问

#include<iostream>
using namespace std;


// 空指针
int main() {

	// 1. 空指针用于给指针变量进行初始化
	int* p = NULL;
	cout << "p: " << p << endl;  // p: 0000000000000000
	
	// 2. 空指针是不可以进行访问的(0 ~ 255的内存编号是系统占用的,因此不可以访问)
	// *p = 100;  // 引发了异常: 写入访问权限冲突。p 是 nullptr。

	return 0;
}
  • 野指针:指针变量指向非法的内存空间。

示例:

#include<iostream>
using namespace std;


// 野指针:在程序中尽量避免出现野指针
int main() {

	// 没有申请内存就直接让指针指向这个地址
	int* p = (int*)0x1100;

	cout << "p: " << p << endl;  // p: 0000000000001100
	cout << "*p: " << *p << endl;  // 引发了异常: 读取访问权限冲突。p 是 0x1100。

	// 因为我们之前没有申请0x1100这个地址,所以C++程序是没有权限去操作这块地址

	return 0;
}

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

7.5 const修饰指针

const修饰指针有三种情况:

  1. const修饰指针 -> 常量指针 -> const int* p = &a;
  2. const修饰常量 -> 指针常量 -> int* const p = &a;
  3. const即修饰指针,又修饰常量 -> const int* const p = &a;

记名称:常量=const, 指针=*,这样就好记了
对于常量指针,可以理解为const限制的*,所以 *p 的操作就不可以了
对于指针常量,可以理解为const限制的p,所以 p 的操作就不可以了
const修饰谁,谁就是常量,常量是不可以修改的!

7.5.1 常量指针

const修饰指针 -> 常量指针 -> const int* p = &a;

特点:指针的指向可以修改,但指针指向的值不可以修改,即

int a = 10;
int b = 10;

// 定义常量指针
const int* p = &a;

// 判断
*p = 20;  // 不可以,指针不可以修改指向的值
p = &b;  // 可以,指针可以修改指向

7.5.2 指针常量

const修饰常量 -> 指针常量 -> int* const p = &a;

特点:指针的指向不可以改,指针指向的值可以修改。

int a = 10;
int b = 10;

// 定义常量指针
int* const p = &a;

// 判断
*p = 20;  // 可以,指针可以修改指向的值
p = &b;  // 不可以,指针不可以修改指向

7.5.2 const即修饰指针,又修饰常量

const即修饰指针,又修饰常量 -> const int* const p = &a;

特点:指针的指向和指向的值都不可以改!

int a = 10;
int b = 10;

// 定义常量指针
const int* const p = &a;

// 判断
*p = 20;  // 不可以,指针不可以修改指向的值
p = &b;  // 不可以,指针不可以修改指向

示例:

#include<iostream>
using namespace std;


int main() {
	int a = 10;
	int b = 20;

	// 1. const修饰指针 -> 常量指针
	const int* p1 = &a;
	// *p1 = 20;  // 报错:“p1” : 不能给常量赋值	指针
	p1 = &b;  // 不报错


	// 2. const修饰常量 -> 指针常量
	int* const p2 = &a;
	*p2 = 20;  // 不报错
	// p2 = &b;  // 报错:“p2” : 不能给常量赋值


	// 3. const修饰指针和常量
	const int* const p3 = &a;
	// *p3 = 20;  // 报错:表达式必须是可修改的左值
	// p3 = &b;  // 报错:“p3” : 不能给常量赋值


	return 0;
}

技巧: 看const右侧紧跟着的是指针还是常量,是指针就是常量指针,是常量就是指针常量

7.6 指针和数组

作用:利用指针访问数组中元素

示例:

#include<iostream>
using namespace std;


int main() {

	// 利用指针访问数组中的元素
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	cout << "第一个元素为: " << arr[0] << endl;  // 1

	int* p = arr;  // 数组名就是数组第一个元素的地址(数组的首地址)
	cout << "利用指针访问第一个元素: " << *p << endl;  // 1

	p += 1;  // 让指针向后偏移4个字节

	cout << "利用指针访问第二个元素: " << *p << endl;  // 1  // 2

	cout << "\r\n--------利用指针遍历数组--------" << endl;

	int* p2 = arr;
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		cout << *p2 << " ";  // 1 2 3 4 5 6 7 8 9 10
		p2 += 1;
	}
	cout << endl;

	return 0;
}

7.7 指针和函数

作用:利用指针作函数参数,可以修改实参的值

#include<iostream>
using namespace std;


// 实现两个数字进行交互
void swap_01(int a, int b) {
	int tmp = a;
	a = b;
	b = tmp;
	cout << "----形参----" << endl;
	cout << "[swap_01] a = " << a << endl;
	cout << "[swap_01] b = " << b << endl;
}


void swap_02(int* p1, int* p2) {
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
	cout << "----形参----" << endl;
	cout << "[swap_02] a = " << *p1 << endl;
	cout << "[swap_02] b = " << *p2 << endl;
}


int main() {

	// 1. 值传递: 不会修改实参
	int a = 10;
	int b = 20;
	swap_01(a, b);
	cout << "----实参----" << endl;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	/*
		----形参----
		[swap_01] a = 20
		[swap_01] b = 10
		----实参----
		a = 10
		b = 20
	*/


	// 2. 地址传递: 可以修改实参
	swap_02(&a, &b);
	cout << "----实参----" << endl;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	/*
		----形参----
		[swap_02] a = 20
		[swap_02] b = 10
		----实参----
		a = 20
		b = 10
	*/

	return 0;
}

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

7.8 指针、数组、函数

案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序。

例如数组: int arr[10] = { 4, 3, 6, 9, 1, 2, 10, 8, 7, 5 }

#include<iostream>
using namespace std;


void bubble_sort(int* arr, int n) {
	/*
	* arr: 数组的首地址(因为要接收一个地址,所以数据类型是int*)
	* n: 数组的长度
	*/

	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (arr[j] > arr[j + 1]) {
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}


void print_array(int* arr, int n) {
	/*
	* arr: 数组的首地址(因为要接收一个地址,所以数据类型是int*)
	* n: 数组的长度
	*/
	for (int i = 0; i < n; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}


int main8() {

	// 1. 创建数组
	int arr[10] = { 4, 3, 6, 9, 1, 2, 10, 8, 7, 5 };
	int len = sizeof(arr) / sizeof(arr[0]);

	// 2. 创建函数,实现冒泡排序
	bubble_sort(arr, len);

	// 3. 打印排序后的数组
	print_array(arr, len);  // 1 2 3 4 5 6 7 8 9 10

	return 0;
}

8. 结构体

8.1 结构体基本概念

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。

8.2 结构体定义和使用

语法: struct 结构体名 { 结构体成员列表 };

通过结构体创建变量的方式有三种:

  1. struct 结构体名 变量名;
  2. struct 结构体名 变量名 = { 成员1值, 成员2值, ... };
  3. 定义结构体时顺便创建变量

注意

  • 在日常使用时,方法1和方法2用的比较多,第3种用的比较少!
  • 在定义结构体变量时,struct关键字可以省略

总结

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

可以看到,C/C++中的结构体和Python中的class很像。

示例:

#include<iostream>
using namespace std;
#include<string>

// 1. 创建学生数据类型:姓名,年龄,分数
struct Student
{
	// 成员列表
	string name;
	int age;
	int score;
};


struct Student2
{
	// 成员列表
	string name;
	int age;
	int score;
}s3;  // 在创建结构体的时候创建顺便创建变量


int main() {

	// 2. 通过学生类型创建具体学生

	// 2.1 struct 结构体名 变量名;
	struct Student s1;
	s1.name = "张三";
	s1.age = 18;
	s1.score = 100;

	// cout << s1 << endl;  // 没有与这些操作数匹配的
	cout << "姓名: " << s1.name << "\t年龄: " << s1.age << "\t分数: " << s1.score << endl;  
	// 姓名: 张三      年龄: 18        分数: 100



	// 2.2 struct 结构体名 变量名 = { 成员1值, 成员2值, ... }; 
	Student s2 = { "李四", 19, 80 };  // 在创建结构体变量时struct关键字可以省略
	cout << "姓名: " << s2.name << "\t年龄: " << s2.age << "\t分数: " << s2.score << endl;  
	// 姓名: 李四      年龄: 19        分数: 80

	// 2.3 定义结构体时顺便创建变量
	s3.name = "王五";
	s3.age = 20;
	s3.score = 60;
	cout << "姓名: " << s3.name << "\t年龄: " << s3.age << "\t分数: " << s3.score << endl;  
	// 姓名: 王五      年龄: 20        分数: 60

	return 0;
}

8.3 结构体数组

作用:将自定义的结构体放入到数组中以方便维护。

语法:struct 结构体名 数组名[元素个数] = { {}, {}, ... }

示例:

#include<iostream>
using namespace std;
#include<string>

// 1. 定义结构体
struct Student
{
	string name;
	int age;
	int score;
};


int main() {

	// 2. 创建结构体数组
	Student stuArray[] = {
		{"张三", 18, 100},
		{"李四", 20, 99},
		{"王五", 38, 80},
	};

	// 3. 给结构体数组中的元素赋值
	stuArray[2].name = "赵六";
	stuArray[2].age = 40;
	stuArray[2].score = 75;

	// 4. 遍历结构体数组
	for (int i = 0; i < 3; i++)
	{
		cout << "姓名: "   << stuArray[i].name 
			 << "\t年龄: " << stuArray[i].age 
			 << "\t分数: " << stuArray[i].score 
			 << endl;
	}

	/*
		姓名: 张三      年龄: 18        分数: 100
		姓名: 李四      年龄: 20        分数: 99
		姓名: 赵六      年龄: 40        分数: 75
	*/


	return 0;
}

8.4 结构体指针

作用:通过指针访问结构体中的成员

  • 利用操作符 -> 可以通过结构体指针访问结构体属性
struct Student {
	string name;
	int age;
	int score;
}

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

int main() {
	
	Student s1 = { "张三", 18, 100 };

	Student* p = &s1;

	// 利用 -> 访问结构体中的成员变量
	cout << p->name << endl;
	cout << p->age << endl;
	cout << p->score << endl;

	return 0;
}

示例:

#include<iostream>
using namespace std;
#include<string>

// 1. 定义结构体
struct Student
{
	string name;
	int age;
	int score;
};


int main() {

	// 2. 创建学生的结构体变量
	Student s = { "张三", 18, 100 };


	// 3. 通过指针指向结构体变量
	// int* p = &s;  // "Student *" 类型的值不能用于初始化
	Student* p = &s;  // 结构体就是我们自定义的数据类型,指针当然也要使用这种数据类型


	// 4. 通过指针访问结构体变量中的数据
	cout << "姓名: " << p->name << "\t年龄: " 
		<< p->age << "\t分数: " << p->score << endl;
	// 姓名: 张三      年龄: 18        分数: 100

	return 0;
}

8.5 结构体嵌套结构体

作用:结构体中的成员可以是另一个结构体。

例子:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体。

示例:

#include<iostream>
using namespace std;
#include<string>


// 1. 定义学生的结构体
struct Student
{
	string name;
	int age;
	int score;
};


// 2. 定义老师的结构体
struct Teacher
{
	int id;  // 教师编号
	string name;
	int age;
	
	Student stu;  // 辅导的学生
};


int main() {

	// 3. 创建老师
	Teacher t = { 10086, "王老师", 50, {"小王", 16, 60} };
	/*
		Teacher t;
		t.id = 10086;
		t.name = "王老师";
		t.age = 50;
		t.stu.name = "小王";
		t.stu.age = 16;
		t.stu.score = 60;
	*/

	cout << "老师姓名: " << t.name << "\t老师编号: " 
		<< t.id	<< "\t老师年龄: " << t.age 
		<< "\n辅导的学生的姓名: " << t.stu.name 
		<< "\t辅导的学生的年龄: " << t.stu.age 
		<< "\t辅导的学生的分数" << t.stu.score 
		<< endl;
	/*
		老师姓名: 王老师        老师编号: 10086 老师年龄: 50
		辅导的学生的姓名: 小王  辅导的学生的年龄: 16    辅导的学生的分数60
	*/

	return 0;
}

8.6 结构体做函数参数

作用:将结构体作为参数向函数中传递

传递方式有两种:

  1. 值传递: 改变形参 不会 改变实参
  2. 地址传递:改变形参 改变实参

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

示例:

#include<iostream>
using namespace std;
#include<string>


// 定义学生结构体
struct Student
{
	string name;
	int age;
	int score;
};


// 打印学生信息的函数
// 1. 值传递
void print_student_1(Student stu) {
	cout << "---------子函数[print_student_1]中打印---------" << endl;

	stu.name = "[值传递修]改后的姓名";

	cout << "姓名: " << stu.name << "\t年龄: "
		<< stu.age << "\t分数: " << stu.score
		<< endl;
}

// 2. 地址传递
void print_student_2(Student* p) {
	cout << "---------子函数[print_student_2]中打印---------" << endl;

	p->name = "[地址传递]修改后的姓名";

	cout << "姓名: " << p->name << "\t年龄: "
		<< p->age << "\t分数: " << p->score
		<< endl;
}


int main() {

	Student stu;
	stu.name = "张三";
	stu.age = 20;
	stu.score = 80;


	// 将学生传入到一个函数中,打印学生身上所有的信息
	// 1. 值传递
	print_student_1(stu);

	cout << "---------main函数中打印---------" << endl;
	cout << "姓名: " << stu.name << "\t年龄: "
		<< stu.age << "\t分数: " << stu.score
		<< endl << endl << endl;


	// 2. 地址传递
	print_student_2(&stu);

	cout << "---------main函数中打印---------" << endl;
	cout << "姓名: " << stu.name << "\t年龄: "
		<< stu.age << "\t分数: " << stu.score
		<< endl;

	/*
		---------子函数[print_student_1]中打印---------
		姓名: [值传递修]改后的姓名      年龄: 20        分数: 80
		---------main函数中打印---------
		姓名: 张三      年龄: 20        分数: 80


		---------子函数[print_student_2]中打印---------
		姓名: [地址传递]修改后的姓名    年龄: 20        分数: 80
		---------main函数中打印---------
		姓名: [地址传递]修改后的姓名    年龄: 20        分数: 80
	*/

	return 0;
}

8.7结构体中const使用场景

作用:用const来防止误操作。

示例:

#include<iostream>
using namespace std;
#include<string>


struct Student
{
	string name;
	int age;
	int score;
};


void print_stu_info_value_trans(Student stu) {
	/*
	* 这种值传递的方式会有问题:
	* 值传递的形参会复制传入的值,因此会带来额外的性能开销。
	* 如果调用这个函数的次数很多,那么带来的性能开销就会变大!
	* 
	* 要想减小开销,可以使用地址传递!
	*/
	stu.age += 1;  // 让年龄增加一岁
	cout << "姓名: " << stu.name << "\t年龄: " 
		<< stu.age << "\t分数: " << stu.score 
		<< endl;
}


void print_stu_info_pointer_trans(const Student* stu) {
	/*
	* 值传递因为会复制实参,带来了大量的性能开销,而地址传递
	* 传入的是指针,一个指针的大小为4字节(32位系统下),因此
	* 复制指针带来的性能开销明显降低!
	* 
	* 但是地址传递有一个问题:那就是改变形参会改变实参。
	* 为了解决这个问题,我们应该使用const关键字!
	* 
	* 具体实现是给形参加一个const,即const Student+* stu。
	* const修饰的是*,因此指针指向地址的值是不允许改变的,
	* 这样stu->age += 1;这行语句写完后就IDE就会报错,告诉
	* 我们不要修改指针指向地址的值!
	*/

	//stu->age += 1;  // 表达式必须是可修改的左值

	cout << "姓名: " << stu->name << "\t年龄: "
		<< stu->age + 1 << "\t分数: " << stu->score
		<< endl;
}


int main() {

	// 创建结构体变量
	Student stu = { "张三", 18, 90 };

	// 通过函数打印结构体变量的信息
	print_stu_info_value_trans(stu);
	cout << "-----调用print_stu_info_value_trans后-----" << endl;
	cout << "姓名: " << stu.name << "\t年龄: "
		<< stu.age << "\t分数: " << stu.score
		<< endl << endl << endl;
	

	print_stu_info_pointer_trans(&stu);
	cout << "-----调用print_stu_info_pointer_trans后-----" << endl;
	cout << "姓名: " << stu.name << "\t年龄: "
		<< stu.age << "\t分数: " << stu.score
		<< endl;

	/*
		姓名: 张三      年龄: 19        分数: 90
		-----调用print_stu_info_value_trans后-----
		姓名: 张三      年龄: 18        分数: 90


		姓名: 张三      年龄: 19        分数: 90
		-----调用print_stu_info_pointer_trans后-----
		姓名: 张三      年龄: 18        分数: 90
	*/

	return 0;
}

8.8 结构体案例

8.8.1 案例1

案例描述:学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下:

  • 设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员
  • 学生的成员有姓名、考试分数
  • 创建数组存放3名老师,通过函数给每个老师及所带的学生赋值
  • 最终打印出老师数据以及老师所带的学生数据。

示例:

#include<iostream>
using namespace std;
#include<string>
#include<ctime>


// 定义学生的结构体
struct Student
{
	string stu_name;
	int stu_score;
};


// 定义老师的结构体
struct Teacher
{
	string teac_name;

	// 学生数组
	Student stu_array[5];
};


// 给老师和学生赋值的函数
void allocate_space(Teacher teac_array[], int len) {
	string name_seed = "ABCDE";

	for (int i = 0; i < len; i++)  // 外层:给老师赋值
	{
		teac_array[i].teac_name = "Teacher_";
		teac_array[i].teac_name += name_seed[i];

		for (int j = 0; j < 5; j++)  // 内层:给学生赋值
		{
			teac_array[i].stu_array[j].stu_name = "Student_";
			teac_array[i].stu_array[j].stu_name += name_seed[j];

			int random = rand() % 61 + 40;  // 40 ~ 100
			teac_array[i].stu_array[j].stu_score = random;
		}
	}
}


void print_info(Teacher teac_array[], int len) {
	for (int i = 0; i < len; i++)
	{
		cout << "老师姓名: " << teac_array[i].teac_name << endl;
		
		for (int j = 0; j < 5; j++)
		{
			cout << "\t学生姓名: " << teac_array[i].stu_array[j].stu_name 
				<< "\t考试分数: " << teac_array[i].stu_array[j].stu_score << endl;
		}
	}
}


int main() {

	// 加入随机数种子
	srand((unsigned int)time(NULL));

	// 1. 创建3名老师的数组
	Teacher teac_array[3];

	// 2. 通过函数给3名老师的信息赋值,并给老师带的学生赋值
	int len = sizeof(teac_array) / sizeof(teac_array[0]);
	allocate_space(teac_array, len);

	// 3. 打印所有老师及所带学生的信息
	print_info(teac_array, len);

	/*
	老师姓名: Teacher_A
			学生姓名: Student_A     考试分数: 78
			学生姓名: Student_B     考试分数: 40
			学生姓名: Student_C     考试分数: 86
			学生姓名: Student_D     考试分数: 61
			学生姓名: Student_E     考试分数: 42
	老师姓名: Teacher_B
			学生姓名: Student_A     考试分数: 87
			学生姓名: Student_B     考试分数: 61
			学生姓名: Student_C     考试分数: 100
			学生姓名: Student_D     考试分数: 68
			学生姓名: Student_E     考试分数: 48
	老师姓名: Teacher_C
			学生姓名: Student_A     考试分数: 88
			学生姓名: Student_B     考试分数: 83
			学生姓名: Student_C     考试分数: 53
			学生姓名: Student_D     考试分数: 79
			学生姓名: Student_E     考试分数: 90
	*/

	return 0;
}

8.8.2案例2

案例描述:设计一个英雄的结构体,包括成员姓名,年龄,性别;创建结构体数组,数组中存放5名英雄。

通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。

五名英雄信息如下:

{"盖伦", 23, "男"},
{"卡特", 22, "女"},
{"赵信", 30, "男"},
{"嘉文四世", 25, "男"},
{"瑞文", 24, "女"},
{"亚索", 20, "男"},
{"卡莎", 26, "女"},

示例:

#include<iostream>
using namespace std;
#include<string>


// 1. 设计英雄结构体
struct Hero
{
	string name;
	int age;
	string gender;
};


void bubble_sort_for_hero(Hero hero_arr[], int n) {
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = 0; j < n - i - 1; j++)
		{
			if (hero_arr[j].age > hero_arr[j + 1].age) {
				Hero tmp = hero_arr[j];
				hero_arr[j] = hero_arr[j + 1];
				hero_arr[j + 1] = tmp;
			}
		}
	}
}


void print_hero(Hero hero_arr[], int n) {
	cout << "-------排序后的结果-------" << endl;
	for (int i = 0; i < n; i++)
	{
		cout << "姓名: " << hero_arr[i].name << "\t年龄: "
			<< hero_arr[i].age << "\t性别: " << 
			hero_arr[i].gender << endl;
	}
}


int main() {
	
	// 2. 创建数组存放5名英雄
	Hero hero_arr[] = {
		{"盖伦", 23, "男"},
		{"卡特", 22, "女"},
		{"赵信", 30, "男"},
		{"嘉文四世", 25, "男"},
		{"瑞文", 24, "女"},
		{"亚索", 20, "男"},
		{"卡莎", 26, "女"},
	};

	int n = sizeof(hero_arr) / sizeof(hero_arr[0]);

	// 3. 对数组进行排序,按照年龄进行升序排序
	bubble_sort_for_hero(hero_arr, n);


	// 4. 将排序后的结果打印输出
	print_hero(hero_arr, n);

	/*
		-------排序后的结果-------
		姓名: 亚索      年龄: 20        性别: 男
		姓名: 卡特      年龄: 22        性别: 女
		姓名: 盖伦      年龄: 23        性别: 男
		姓名: 瑞文      年龄: 24        性别: 女
		姓名: 嘉文四世  年龄: 25        性别: 男
		姓名: 卡莎      年龄: 26        性别: 女
		姓名: 赵信      年龄: 30        性别: 男
	*/

	return 0;
}

9. 通讯录管理系统

9.1 系统需求

通讯录是一个可以记录亲人、好友信息的工具。本教程主要利用C++来实现一个通讯录管理系统。系统中需要实现的功能如下:

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

9.2 创建项目

9.3 菜单功能

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

菜单界面效果如下图:

在这里插入图片描述

步骤:

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

代码:

// 菜单界面
void show_menu() {
	cout << "*****************************" << endl;
	cout << "*****\t1. 添加联系人\t*****" << endl;
	cout << "*****\t2. 显示联系人\t*****" << endl;
	cout << "*****\t3. 删除联系人\t*****" << endl;
	cout << "*****\t4. 查找联系人\t*****" << endl;
	cout << "*****\t5. 修改联系人\t*****" << endl;
	cout << "*****\t6. 清空联系人\t*****" << endl;
	cout << "*****\t0. 退出通讯录\t*****" << endl;
	cout << "*****************************" << endl;
}

9.4 退出功能

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

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

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

代码:

int main() {

	int select = 0;  // 用户选择输入的变量

	while (true)  // 大循环
	{
		show_menu();

		cout << "请选择功能: ";
		cin >> select;

		// 根据输入做出判断
		switch (select)
		{
		case 1:  // 1. 添加联系人
			break;
		case 2:  // 2. 显示联系人
			break;
		case 3:  // 3. 删除联系人
			break;
		case 4:  // 4. 查找联系人
			break;
		case 5:  // 5. 修改联系人
			break;
		case 6:  // 6. 清空联系人
			break;
		case 0:  // 0. 退出通讯录
			cout << "欢迎下次使用!" << endl;
			system("pause");  // 请按任意键继续
			return 0;  // main函数的return -> 退出main函数
			break;
		default:
			cout << "您的输入无效!" << endl;
			break;
		}
	}

	system("pause");
	return 0;  // 返回正常退出值
}

9.5 添加联系人

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

添加联系人实现步骤:

  1. 设计联系人结构体
  2. 设计通讯录结构体
  3. main函数中创建通讯录
  4. 封装添加联系人函数
  5. 测试添加联系人功能

9.5.1 设计联系人结构体

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

设计如下:

// 设计联系人结构体
struct Person
{
	string p_name;
	int p_gender;  // 1: 男; 2: 女
	int p_age;
	string p_phone;
	string p_address;
};

9.5.2. 设计通讯录结构体

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

设计如下:

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

	// 初始化通讯录中当前人员个数(最后一个联系人的索引)
	int p_last_idx;
};

9.5.3. main函数中创建通讯录

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

int main() {

	// 创建通讯录结构体变量
	AddressBooks abs;

	// 初始化通讯录中当前人员个数(最后一个联系人的索引)
	abs.p_last_idx = 0;

}

9.5.4. 封装添加联系人函数

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

添加联系人代码:

// 1. 添加联系人
void add_person(AddressBooks* abs) {
	// 判断通讯录是否满了
	if (abs->p_last_idx == MAX) {
		cout << "通讯录已满,无法添加!" << endl;
		return;  // 这里的return表明当前函数结束
	}
	else {
		// 添加具体联系人
		// 姓名
		string name;
		cout << "请输入姓名: ";
		cin >> name;
		abs->person_arr[abs->p_last_idx].p_name = name;

		// 性别
		int gender = 0;
		cout << "请输入性别(1为男性, 2为女性): ";

		while (true)
		{
			cin >> gender;

			if (gender == 1 || gender == 2) {
				abs->person_arr[abs->p_last_idx].p_gender = gender;
				break;  // 停止无限循环
			}
			else {
				cout << "您的输入有误,请重新输入: ";
			}
		}

		// 年龄
		int age = 0;
		cout << "请输入年龄: ";
		while (true)
		{
			cin >> age;
			if (age <= 0 || age >= 150)
			{
				cout << "您的输入有误,请重新输入: ";
			}
			else
			{
				abs->person_arr[abs->p_last_idx].p_age = age;
				break;  // 停止无限循环
			}
		}

		// 电话
		cout << "请输入联系电话(移动号码或固定号码): ";
		string phone;
		while (true)
		{
			cin >> phone;
			if (phone.length() != 11 && phone.length() != 7)
			{
				cout << "您输入的联系方式有误,请重新输入: ";
			}

			else
			{
				abs->person_arr[abs->p_last_idx].p_phone = phone;
				break;  // 停止无限循环
			}
		}

		// 住址
		cout << "请输入家庭住址: ";
		string address;
		cin >> address;
		abs->person_arr[abs->p_last_idx].p_address = address;

		// 添加成功后,让最后一个联系的索引+1
		cout << "联系人[" << name << "]添加成功!" << endl;
		abs->p_last_idx += 1;

		// 添加清屏操作
		system("pause");  // 请按任意键继续
		system("cls");  // 清屏操作: cls = clear screen
	}
}

9.5.5. 测试添加联系人功能

测试效果如图:

在这里插入图片描述

9.6 显示联系人

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

显示联系人实现步骤:

  1. 封装显示联系人函数
  2. 测试显示联系人功能

9.6.1 封装显示联系人函数

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

显示联系人代码:

// 2. 显示所有联系人
void show_person(AddressBooks* abs) {
	// 判断通讯录中人数是否为0
	if (abs->p_last_idx == 0)
	{
		cout << "通讯录为空,请添加联系人" << endl;
	}
	else
	{
		for (int i = 0; i < abs->p_last_idx; i++)
		{	
			cout << "[" << i + 1 << "]: " << abs->person_arr[i].p_name << endl;
			cout << "\t性别: " << (abs->person_arr[i].p_gender == 1 ? "男" : "女") << endl;
			cout << "\t年龄: " << abs->person_arr[i].p_age << endl;
			cout << "\t电话: " << abs->person_arr[i].p_phone << endl;
			cout << "\t住址: " << abs->person_arr[i].p_address << endl;
			cout << "-------------------------------" << endl;
		}
	}
	system("pause");
	system("cls");
}

9.7 删除联系人

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

删除联系人实现步骤:

  1. 封装检测联系人是否存在
  2. 封装删除联系人函数
  3. 测试删除联系人功能

9.7.1 封装检测联系人是否存在

设计思路:删除联系人前,我们需要先判断用户输入的联系人是否存在。

  • 如果存在删除
  • 不存在提示用户没有要删除的联系人

因此我们可以把检测联系人是否存在封装成一个函数中:

  • 如果存在,返回联系人在通讯录中的位置
  • 不存在返回-1。
// 检测联系人是否存在,如果存在则返回对应索引,不存在返回-1
int is_exist(AddressBooks* abs, string name) {
	/*
	* abs: 通讯录地址
	* name: 要删除人的名字
	*/
	for (int i = 0; i < abs->p_last_idx; i++)
	{
		if (abs->person_arr[i].p_name == name) // 找到了,返回对应的idx
		{
			return i;
		}
	}

	// 走完了for循环还是没有找到
	return -1;
}

9.7.2. 封装删除联系人函数

根据用户输入的联系人判断该通讯录中是否有此人查找到进行删除,并提示删除成功
查不到提示查无此人。

// 删除指定的联系人
void del_person(AddressBooks* abs) {
	cout << "请输入您要删除的联系人: ";
	string delete_name;
	cin >> delete_name;
	
	int res = is_exist(abs, delete_name);

	if (res != -1)
	{
		for (int i = res; i < abs->p_last_idx; i++)
		{
			// 数据前移
			abs->person_arr[i] = abs->person_arr[i + 1];
		}
		// 更新通讯录最后一个人员的索引
		abs->p_last_idx -= 1;
		cout << "删除成功..." << endl;
	}
	else
	{
		cout << "查无此人!" << endl;
	}

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

9.8 查找联系人

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

查找联系人实现步骤:

  1. 封装查找联系人函数
  2. 测试查找指定联系人

9.8.1 封装查找联系人函数

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

查找联系人代码:

// 4. 查找指定联系人信息
void find_person(AddressBooks* abs) {
	cout << "请输入您要查找的联系人: ";
	string find_name;
	cin >> find_name;

	// 判断指定联系人是否存在
	int res = is_exist(abs, find_name);

	if (res != -1)
	{
		cout << "[" << res + 1 << "]: " << abs->person_arr[res].p_name << endl;
		cout << "\t性别: " << (abs->person_arr[res].p_gender == 1 ? "男" : "女") << endl;
		cout << "\t年龄: " << abs->person_arr[res].p_age << endl;
		cout << "\t电话: " << abs->person_arr[res].p_phone << endl;
		cout << "\t住址: " << abs->person_arr[res].p_address << endl;
		cout << "-------------------------------" << endl;
	}
	else
	{
		cout << "查无此人!" << endl;
	}

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

9.9 修改联系人

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

修改联系人实现步骤

  1. 封装修改联系人函数
  2. 测试修改联系人功能

9.9.1 封装修改联系人函数

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

修改联系人代码:

// 5. 修改指定联系人信息
void modify_person(AddressBooks* abs) {
	cout << "请输入您要修改的联系人: ";
	string name;
	cin >> name;

	int res = is_exist(abs, name);

	if (res != -1)
	{
		// 姓名
		string name;
		cout << "请输入姓名(不修改输入0): ";
		cin >> name;
		if (name != "0")
		{
			abs->person_arr[res].p_name = name;
		}
		
		// 性别
		string gender;
		while (true)
		{
			cout << "请输入性别(不修改输入0): ";
			cin >> gender;
			if (gender != "0")
			{
				if (gender == "1")
				{
					abs->person_arr[res].p_gender = 1;
					break;
				}
				else if (gender == "2")
				{
					abs->person_arr[res].p_gender = 2;
					break;
				}
				else
				{
					cout << "您输入的性别有误(请输入1或2)" << endl;
				}
			}
			else
			{
				break;
			}
		}

		// 年龄
		int age;
		while (true)
		{
			cout << "请输入年龄(不修改输入0): ";
			cin >> age;
			if (age == 0)
			{
				break;
			}
			else
			{
				if (age <= 0 || age >= 150)
				{
					cout << "您的输入有误,请重新输入" << endl;
				}
				else
				{
					abs->person_arr[res].p_age = age;
					break;
				}
			}
		}

		// 电话
		cout << "请输入电话(不修改输入0): ";
		string phone;
		while (true)
		{	
			cin >> phone;

			if (phone == "0")
			{
				break;
			}

			if (phone.length() != 11 && phone.length() != 7)
			{
				cout << "您输入的联系方式有误,请重新输入(移动号码或固定号码): " << endl;
			}

			else
			{
				abs->person_arr[res].p_phone = phone;
				break; 
			}
		}

		// 地址
		cout << "请输入地址(不修改输入0): ";
		string address;
		cin >> address;
		if (address != "0")
		{
			abs->person_arr[res].p_address = address;
		}

		cout << "修改成功!" << endl;

	}
	else
	{
		cout << "查无此人!" << endl;
	}

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

9.10 清空联系人

功能描述:清空通讯录中所有信息清空联系人

实现步骤:

  1. 封装清空联系人函数
  2. 测试清空联系人

9.10.1 封装清空联系人函数

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

清空联系人代码:

// 6. 清空所有联系人
void clear_all_persons(AddressBooks* abs) {
	abs->p_last_idx = 0;
	cout << "通讯录已清空..." << endl;

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

9.11 通讯录系统全部代码

#include <iostream>  // 屏幕中输入输出都需要这个头文件
using namespace std;  // 使用标准的命名空间
#include <string>
#define MAX 1000  // 定义一个宏常量


// 设计联系人结构体
struct Person
{
	string p_name;
	int p_gender;  // 1: 男; 2: 女
	int p_age;
	string p_phone;
	string p_address;
};


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

	// 初始化通讯录中当前人员个数(最后一个联系人的索引)
	int p_last_idx;
};


// 1. 添加联系人
void add_person(AddressBooks* abs) {
	// 判断通讯录是否满了
	if (abs->p_last_idx == MAX) {
		cout << "通讯录已满,无法添加!" << endl;
		return;  // 这里的return表明当前函数结束
	}
	else {
		// 添加具体联系人
		// 姓名
		string name;
		cout << "请输入姓名: ";
		cin >> name;
		abs->person_arr[abs->p_last_idx].p_name = name;

		// 性别
		int gender = 0;
		cout << "请输入性别(1为男性, 2为女性): ";

		while (true)
		{
			cin >> gender;

			if (gender == 1 || gender == 2) {
				abs->person_arr[abs->p_last_idx].p_gender = gender;
				break;  // 停止无限循环
			}
			else {
				cout << "您的输入有误,请重新输入: ";
			}
		}

		// 年龄
		int age = 0;
		cout << "请输入年龄: ";
		while (true)
		{
			cin >> age;
			if (age <= 0 || age >= 150)
			{
				cout << "您的输入有误,请重新输入: ";
			}
			else
			{
				abs->person_arr[abs->p_last_idx].p_age = age;
				break;  // 停止无限循环
			}
		}

		// 电话
		cout << "请输入联系电话(移动号码或固定号码): ";
		string phone;
		while (true)
		{
			cin >> phone;
			if (phone.length() != 11 && phone.length() != 7)
			{
				cout << "您输入的联系方式有误,请重新输入: ";
			}

			else
			{
				abs->person_arr[abs->p_last_idx].p_phone = phone;
				break;  // 停止无限循环
			}
		}

		// 住址
		cout << "请输入家庭住址: ";
		string address;
		cin >> address;
		abs->person_arr[abs->p_last_idx].p_address = address;

		// 添加成功后,让最后一个联系的索引+1
		cout << "联系人[" << name << "]添加成功!" << endl;
		abs->p_last_idx += 1;

		// 添加清屏操作
		system("pause");  // 请按任意键继续
		system("cls");  // 清屏操作: cls = clear screen
	}
}


// 2. 显示所有联系人
void show_person(AddressBooks* abs) {
	// 判断通讯录中人数是否为0
	if (abs->p_last_idx == 0)
	{
		cout << "通讯录为空,请添加联系人" << endl;
	}
	else
	{
		for (int i = 0; i < abs->p_last_idx; i++)
		{	
			cout << "[" << i + 1 << "]: " << abs->person_arr[i].p_name << endl;
			cout << "\t性别: " << (abs->person_arr[i].p_gender == 1 ? "男" : "女") << endl;
			cout << "\t年龄: " << abs->person_arr[i].p_age << endl;
			cout << "\t电话: " << abs->person_arr[i].p_phone << endl;
			cout << "\t住址: " << abs->person_arr[i].p_address << endl;
			cout << "-------------------------------" << endl;
		}
	}
	system("pause");
	system("cls");
}


// 检测联系人是否存在,如果存在则返回对应索引,不存在返回-1
int is_exist(AddressBooks* abs, string name) {
	/*
	* abs: 通讯录地址
	* name: 要删除人的名字
	*/
	for (int i = 0; i < abs->p_last_idx; i++)
	{
		if (abs->person_arr[i].p_name == name) // 找到了,返回对应的idx
		{
			return i;
		}
	}

	// 走完了for循环还是没有找到
	return -1;
}


// 删除指定的联系人
void del_person(AddressBooks* abs) {
	cout << "请输入您要删除的联系人: ";
	string delete_name;
	cin >> delete_name;
	
	int res = is_exist(abs, delete_name);

	if (res != -1)
	{
		for (int i = res; i < abs->p_last_idx; i++)
		{
			// 数据前移
			abs->person_arr[i] = abs->person_arr[i + 1];
		}
		// 更新通讯录最后一个人员的索引
		abs->p_last_idx -= 1;
		cout << "删除成功..." << endl;
	}
	else
	{
		cout << "查无此人!" << endl;
	}

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


// 4. 查找指定联系人信息
void find_person(AddressBooks* abs) {
	cout << "请输入您要查找的联系人: ";
	string find_name;
	cin >> find_name;

	// 判断指定联系人是否存在
	int res = is_exist(abs, find_name);

	if (res != -1)
	{
		cout << "[" << res + 1 << "]: " << abs->person_arr[res].p_name << endl;
		cout << "\t性别: " << (abs->person_arr[res].p_gender == 1 ? "男" : "女") << endl;
		cout << "\t年龄: " << abs->person_arr[res].p_age << endl;
		cout << "\t电话: " << abs->person_arr[res].p_phone << endl;
		cout << "\t住址: " << abs->person_arr[res].p_address << endl;
		cout << "-------------------------------" << endl;
	}
	else
	{
		cout << "查无此人!" << endl;
	}

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


// 5. 修改指定联系人信息
void modify_person(AddressBooks* abs) {
	cout << "请输入您要修改的联系人: ";
	string name;
	cin >> name;

	int res = is_exist(abs, name);

	if (res != -1)
	{
		// 姓名
		string name;
		cout << "请输入姓名(不修改输入0): ";
		cin >> name;
		if (name != "0")
		{
			abs->person_arr[res].p_name = name;
		}
		
		// 性别
		string gender;
		while (true)
		{
			cout << "请输入性别(不修改输入0): ";
			cin >> gender;
			if (gender != "0")
			{
				if (gender == "1")
				{
					abs->person_arr[res].p_gender = 1;
					break;
				}
				else if (gender == "2")
				{
					abs->person_arr[res].p_gender = 2;
					break;
				}
				else
				{
					cout << "您输入的性别有误(请输入1或2)" << endl;
				}
			}
			else
			{
				break;
			}
		}

		// 年龄
		int age;
		while (true)
		{
			cout << "请输入年龄(不修改输入0): ";
			cin >> age;
			if (age == 0)
			{
				break;
			}
			else
			{
				if (age <= 0 || age >= 150)
				{
					cout << "您的输入有误,请重新输入" << endl;
				}
				else
				{
					abs->person_arr[res].p_age = age;
					break;
				}
			}
		}

		// 电话
		cout << "请输入电话(不修改输入0): ";
		string phone;
		while (true)
		{	
			cin >> phone;

			if (phone == "0")
			{
				break;
			}

			if (phone.length() != 11 && phone.length() != 7)
			{
				cout << "您输入的联系方式有误,请重新输入(移动号码或固定号码): " << endl;
			}

			else
			{
				abs->person_arr[res].p_phone = phone;
				break; 
			}
		}

		// 地址
		cout << "请输入地址(不修改输入0): ";
		string address;
		cin >> address;
		if (address != "0")
		{
			abs->person_arr[res].p_address = address;
		}

		cout << "修改成功!" << endl;

	}
	else
	{
		cout << "查无此人!" << endl;
	}

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


// 6. 清空联系人
void clear_all_persons(AddressBooks* abs) {
	
	cout << "[二次确认]确定要清空通讯录吗?(Y/N): ";
	string confirmation;

	while (true)
	{
		cin >> confirmation;

		if (confirmation == "Y")
		{
			// 将最后一个联系人的索引置为0,做逻辑清空操作
			abs->p_last_idx = 0;
			cout << "通讯录已清空..." << endl;
			break;
		}
		else if (confirmation == "N")
		{
			cout << "操作取消" << endl;
			break;
		}
		else
		{
			cout << "请输入Y/N: ";
		}
	}



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


// 菜单界面
void show_menu() {
	cout << "*****************************" << endl;
	cout << "*****\t1. 添加联系人\t*****" << endl;
	cout << "*****\t2. 显示联系人\t*****" << endl;
	cout << "*****\t3. 删除联系人\t*****" << endl;
	cout << "*****\t4. 查找联系人\t*****" << endl;
	cout << "*****\t5. 修改联系人\t*****" << endl;
	cout << "*****\t6. 清空联系人\t*****" << endl;
	cout << "*****\t0. 退出通讯录\t*****" << endl;
	cout << "*****************************" << endl;
}


int main() {

	// 创建通讯录结构体变量
	AddressBooks abs;

	// 初始化通讯录中当前人员个数(最后一个联系人的索引)
	abs.p_last_idx = 0;

	int select = 0;  // 用户选择输入的变量

	while (true)  // 大循环
	{
		show_menu();

		cout << "请选择功能: ";
		cin >> select;

		// 根据输入做出判断
		switch (select)
		{
		case 1:  // 1. 添加联系人
			add_person(&abs);  // 用地址传递,以修改实参
			break;

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

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

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

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

		case 6:  // 6. 清空联系人
			clear_all_persons(&abs);
			break;

		case 0:  // 0. 退出通讯录
			cout << "欢迎下次使用!" << endl;
			system("pause");  // 请按任意键继续
			return 0;  // main函数的return -> 退出main函数
			break;

		default:
			cout << "您的输入无效!" << endl;
			break;
		}
	}


	system("pause");
	return 0;  // 返回正常退出值
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值