[学习笔记] 1. C++ / CPP基础入门
创始人
2025-05-28 14:01:45

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

在这里插入图片描述

文章目录

  • 1. 初识C++
    • 1.1 第一个C++程序
    • 1.2 注释
    • 1.3 变量
    • 1.4 常量
    • 1.5 关键字
    • 1.6 标识符命名规则
  • 2. 数据类型
    • 2.1 整型
    • 2.2 sizeof关键字
    • 2.3 实型(浮点型)
    • 2.4 字符型
      • ASCII码表格
    • 2.5 转义字符
    • 2.6 字符串型
    • 2.7 布尔类型
    • 2.8 数据的输入
  • 3. 运算符
    • 3.1 算术运算符
    • 3.2 赋值运算符
    • 3.3 比较运算符
    • 3.4 逻辑运算符
  • 4. 程序流程结构
    • 4.1 选择结构
      • 4.1.1 if语句
      • 4.1.2 三目运算符
      • 4.1.3 switch语句
    • 4.2 循环结构
      • 4.2.1 while循环
      • 4.2.2 do...while循环语句
      • 4.2.3 for循环语句
    • 4.2.4 嵌套循环
    • 4.3 跳转语句
      • 4.3.1 break语句
      • 4.3.2 continue语句
      • 4.3.3 goto语句
  • 5. 数组 (array)
    • 5.1 概述
    • 5.2 一维数组
      • 5.2.1 一维数组的定义方式
    • 5.2.2 一维数组数组名
      • 5.2.3 冒泡排序
    • 5.3 二维数组
      • 5.3.1 二维数组定义方式
      • 5.3.2 二维数组数组名
      • 5.3.3 二维数组应用案例
  • 6. 函数
    • 6.1 概述
    • 6.2 函数的定义
    • 6.3 函数的调用
    • 6.4 值传递
    • 6.5 函数的常见样式
    • 6.6 函数的声明
    • 6.7 函数的分文件编写
      • 1. 创建后缀名为`.h`的头文件 -> `swap.h`
      • 2. 创建后缀名为`.cpp`的源文件 -> `swap.cpp`
      • 3. 在头文件中写函数的声明
      • 4. 在源文件中写函数的定义
      • main函数所在.cpp文件
  • 7. 指针 (pointer)
    • 7.1 指针的基本概念
    • 7.2 指针变量的定义和使用
    • 7.3 指针所占的内存空间
    • 7.4 空指针和野指针
    • 7.5 const修饰指针
      • 7.5.1 常量指针
      • 7.5.2 指针常量
      • 7.5.2 `const`即修饰指针,又修饰常量
    • 7.6 指针和数组
    • 7.7 指针和函数
    • 7.8 指针、数组、函数
  • 8. 结构体
    • 8.1 结构体基本概念
    • 8.2 结构体定义和使用
    • 8.3 结构体数组
    • 8.4 结构体指针
    • 8.5 结构体嵌套结构体
    • 8.6 结构体做函数参数
    • 8.7结构体中const使用场景
    • 8.8 结构体案例
      • 8.8.1 案例1
      • 8.8.2案例2
  • 9. 通讯录管理系统
    • 9.1 系统需求
    • 9.2 创建项目
    • 9.3 菜单功能
    • 9.4 退出功能
    • 9.5 添加联系人
      • 9.5.1 设计联系人结构体
      • 9.5.2. 设计通讯录结构体
      • 9.5.3. `main`函数中创建通讯录
      • 9.5.4. 封装添加联系人函数
      • 9.5.5. 测试添加联系人功能
    • 9.6 显示联系人
      • 9.6.1 封装显示联系人函数
    • 9.7 删除联系人
      • 9.7.1 封装检测联系人是否存在
      • 9.7.2. 封装删除联系人函数
    • 9.8 查找联系人
      • 9.8.1 封装查找联系人函数
    • 9.9 修改联系人
      • 9.9.1 封装修改联系人函数
    • 9.10 清空联系人
      • 9.10.1 封装清空联系人函数
    • 9.11 通讯录系统全部代码

1. 初识C++

1.1 第一个C++程序

#include 
using namespace std;int main() {cout << "Hello World" << endl;system("pause");return 0;
}

1.2 注释

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

CPP的注释有两种格式:

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

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

#include 
using namespace std;/*main是一个程序的入口,每个程序都必须有这么一个函数,有且仅有一个
*/
int main() {// 在屏幕中输出Hello Worldcout << "Hello World" << endl;system("pause");return 0;
}

1.3 变量

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

示例:

#include
using namespace std;int main() {// 变量创建的语法:数据类型 变量名 = 变量初始值int a = 10;cout << "a = " << a << endl; // a = 10system("pause");return 0;
}

1.4 常量

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

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

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

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

示例:

#include 
using namespace std;/*
常量的定义方式1. #define 宏常量 -> #define 常量名 常量值2. const修饰的变量 -> const 数据类型 常量名 = 常量值
*/#define Day 7int 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 
using namespace std;int main() {// 创建变量时变量名使用关键字int true = 10;  // 不能使用关键字作为变量名,会报错!system("pause");return 0;
}

在这里插入图片描述

1.6 标识符命名规则

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

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

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

#include 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;  // 100cout << AAA << endl;  // 300system("pause");return 0;
}

2. 数据类型

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

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

2.1 整型

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

数据类型占用空间取值范围
short(短整型)2字节(−215∼215−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字节(−231∼231−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位)(−231∼231−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字节(−263∼263−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
using namespace std;/*整型:1. 短整型 short2. 整型 int3. 长整型 long4. 长长整型 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: 10cout << "num2: " << num2 << endl;  // num2: 10cout << "num3: " << num3 << endl;  // num3: 10cout << "num4: " << num4 << endl;  // num4: 10cout << "-------------------" << 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: -9223372036854775808system("pause");return 0;
}

2.2 sizeof关键字

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

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

示例:

#include
using namespace std;int main() {/*使用sizeof求出数据类型/变量所占的内存大小*/short num1 = 10;cout << "short占用的内存空间为: " << sizeof(short) << endl;  // short占用的内存空间为: 2cout << "num1变量对应的数据类型所占用的内存空间为: " << sizeof(num1) << endl;  // num1变量对应的数据类型所占用的内存空间为: 2int num2 = 10;cout << "int占用的内存空间为: " << sizeof(int) << endl;  // int占用的内存空间为: 4long num3 = 10;cout << "long占用的内存空间为: " << sizeof(long) << endl;  // long占用的内存空间为: 4long long num4 = 10;cout << "long long占用的内存空间为: " << sizeof(long long) << endl;  // long long占用的内存空间为: 8system("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 
using namespace std;int main() {// 1. 单精度 float// 2. 双精度 doublefloat f1 = 3.1415126f;  // 一般会在后面写一个f// endl = end linecout << "f1: " << f1 << endl;  // f1: 3.14151double 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^2double d2 = 3e-2;  // 3 * 10^-2cout << "f2: " << f2 << endl;  // f2: 300cout << "d2: " << d2 << endl;  // d2: 0.03system("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
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;  // 97cout << (int)'A' << endl;  // 65system("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
using namespace std;int main() {// 1. 换行符 \ncout << "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 worldab      hello worldabc     hello worldabcd    hello world请按任意键继续. . .*/// 4. 回车 \rcout << "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

示例:

#include 
using namespace std;
#include 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 , 
using namespace std;int main() {// 1. 创建bool数据类型bool flag_1 = true;bool flag_2 = false;cout << flag_1 << endl;  // 1cout << flag_2 << endl;  // 0// 2. 查看bool数据类型占用空间大小cout << "bool数据类型占 " << sizeof(bool) << " 字节" << endl;  // bool数据类型占 1 字节system("pause");return 0;
}

2.8 数据的输入

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

关键字:cin

语法:cin >> 变量

示例:

#include
#include
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. boolbool 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赋值:1bool型变量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
using namespace std;int main() {// 1. 加减乘除int a1 = 10;int b1 = 3;/*因为a和b都是int,因此两个整数做除法仍然是int*/cout << a1 + b1 << endl;  // 13cout << a1 - b1 << endl;  // 7cout << a1 * b1 << endl;  // 30cout << a1 / b1 << endl;  // 3int a2 = 10;int b2 = 20;cout << a2 / b2 << endl;  // 0int 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;  // 1cout << aa2 % aa1 << endl;  // 3// 两个小数是不可以取余float ff1 = 3.14f;float ff2 = 1.01f;// cout << ff1 % ff2 << endl;  // 表达式必须具有整数或未区分范围的枚举类型(取余操作必须是整数!)// 取余操作的除数不能是0,被除数可以是0且结果恒等于0int 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;  // 让变量+1cout << "a = " << a << endl;  // 11// 3.2 后置递增: 先进行表达式运算,再让变量+1int b = 10;b++;cout << "b = " << b << endl;  // 11// 前置和后置的区别a = 10;b = ++a * 10;cout << "a = " << a << endl;  // a = 11cout << "b = " << b << endl;  // b = 110a = 10;b = a++ * 10;cout << "a = " << a << endl;  // a = 11cout << "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 = 9cout << "b = " << b << endl;  // b = 90a = 10;b = a-- * 10;cout << "a = " << a << endl;  // a = 9cout << "b = " << b << endl;  // b = 100system("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
using namespace std;int main() {// =int a = 10;cout << "a = " << a << endl;  // a = 10a = 20;cout << "a = " << a << endl;  // a = 20// +=a = 10;a += 2;  // a = a + 2cout << "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 = 0system("pause");return 0;
}

3.3 比较运算符

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

比较运算符有以下符号:

运算符术语示例结果
==相等于4==30
!=不等于4!=31
<小于4<30
>大于4>31
<=小于等于4<=30
>=大于等于4>=31
#include
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;  // 0// <=cout << (a <= b) << endl;  // 1system("pause");return 0;
}

3.4 逻辑运算符

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

逻辑运算符有以下符号:

运算符术语示例结果
!!a如果a为假,则!a为真;如果a为真,则!a为假
&&a && b如果a和b都为真,则结果为真,否则为假
||a || b如果a或b有一个为真,则结果为真
#include
using namespace std;int main() {// 逻辑非 !int a = 10;cout << "!a: " << !a << endl;  // !a: 0cout << "!!a: " << !!a << endl;  // !!a: 1// 逻辑与 &&int b = 20;cout << "a && b: " << (a && b) << endl;  // a && b: 1bool c = false;cout << "b && c: " << (b && c) << endl;  // b && c: 0int d = 0;cout << "c && d: " << (c && d) << endl;  // c && d: 0// 逻辑或 ||cout << "a || b: " << (a || b) << endl;  // a || b: 1cout << "b || c: " << (b || c) << endl;  // b || c: 1cout << "c || d: " << (c || d) << endl;  // c || d: 0system("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
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
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 > aif (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
using namespace std;int main() {// 创建三个变量 abc// 将a和b作比较,将变量大的值赋值给变量cint 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 = 10cout << "b = " << b << endl;  // b = 100system("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
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
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
using namespace std;
// 引入time头文件
#includeint 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
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次幂之和等于它本身

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

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

#include
using namespace std;
#include  // 引入数学头文件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
using namespace std;int main() {// 打印数字0~9for (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
using namespace std;int main() {// 1. 先输出1到100for (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
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
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=12*1=2   2*2=43*1=3   3*2=6   3*3=94*1=4   4*2=8   4*3=12  4*4=165*1=5   5*2=10  5*3=15  5*4=20  5*5=256*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=367*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=498*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=649*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
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
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       911      13      15      17      1921      23      25      27      2931      33      35      37      3941      43      45      47      4951      53      55      57      5961      63      65      67      6971      73      75      77      7981      83      85      87      8991      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
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. xxxxxxxx2. xxxxxxxx5. 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
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]: 10arr1[1]: 20arr1[2]: 30arr1[3]: 40arr1[4]: 50----- 2. 数据类型 数组名[数组长度] = {值1, 值2, ...}; -----arr2[0]: 10arr2[1]: 20arr2[2]: 30arr2[3]: 40arr2[4]: 50----- 3. 数据类型 数组名[] = {值1, 值2, ...}; -----arr3[0]: 90arr3[1]: 80arr3[2]: 60arr3[3]: 50arr3[4]: 40arr3[5]: 30arr3[6]: 20arr3[7]: 10arr3[8]: 0*/system("pause");return 0;
}

5.2.2 一维数组数组名

一维数组名称的用途:

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

在这里插入图片描述

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

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

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

#include
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;  // 000000B955D0F748cout << "arr数组的首地址(十进制)为: " << (int) arr << endl;  // 1439758152cout << "arr数组的第一个元素的地址为: " << &arr[0] << endl;  // 000000B955D0F748cout << "arr数组的第一个元素的地址(十进制)为: " << (int)&arr[0] << endl;  // 1439758152cout << "arr数组的第二个元素的地址为: " << &arr[0] << endl;  // 000000B955D0F748cout << "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
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
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
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
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
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 3020 40 60------ 2. 数据类型 数组名[行数][列数] = { {数据1,数据2},{数据3,数据4 }}; ------10 20 3040 50 60------ 3. 数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4}; ------10 20 3040 50 60------ 4. 数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4}; ------10 20 3040 50 60*/system("pause");return 0;
}

5.3.2 二维数组数组名

  1. 查看二维数组所占内存空间
  2. 获取二维数组首地址
#include
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;  // 2cout << "二维数组列数为: " << 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
using namespace std;
#include// 二维数组案例 —— 考试成绩统计
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
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
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
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.17return 0;
}

6.6 函数的声明

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

  • 函数的声明可以多次,但是函数的定义只能有一次
#include
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: 20return 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
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
using namespace std;  // 让我们可以使用cout在屏幕输出
#include "swap.h"/*函数分文件编写一般有4个步骤:1. 创建后缀名为.h的头文件: swap.h2. 创建后缀名为.cpp的源文件: swap.cpp3. 在头文件中写函数的声明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
using namespace std;int main() {// 1. 定义指针: 数据类型 *指针变量名int a = 10;int* p;;  // 这就定义了一个指针// 让指针*p记录变量a的地址p = &a;cout << "a的地址为: " << &a << endl;  // 000000F62119F7C4cout << "指针p为: " << p << endl;  // 000000F62119F7C4cout << "*p为: " << *p << endl;  // 10// 2. 使用指针// 可以通过解引用的方式来找到指针指向的内存// 指针前加一个*代表解引用,即找到指针指向的内存中的数据*p = 1000;cout << "a = " << a << endl;  // 1000cout << "*p = " << *p << endl;  // 1000return 0;
}

7.3 指针所占的内存空间

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

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

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

在这里插入图片描述

示例:

#include
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
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
using namespace std;// 野指针:在程序中尽量避免出现野指针
int main() {// 没有申请内存就直接让指针指向这个地址int* p = (int*)0x1100;cout << "p: " << p << endl;  // p: 0000000000001100cout << "*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
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
using namespace std;int main() {// 利用指针访问数组中的元素int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };cout << "第一个元素为: " << arr[0] << endl;  // 1int* p = arr;  // 数组名就是数组第一个元素的地址(数组的首地址)cout << "利用指针访问第一个元素: " << *p << endl;  // 1p += 1;  // 让指针向后偏移4个字节cout << "利用指针访问第二个元素: " << *p << endl;  // 1  // 2cout << "\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 10p2 += 1;}cout << endl;return 0;
}

7.7 指针和函数

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

#include
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 = 10b = 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 = 20b = 10*/return 0;
}

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

7.8 指针、数组、函数

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

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

#include
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 10return 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
using namespace std;
#include// 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        分数: 60return 0;
}

8.3 结构体数组

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

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

示例:

#include
using namespace std;
#include// 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
using namespace std;
#include// 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        分数: 100return 0;
}

8.5 结构体嵌套结构体

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

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

示例:

#include
using namespace std;
#include// 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
using namespace std;
#include// 定义学生结构体
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
using namespace std;
#includestruct 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
using namespace std;
#include
#include// 定义学生的结构体
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 ~ 100teac_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
using namespace std;
#include// 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;// 添加成功后,让最后一个联系的索引+1cout << "联系人[" << 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) {// 判断通讯录中人数是否为0if (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   // 屏幕中输入输出都需要这个头文件
using namespace std;  // 使用标准的命名空间
#include 
#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;// 添加成功后,让最后一个联系的索引+1cout << "联系人[" << name << "]添加成功!" << endl;abs->p_last_idx += 1;// 添加清屏操作system("pause");  // 请按任意键继续system("cls");  // 清屏操作: cls = clear screen}
}// 2. 显示所有联系人
void show_person(AddressBooks* abs) {// 判断通讯录中人数是否为0if (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;  // 返回正常退出值
}

相关内容

热门资讯

“Script“ 标签 1. “Script” 标签 可以使用 script 标签将任何 js 代码嵌入到 html 中 当...
特朗普关税被法院叫停,风险资产... 蒋立冬 派生万物AI 图特朗普关税“被叫停”后,亚洲股市应声走高。截至5月29日收盘,日经225指数...
图神经网络之CNN与GCN的联... 一、CNN(Convolutional Neural Network)卷...
DJ2-2 Web应用和HTT... 目录 1. Web 和 HTTP 常用术语 2. HTTP 概述 3. TCP 和 HTTP 4. ...
nginx-http-1 HTTP介绍 HTTP协议是Hyper Text Transfer Protocol(...
V观财报|“四连板”均瑶健康:...   中新经纬5月29日电 29日,均瑶健康再发股票交易异常波动公告。  公告显示,均瑶健康于5月28...
OceanMind海睿思受邀参... 近日,由江苏省工业和信息化厅、宜兴市人民政府指导,宜兴市工业和信息化局、...
SpringBoot Rabb... SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见。 程序员每天的C...
为了赶走烂物业,我被举报到纪委... 我本是为了维护自身权益,试图赶走那令人不满的烂物业。然而,未曾想此举竟让我陷入了意想不到的境地。不知...
*ST围海:签订1.82亿元重... 新京报贝壳财经讯 5月29日,*ST围海公告,公司与文成县水利发展有限公司签署了“文成县城防洪提升工...
宇树回应更名“股份有限公司” ... 5月29日,有消息称,宇树科技向合作伙伴发布通知称,因公司发展需要,杭州宇树科技有限公司即日起名称变...
美股七巨头交出超预期一季报:提... 围绕关税和AI(人工智能)两大话题,美股科技“七巨头”发布了基本超出预期的一季报。近日,美股科技“七...
LuaJIT 常量数组(con... 从LuaJIT Bytecode介绍中可知道,Bytecode关于常量操作的指令均为D...
js 数组中的对象去掉重复的对...  id相同的,保留第一个,其它的删除this.defaultFileLi...
许昌,几线城市?请看2025年... 第一财经·新一线城市研究所 5月28日发布 《2025新一线城市魅力排行榜》 在中国内地337座地级...
拼多多股价波动:牺牲短期业绩,... 5月27日晚,拼多多最新一季度财报发布,营收约957亿人民币,同比增长10%,已经是最近几个季度的最...
额度放开了,再选一遍美债基金~ 转自:懒猫的丰收日 最近,好多美债基金放开了额度, 也有不少小伙伴问美债基金,那就再筛一遍,大概是这...
广州农商银行:唤醒农村沉睡资源... 随着乡村振兴战略的深入实施,农村“三块地”——耕地、宅基地和集体经营性建设用地的改革与盘活,正在成为...
中国艾欧智能团队远程操控机器人... 在2025年度ICRA双臂机器人能力边界挑战赛上,深圳艾欧智能公司凭借其卓越表现,从全球88支参赛队...
同仁堂资本新局 拆分上市在下什... 暌违十多年的资本运作。 《投资者网》蔡俊 暌违十多年,同仁堂(600085.SH,下称“公司”)...
集中上市!增量资金来了 增量资金持续驰援市场,新发ETF密集上市。5月以来,23只ETF上市,9只ETF即将上市,这些ETF...
3.类型、存储和变量 目录 3.1 C#程序是一组类型声明  3.2 类型是一种模板  3.3 实例化类型  3.4 数...
圆通速递今日大宗交易折价成交7... 5月29日,圆通速递大宗交易成交795万股,成交额1.01亿元,占当日总成交额的15.22%,成交价...
最新!宇树科技改名了 证券时报消息,5月29日,宇树科技向合作伙伴发布通知称,因公司发展需要,杭州宇树科技有限公司即日起名...
持续改进与回归 系列文章目录 第一章 敏捷核心知识点 第二章 敏捷宣言与原则 第三章 价值驱动交付-优先级排序&增量...
ST先锋:6月3日起撤销其他风... 5月29日晚间,宁波先锋新材料股份有限公司(ST先锋,300163.SZ)公告,公司股票将于5月30...
双指针 -876. 链表的中间... 开始一个专栏,写自己的博客 双指针,也算是作为自己的笔记吧!...
100%原价买飞天茅台!茅台机... 红星资本局5月29日消息,遵义茅台机场官微发布消息,5月30日-6月30日期间,茅台机场开启2个乘机...
restTemplate未设置... 背景 昨天发版遇到个线上问题,由于运维操作放量时隔离机器过多,导致只有大...
第一批痴迷中古的年轻人,已经袪... 第一批痴迷中古的年轻人,曾如朝圣般追寻那些古老的物件与文化。他们沉浸在中古的世界里,仿佛找到了心灵的...