课程链接: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的注释有两种格式:
- 单行注释:
// 描述信息
- 通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明
- 多行注释:
/* 描述信息 */
- 通常放在一段代码的上方,对该段代码做整体说明
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++定义常量的两种方式:
- #define 宏常量:
#define 常量名 常量值
- 通常在文件上方定义,表示一个常量
- 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++关键字如下:
关键字 | ||||
---|---|---|---|---|
asm | do | if | return | typedef |
auto | double | inline | short | typeid |
bool | dynamic_cast | int | signed | typename |
break | else | long | sizeof | union |
case | enum | mutable | static | unsigned |
catch | explicit | namespace | static_cast | using |
char | export | new | struct | virtual |
class | extern | operator | switch | void |
const | false | private | template | volatile |
const_cast | float | protected | this | wchar_t |
continue | for | public | throw | while |
default | friend | register | true | |
delete | goto | reinterpret_cast | try |
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) (−215∼215−1) ⇔ \Leftrightarrow ⇔ ( − 32 , 768 ∼ 32 , 767 ) (-32,768 \sim 32,767) (−32,768∼32,767) |
int (整型) | 4字节 | ( − 2 31 ∼ 2 31 − 1 ) (-2^{31} \sim 2^{31} - 1) (−231∼231−1) ⇔ \Leftrightarrow ⇔ ( − 2 , 147 , 483 , 648 ∼ 2 , 147 , 483 , 647 ) (-2,147,483,648 \sim 2,147,483,647) (−2,147,483,648∼2,147,483,647) |
long (长整型) | Windows为4字节,Linux为4字节(32位),8字节(64位) | ( − 2 31 ∼ 2 31 − 1 ) (-2^{31} \sim 2^{31} - 1) (−231∼231−1) ⇔ \Leftrightarrow ⇔ ( − 2 , 147 , 483 , 648 ∼ 2 , 147 , 483 , 647 ) (-2,147,483,648 \sim 2,147,483,647) (−2,147,483,648∼2,147,483,647) |
long long (长长整型) | 8字节 | ( − 2 63 ∼ 2 63 − 1 ) (-2^{63} \sim 2^{63} - 1) (−263∼263−1) ⇔ \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,808∼9,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 实型(浮点型)
作用:用于表示小数。
浮点型变量分为两种:
- 单精度:
float
- 双精度:
double
两者的区别在于表示的有效数字范围不同。
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 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';
注意:
- 在显示字符型变量时,用单引号将字符括起来,不要使用双引号
- 单引号内只能有一个字符,不可以是字符串
- 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元
- C和CPP中字符型变量只占用1个字节
字节字节,就是一个英文单词所占用的大小,所以对于
char
,在空间中就是占一个字节。
需要记忆的ASCII码值:
a
: 97A
: 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值(十进制) | 控制字符 | 解释 |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | NUT (null) | 空字符 | 32 | (space) | 空格 | 64 | @ | 电子邮件符号 | 96 | ` | 开单引号 |
1 | SOH (start of headline) | 标题开始 | 33 | ! | 叹号 | 65 | A | 大写字母A | 97 | a | 小写字母 |
2 | STX (start of text) | 正文开始 | 34 | " | 双引号 | 66 | B | 大写字母B | 98 | b | 小写字母b |
3 | ETX (end of text) | 正文结束 | 35 | # | 井号 | 67 | C | 大写字母C | 99 | c | 小写字母c |
4 | EOT (end of transmission) | 传输结束 | 36 | $ | 美元符号 | 68 | D | 大写字母D | 100 | d | 小写字母d |
5 | ENQ (enquiry) | 请求 | 37 | % | 百分号 | 69 | E | 大写字母E | 101 | e | 小写字母e |
6 | ACK (acknowledge) | 收到通知 | 38 | & | 和号 | 70 | F | 大写字母F | 102 | f | 小写字母f |
7 | BEL (bell) | 响铃 | 39 | ’ | 闭单引号 | 71 | G | 大写字母G | 103 | g | 小写字母g |
8 | BS (backspace) | 退格 | 40 | ( | 开括号 | 72 | H | 大写字母H | 104 | h | 小写字母h |
9 | HT (horizontal tab) | 水平制表符 | 41 | ) | 闭括号 | 73 | I | 大写字母I | 105 | i | 小写字母i |
10 | LF (NL line feed, new line) | 换行键 | 42 | * | 星号 | 74 | J | 大写字母J | 106 | j | 小写字母j |
11 | VT (vertical tab) | 垂直制表符 | 43 | + | 加号 | 75 | K | 大写字母K | 107 | k | 小写字母k |
12 | FF (NP form feed, new page) | 换页键 | 44 | , | 逗号 | 76 | L | 大写字母L | 108 | l | 小写字母l |
13 | CR (carriage return) | 回车键 | 45 | - | 减号/破折号 | 77 | M | 大写字母M | 109 | m | 小写字母m |
14 | SO (shift out) | 不用切换 | 46 | . | 句号/点 | 78 | N | 大写字母N | 110 | n | 小写字母n |
15 | SI (shift in) | 启用切换 | 47 | / | 斜杠 | 79 | O | 大写字母O | 111 | o | 小写字母o |
16 | DLE (data link escape) | 数据链路转义 | 48 | 0 | 数字0 | 80 | P | 大写字母P | 112 | p | 小写字母p |
17 | DC1 (device control 1) | 设备控制1 | 49 | 1 | 数字1 | 81 | Q | 大写字母Q | 113 | q | 小写字母q |
18 | DC2 (device control 2) | 设备控制2 | 50 | 2 | 数字2 | 82 | R | 大写字母R | 114 | r | 小写字母r |
19 | DC3 (device control 3) | 设备控制3 | 51 | 3 | 数字3 | 83 | S | 大写字母S | 115 | s | 小写字母s |
20 | DC4 (device control 4) | 设备控制4 | 52 | 4 | 数字4 | 84 | T | 大写字母T | 116 | t | 小写字母t |
21 | NAK (negative acknowledge) | 拒绝接收 | 53 | 5 | 数字5 | 85 | U | 大写字母U | 117 | u | 小写字母u |
22 | SYN (synchronous idle) | 同步空闲 | 54 | 6 | 数字6 | 86 | V | 大写字母V | 118 | v | 小写字母v |
23 | ETB (end of trans. block) | 结束传输块 | 55 | 7 | 数字7 | 87 | W | 大写字母W | 119 | w | 小写字母w |
24 | CAN (cancel) | 取消 | 56 | 8 | 数字8 | 88 | X | 大写字母X | 120 | x | 小写字母x |
25 | EM (end of medium) | 媒介结束 | 57 | 9 | 数字9 | 89 | Y | 大写字母Y | 121 | y | 小写字母y |
26 | SUB (substitute) | 代替 | 58 | : | 冒号 | 90 | Z | 大写字母Z | 122 | z | 小写字母z |
27 | ESC (escape) | 换码(溢出) | 59 | ; | 分号 | 91 | [ | 开方括号 | 123 | { | 开花括号 |
28 | FS (file separator) | 文件分隔符 | 60 | < | 小于 | 92 | \ | 反斜杠 | 124 | ` | ` |
29 | GS (group separator) | 分组符 | 61 | = | 等号 | 93 | ] | 闭方括号 | 125 | } | 闭花括号 |
30 | RS (record separator) | 记录分隔符 | 62 | > | 大于 | 94 | ^ | 脱字符 | 126 | ~ | 波浪号 |
31 | US (unit separator) | 单元分隔符 | 63 | ? | 问号 | 95 | _ | 下划线 | 127 | DEL (delete) | 删除 |
ASCII码大致由以下两部分组成:
- ASCII非打印控制字符:ASCII表上的数字
0 ~ 31
分配给了控制字符,用于控制像打印机等一些外围设备。 - 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 | |
\ddd | 8进制转义字符, d范围0~7 | 3位8进制 | |
\xhh | 16进制转义字符, h范围0~9, a~f, A~F | 3位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 字符串型
作用:用于表示一串字符。
两种风格:
- C语言风格字符串:
char 变量名[] = "字符串值";
->char str1[] = "Hello World";
- 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 算术运算符
作用:用于处理四则运算。
算术运算符包含以下符号:
运算符 | 术语 | 示例 | 结果 | 注意 |
---|---|---|---|---|
+ | 正号 | +3 | 3 | |
- | 负号 | -3 | -3 | |
+ | 加 | 10+5 | 15 | |
- | 减 | 10-5 | 5 | |
* | 乘 | 10*5 | 50 | |
/ | 除 | 10/5 | 2 | 除数不能是0 |
% | 取模(取余数) | 10%3 | 1 | 必须是两个整数, 除数不能是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==3 | 0 |
!= | 不等于 | 4!=3 | 1 |
< | 小于 | 4<3 | 0 |
> | 大于 | 4>3 | 1 |
<= | 小于等于 | 4<=3 | 0 |
>= | 大于等于 | 4>=3 | 1 |
#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语句的三种形式:
- 单行格式if语句:
if(条件){满足条件时执行的语句}
- 多行格式if语句:
if(条件){满足条件时执行的语句}else{不满足条件时执行的语句}
- 多条件的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;
}
注意:
switch
语句中表达式类型只能是整型或字符型case
里如果没有break
,那么程序会一直向下执行- 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...while
与while
循环的区别在于: 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),就是一个集合,里面存放了相同类型的数据元素,数组有两个特点:
- 数组中的每个数据元素都是相同的数据类型
- 数组是由连续的内存位置组成的
在Python中,list是列表不是数组;Python的list里面可以存放不一样的数据类型,但数组中一定要存放相同的数据类型
5.2 一维数组
5.2.1 一维数组的定义方式
一维数组定义的三种方式:
数据类型 数组名 [数组长度];
数据类型 数组名[数组长度] = {值1, 值2, ...};
(如果在初始化数据时没有全部填写,则会用0来填补)数据类型 数组名[] = {值1, 值2, ...];
C++中没有built-in的方法求数组的长度
总结:
- 数组名的命名规范与变量名命名规范一致,不要和变量重名
- 数组的下标从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 一维数组数组名
一维数组名称的用途:
- 可以统计整个数组在内存中的长度:
sizeof(数组名)
- 可以获取数组在内存中的首地址:
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,直到不需要比较
示例: 将数组{ 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},{数据3,数据4 }};
数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据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 二维数组数组名
- 查看二维数组所占内存空间
- 获取二维数组首地址
#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 二维数组应用案例
考试成绩统计:
案例描述: 有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩。
姓名 | 语文 | 数学 | 英语 |
---|---|---|---|
张三 | 100 | 100 | 100 |
李四 | 90 | 50 | 100 |
王五 | 60 | 70 | 80 |
#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个步骤:
- 返回值类型
- 函数名
- 参数表列
- 函数体语句
- 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种:
- 无参无返
- 有参无返
- 无参有返
- 有参有返
#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个步骤:
- 创建后缀名为
.h
的头文件 ->swap.h
- 创建后缀名为
.cpp
的源文件 ->swap.cpp
- 在头文件中写函数的声明
- 在源文件中写函数的定义
示例:
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修饰指针有三种情况:
const
修饰指针 -> 常量指针 ->const int* p = &a;
const
修饰常量 -> 指针常量 ->int* const p = &a;
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 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
struct 结构体名 变量名;
struct 结构体名 变量名 = { 成员1值, 成员2值, ... };
- 定义结构体时顺便创建变量
注意:
- 在日常使用时,方法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 结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
- 值传递: 改变形参 不会 改变实参
- 地址传递:改变形参 会 改变实参
总结:如果不想修改主函数中的数据,用值传递,反之用地址传递。
示例:
#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++来实现一个通讯录管理系统。系统中需要实现的功能如下:
- 添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000人
- 显示联系人:显示通讯录中所有联系人信息
- 删除联系人:按照姓名进行删除指定联系人
- 查找联系人:按照姓名查看指定联系人信息
- 修改联系人:按照姓名重新修改指定联系人
- 清空联系人:清空通讯录中所有信息
- 退出通讯录:退出当前使用的通讯录
9.2 创建项目
9.3 菜单功能
功能描述:用户选择功能的界面
菜单界面效果如下图:
步骤:
- 封装函数显示该界面如
void showMenu()
- 在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人,联系人信息包括(姓名、性别、年龄、联系电话、家庭住址)
添加联系人实现步骤:
- 设计联系人结构体
- 设计通讯录结构体
main
函数中创建通讯录- 封装添加联系人函数
- 测试添加联系人功能
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 显示联系人
功能描述:显示通讯录中已有的联系人信息
显示联系人实现步骤:
- 封装显示联系人函数
- 测试显示联系人功能
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 删除联系人
功能描述:按照姓名进行删除指定联系人
删除联系人实现步骤:
- 封装检测联系人是否存在
- 封装删除联系人函数
- 测试删除联系人功能
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 查找联系人
功能描述:按照姓名查看指定联系人信息
查找联系人实现步骤:
- 封装查找联系人函数
- 测试查找指定联系人
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 修改联系人
功能描述:按照姓名重新修改指定联系人
修改联系人实现步骤
- 封装修改联系人函数
- 测试修改联系人功能
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 清空联系人
功能描述:清空通讯录中所有信息清空联系人
实现步骤:
- 封装清空联系人函数
- 测试清空联系人
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; // 返回正常退出值
}