C++[面向对象的程序设计]_基础入门(上)(万字总结)(建议收藏!!!)
目录
? ? ? ? C++ 在 C 语言的基础上进一步扩充和完善了 C 语言,成为一种面向对象的程序设计语言,C++ 应用广泛,支持多种编程范式,面向对象编程、泛型编程和过程化编程。从这里开始,我们踏上 C++ 的学习之路。长路漫漫亦灿灿,祝我也祝所有看到这篇文章的朋友!!!
1. C++基础入门
? ? ? ? 相信大多数朋友和我一样,在学习 C 语言的过程中,都经历了从 HelloWorld 开始的程序编写;C++ 作为一门编程语言,首先我们就从 HelloWorld 开始书写!
用 C++ 书写 HelloWorld!!!
? ? ? ? 编译平台:Visual Studio 2013;? ? ? ? 大家对 C++ 的创建过程一定很熟悉了,这里简单的回顾如何创建 C++ 工程!首先 文件 -> 添加项目 -> 添加空项目(合理设置路径和名称) -> 源文件 -> 添加新建项 -> C++文件(合理设置文件名即可)。到这里,一个 C++ 的项目的创建成功!
#include <iostream> using namespace std; int main() { system("pause"); return 0; } //这里是一个 c++ 的程序框架,不管我们写任何 c++ 代码,都可以先写上这些代码 //至于为什么,我们往下学习!!!
? ? ? ? 当我们在 system("pause"); 前加上 cout << "Hello World" << endl; 以后,就可以打印出 Hello World 了,也就是说在 << << (双书名号)中加上要打印的字符即可打印出我们想要 Hello World 了。
注释:
? ? ? ? 在代码中加一些说明和解释,方便自己或其他程序员阅读代码!!!
? ? ? ? 在 C++ 中有两种格式的注释代码:
????????1. 单行注释: // 描述信号
?????????通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明。
? ? ? ? 2. 多行注释: /* 描述信息?*/
? ? ? ? 通常放在一段代码的上方,对该段代码做整体说明。
? ? ? ? 编译器在编译代码时,会忽略注释的内容。
1.1 变量?
? ? ? ? 变量本质的作用:给一段指定的内存空间起名,方便操作这段内存。(首先我们知道 C++ 中的所有数据都会放在内存中,变量就相当于给这段内存起个名字,方便我们操作这段内存)
? ? ? ? 语法:数据类型? 变量名? =? 初始值;
1.2 常量
? ? ? ? 作用:用于记录程序中不可更改的数据。
C++ 定义常量的两种方式:
? ? ? ? 1. #define 宏常量:#define 常量名 常量值
? ? ? ? 通常在文件上方,表示一个常量。
? ? ? ? 2. const 修饰的变量:const 数据类型 常量名 = 常量值
? ? ? ? 通常在变量定义前加关键字 const,修饰该变量为常量,不可修改。
1.3 关键字
? ? ? ? 作用:关键字是 C++ 中预先保留的单次(标识符)
? ? ? ? 在定义变量或常量的时候,不要用关键字。
在定义变量或常量的时候,不要用关键字。
????????这句话的意思等同于不要用关键字给变量或者常量起名称。
定义变量的时候: 我们可以定义 int a = 10; // 此时 a 表示给一段内存起了个名称,方便我们后续进行内存管理 但是我们不可以定义 int int = 10; // 上述代码的意思表示给一段内存起名称为 int,int 是 C++ 中的关键字 // 这是程序就会报错
提示:在给变量或者常量起名称的时候,不要用 C++ 的关键字,否则会引起歧义!
1.4 标识符命名规则
? ? ? ? 作用:C++ 规定给标识符(变量、常量)命名时,有自己的一套规则。(标识符其实就是代表常量或者变量)(标识符其实就是我们给一段内存起的名字)
- ? ? ? ? 标识符不能是关键字。? ? ? ? // 这个在上一节已经提到了,不可以 int int = 10;
- ? ? ? ? 标识符只能由字母、数字、下划线组成。 // 给一段内存起名时 要注意:不可以掺杂 ¥……%……&¥#¥@ 等符号,有明确的要求
- ? ? ? ? 第一个字符必须为字母或下划线。? ? // 给一段内存起的名字开头有要求 必须为 : Hello_ ; _Hello?
- ? ? ? ? 标识符中字母区分大小写。? ?// 给一段内存起的名字 中的字母要区分大小写
1.5 数据类型
? ? ? ? C++ 规定在创建一个变量或者常量时,必须要指定出相应的数据类型,否则无法给变量分配内存。(数据类型存在的意义就是给变量分配合适的内存空间)
1.5.1 整型
? ? ? ? 作用:整型变量表示的是整数类型的数据。
? ? ? ? C++ 中能够表示整型的类型有有以下几种,区别在于所占内存空间不同:
? ? ? ? 数据类型? ? ? ? ? ? ? ? ? ? ? ? 占用空间? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????取值范围
? ? ? ? short(短整型)? ? ? ? ? ? ? ? 2字节? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?~ )
? ? ? ? int(整型)? ? ? ? ? ? ? ? ? ? ? ? 4字节? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?~ )
? ? ? ? long(长整型)? ? ? ? Windows为4字节,Linux为4字节(32位OS),8字节(64位)??(?~ )
? ? ? ? long long(长长整型)? ? ? 8字节? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (?~ )
? ? ? ? 我们知道数据类型本质的区别是:占用空间内存的大小不同!!!现在我们修改存储数据的大小,让其越界。比如说:短整型的范围是 -32768 ~ 32767;
? ? ? ? 现在可以看出,短整型的范围为 -32768 ~ 32767,现在设置短整型的数值为 32768 ,越界之后的短整型数据显示为 -32768,显然与真实表达的数据不同!!!
1.5.2 sizeof 关键字
? ? ? ? 作用:利用 sizeof 关键字可以统计数据类型所占内存大小。
? ? ? ? 语法:sizeof(数据类型/变量)
1.5.3 实型(浮点型)
? ? ? ? 作用:用于表示小数。
? ? ? ? 浮点型变量分为两种:
? ? ? ? 1. 单精度 float
? ? ? ? 2. 双精度 double
? ? ? ? 两者的区别在于表示的有效数据范围不同。
? ? ? ? 数据类型? ? ? ? ? ? ? ? ? ? ? ? 占用空间? ? ? ? ? ? ? ? ? ? ? ? 有效数据范围
? ? ? ? float? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 4字节? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 7位有效数据
? ? ? ? double? ? ? ? ? ? ? ? ? ? ? ? ? ? 8字节? ? ? ? ? ? ? ? ? ? ? ? ? 15 ~ 16位有效数据
注:这里解释一下 float num1 = 3.14f,为什么 3.14 后面会加上一个 f;(明确加上这个 f 后缀的意思表示的是 float 类型)
? ? ? ? 首先如果不加这个 f 后缀,那么系统默认小数是 double 类型,然后?float num1 = 3.14;的意思表示的是将双精度浮点型的 3.14 转换为 float 类型,相比于加上后缀,中间是多一步转换过程的!
默认情况下,输出一个小数会显示 6 位有效数字!
? ? ? ? num2 初始值为 3.1415926,但是实际的打印结果为 3.14159;
科学计数法:
1.5.4 字符型
? ? ? ? 作用:字符型变量用于显示单个字符
? ? ? ? 语法:char ch = ‘a’;
注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号;
注意2:单引号内只能有一个字符,不可以是字符串;
注意3:C 和 C++ 中的字符型变量只占 1 个字节。
注意4:字符型变量并不是把字符本身放到内存中存储,而是将对应的 ASCII 编码放入到存储单元。
1.5.5 转义字符
? ? ? ? 作用:用于表示一些不能显示出来的 ASCII 字符。
? ? ? ? 现阶段我们常用的 ASCII 字符有 \n? \\? ?\r;
\n:换行(LF),将当前位置移到下一行开头? ? ? ? ASCII 码值 010
? ? ? ? 可以看出,换行符 \n 等价于 换行标志 endl;
\t:水平制表(HT),(跳到下一个 TAB 位置)? ? ? ? ASCII 码值 009
? ? ? ? 反斜杠 + t 表示:在 反斜杠 + t 之前的基础上跳到下一个 TAB 位置,现在 Zhang 占用 5 个字符,那么后续会再空出 3 个字符的位置,然后才会打印 Hello World;
\\:代表一个反斜线字符 “\”? ? ? ? ASCII 码值 092
? ? ? ? 现在我要输出一个 \ ,那么必须要输出两个反斜杠,第一个反斜杠表示告诉编辑我要使用转义字符;
1.5.6 字符串类型
? ? ? ? 作用:表示的是一串字符。
两种风格:
? ? ? ? 1. C 风格字符串:char 变量名[] = “字符串值”;
? ? ? ? C 风格的字符串要用中括号括起来;
????????2. C++ 风格字符串:string 变量名 = “字符串值”;
????????C++ 风格的字符串要引用头文件:#include <string>
1.5.7 布尔类型
? ? ? ? 作用:布尔数据类型代表真或假的值。
? ? ? ? bool 类型只有两个值:
- ? ? ? ? true --- 真(本质是 1)
- ? ? ? ? false --- 假(本质是 0)
? ? ? ? bool 类型占 1 个字节大小;
注意:布尔类型创建 true 或者 false 的时候,只用在最开始定义一个 bool 类型即可!
1.5.8 数据的输入
? ? ? ? 作用:用于从键盘获取数据
? ? ? ? 关键字:cin
? ? ? ? 语法:cin>>变量
注:
? ? ? ? cout 和 cin 可以记忆为:out 译为输出,in 译为输入,cin 键盘输入,cout 显示器输出;
1.6 运算符
? ? ? ? 作用:用于执行代码的运算
? ? ? ? 运算符类型? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 作用
? ? ? ? 算术运算符? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 用于处理四则运算
? ? ? ? 赋值运算符? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 用于将表达式的值赋给变量
? ? ? ? 比较运算符? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 用于表达式的比较,并返回一个真值或假值
? ? ? ? 逻辑运算符? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 用于根据表达式的值返回真值或假值
1.6.1 算术运算符 - 加减乘除运算
? ? ? ? 算术运算符主要包括加减乘除四则运算,但是其中加法包括两种形式,+2、-2 表示数的正负性,2+1 、2-1 表示四则运算。
1.6.2 算术运算符 - 取模运算
? ? ? ? 运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? ?%? ? ? ? ? ? ? ? ? ? ? ?取模(取余)? ? ? ? ? ? ? 10%3? ? ? ? ? ? ? ? ? ? ? ? ?1?
1.6.3 算术运算符 - 递增递减
? ? ? ??? ? ? ? 运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? ? ? ? ? ?++? ? ? ? ? ? ? ? ? ? ? ? 前置递减? ? ? ? ? ? ?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;
? ? ? ? 程序跑到这里,可以看到不管是前置++还是后置++,都可以实现数值加一,其实质等价于 a = a + 1 ;那么到底有什么本质区别呢?我们接着往下看:
1.6.4 赋值运算符
? ? ? ??? ? ? ? 运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? ? ? ? ? ? =? ? ? ? ? ? ? ? ? ? ? ? ? ? ?赋值? ? ? ? ? ? ? ? ? ? a=2,b=3;? ? ? ? ? ?a=2;b=3;
? ? ? ? ? ? ? ? ? ?+=? ? ? ? ? ? ? ? ? ? ? ? ? 加等于? ? ? ? ? ? ? ? ?a=0;a+=2;? ? ? ? ? ? ? ?a=2;
? ? ? ? ? ? ? ? ? ?-=? ? ? ? ? ? ? ? ? ? ? ? ? ?减等于? ? ? ? ? ? ? ? ?a=5;a-=3;? ? ? ? ? ? ? ? a=2;
? ? ? ? ? ? ? ? ? ?*=? ? ? ? ? ? ? ? ? ? ? ? ? ?乘等于? ? ? ? ? ? ? ? ?a=2;a*=2;? ? ? ? ? ? ? ?a=4;
? ? ? ? ? ? ? ? ? ?/=? ? ? ? ? ? ? ? ? ? ? ? ? ?除等于? ? ? ? ? ? ? ? ?a=4;a/=2;? ? ? ? ? ? ? ?a=2;
? ? ? ? ? ? ? ? ? %=? ? ? ? ? ? ? ? ? ? ? ? ? 模等于? ? ? ? ? ? ? ? ?a=3;a%2;? ? ? ? ? ? ? ?a=1;? ? ? ?
1.6.5 比较运算符
? ? ? ? 作用:用于表达式的比较,并返回一个真值或假值
????????运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? ?==? ? ? ? ? ? ? ? ? ? ? ? ? 相等于? ? ? ? ? ? ? ? ? ? ? 4==3? ? ? ? ? ? ? ? ? ? ? ? ? 0
? ? ? ? ? ?!=? ? ? ? ? ? ? ? ? ? ? ? ? ?不等于? ? ? ? ? ? ? ? ? ? ? 4!=3? ? ? ? ? ? ? ? ? ? ? ? ? ?1
? ? ? ? ? ?<? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 小于? ? ? ? ? ? ? ? ? ? ? ? 4<3? ? ? ? ? ? ? ? ? ? ? ? ? ? 0
? ? ? ? ? ?>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 大于? ? ? ? ? ? ? ? ? ? ? ? 4>3? ? ? ? ? ? ? ? ? ? ? ? ? ? 1
? ? ? ? ? <=? ? ? ? ? ? ? ? ? ? ? ? ?小于等于? ? ? ? ? ? ? ? ? ? 4<=3? ? ? ? ? ? ? ? ? ? ?? ? ?0
? ? ? ? ? >=? ? ? ? ? ? ? ? ? ? ? ? ?大于等于? ? ? ? ? ? ? ? ? ? 4>=1? ? ? ? ? ? ? ? ? ? ? ? ? 1
1.6.6 逻辑运算符 - 非
? ? ? ? 作用:用于根据表达式的值返回真值或假值(注意:C++ 中定义只要不为 0 都为真,非假即真!)
????????运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? ? !? ? ? ? ? ? ? ? ? ? ? ? ? ? ?非? ? ? ? ? ? ? ? ? ? ? ? ? !a? ? ? ? ? ? ? ?如果a为假,则!a为真;如果a为真,则!a为假;
1.6.7 逻辑运算符 - 与
????????运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? &&? ? ? ? ? ? ? ? ? ? ? ? ? ? ?与? ? ? ? ? ? ? ? ? ? ? ? ?a&&b? ? ? ? ? ? ?如果a和b都为真,则结果为真,否则为假;
1.6.8 逻辑运算符 - 或
????????运算符? ? ? ? ? ? ? ? ? ? ? ? 术语? ? ? ? ? ? ? ? ? ? ? ? 示例? ? ? ? ? ? ? ? ? ? ? ? 结果
? ? ? ? ? ? ||? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?或? ? ? ? ? ? ? ? ? ? ? ? ? a||b? ? ? ? ? ? ? 如果a和b有一个为真,则结果为真,二者都为假时,结果为假;
1.7 程序流程结构
? ? ? ? C 和 C++ 支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构
- ? ? ? ? 顺序结构:程序按顺序执行,不发生跳转
- ? ? ? ? 选择结构:依据条件是否满足,有选择的执行相应功能
- ? ? ? ? 循环结构:依据条件是否满足,循环多次执行某段代码
1.7.1 选择结构_单行if语句
? ? ? ? 语法:if{条件}{条件满足执行的语句}
1.7.2 选择结构_多行if语句
? ? ? ? 语法:if{条件}{条件满足执行的语句}else{条件不满足执行的语句};
1.7.3 选择结构_多条件if语句
? ? ? ? 语法:if{条件1}{条件1满足执行的语句}else if{条件2}{条件2满足执行的语句}……else{都不满足执行的语句}
1.7.4 选择结构_嵌套if语句
? ? ? ? 嵌套 if 语句:在 if 语句中,可以嵌套使用 if 语句,达到更精确的条件判断。
1.7.5 选择结构案例_三只小猪称体重
练习案例:三只小猪称体重,有三只小猪 ABC,请分别输入三只小猪的体重,并且判断哪只小猪最重?
1.7.6 选择结构案例_三目运算符
? ? ? ? 作用:通过三目运算符实现简单的运算
? ? ? ? 语法:表达式1?表达式2:表达式3
- ? ? ? ? 如果表达式1的值为真,执行表达式2,并返回表达式2的结果;
- ? ? ? ? 如果表达式1的值为假,执行表达式3,并返回表达式3的结果;
在 C++ 中三目运行符返回的是变量,可以继续赋值!
1.7.7 选择结构_switch语句
? ? ? ? 作用:执行多条件分支语句
switch语句语法:
switch(表达式)
{
case 结果1:
执行语句;
break;
case 结果2:
执行语句;
break;
……
default:
执行语句;
break;
}
if 和 switch 区别?
? ? ? ? switch 缺点:判断时候只能是整型或者字符型,不可以是一个区别;
? ? ? ? switch 优点:结构清晰,执行效率高!
注意1:switch语句中表达式类型只能是整型或者字符型;
注意2:case里如果没有break,那么程序会一直向下执行;
总结:与 if 语句相比,对于多条件判断时,switch的结构清晰,执行效率高,缺点是switch不可以判断区间。
1.7.8 循环结构_while语句
? ? ? ? 作用:满足循环条件,执行循环语句
? ? ? ? 语法:while(循环条件){循环语句}
? ? ? ? 解释:只要循环条件为真,就执行循环语句
注意:while 循环中一定要设置跳出循环的条件,否则就会出现死循环的现象!
1.7.9 循环结构案例_猜数字
? ? ? ? 案例描述:系统随机生成一个 1 到 100 之间的数字,玩家进行猜测,如果猜错,提示玩家数字过大或者过小,如果猜对恭喜玩家胜利,并且退出游戏!
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
int main()
{
// 1. 系统生成一个随机数
int num = rand() % 100 + 1; // rand()%100 表示随机生成一个 0 - 99 的数字,现在要求生成一个 1-100 的数字,所以整体+1即可;
// 这里注意:用rand生成的随机数每次都是一样的,为了保证每次的随机数都不一样,C++ 提供了另外一个函数
// srand((unsigned int) time(NULL));
// 使用此函数需要引用 time 时间头文件!
// 2. 玩家进行猜测
int Value = 0;
int Count = 5;
cout << "请玩家输入您的数值:" << endl;
cin >> Value;
// 3. 判断玩家的猜测是否是这个随机数
while (Count--)
{
if (Value>num)
{
cout << "您输入的数值过大,请重新输入" << endl;
cout << "请玩家输入您的数值:" << endl;
cin >> Value;
}
else if (Value<num)
{
cout << "您输入的数值过小,请重新输入" << endl;
cout << "请玩家输入您的数值:" << endl;
cin >> Value;
}
else
{
cout << "恭喜您猜对了!!!" << endl;
break; // break 关键字用于跳出循环!
}
}
cout << "很遗憾未在有限次数内猜对!!!" << endl;
// 4. 猜对 推出游戏 猜错,提示玩家数值过大或者过小,重新返回第二步
system("pause");
return 0;
}
1.7.10 循环结构_dowhile语句
? ? ? ? 作用:满足循环条件,执行循环语句
? ? ? ? 语法:do{循环语句}while(循环条件);
注意:与 while 循环的区别在于 do……while 循环会先执行一次循环语句,再判断循环条件;也就是说哪怕循环条件不是成立的,循环语句也会执行一次;
1.7.11 循环结构案例_水仙花数
? ? ? ? 案例描述:水仙花数是指一个 3 位数,它的每个位上的数字的 3 次幂之和等于它本身(用 do……while 循环语句求出 3 位数中的水仙花数)
? ? ? ? 例如:
1.7.12 循环结构_for循环
? ? ? ? 作用:满足循环条件,执行循环语句
? ? ? ? 语法:for(起始表达式;条件表达式;末尾循环体){循环语句;}
1.7.13 循环结构案例_敲桌子
? ? ? ? 练习案例:从 1 开始数到数字 100,如果数字个位含有 7,或者数字十位含有 7,或者该数字是 7 的倍数,满足以上条件我们打印敲桌子,其余数字直接打印输出。
1.7.14 嵌套循环
? ? ? ? 作用:在嵌套体中再嵌套一层循环,解决一些实际问题
注意:
? ? ? ? 在嵌套循环中需要注意,两个循环设置的变量尽量不要设置为一样的;这样对初学者来说,可能不清楚 i 对应的究竟是哪一个循环,如果了解过的可能清楚,嵌套循环中的变量遵循就近原则,也就是离哪个循环越近,变量就属于哪一个循环。
int main() { // 1. 利用嵌套循环打印一个矩阵式的 * 图 //通常认为:外层执行一次,内层执行一周; //外层循环 for (int i = 1; i < 11;i++) { //内层循环 for (int i = 1; i < 11; i++) { cout <<"* "; } /*cout << endl;*/ cout << "\n"; } system("pause"); return 0; }
1.7.15 嵌套循环案例_乘法口诀表
? ? ? ? 案例描述:利用嵌套循环,实现九九乘法表!
1.7.16 跳转语句_break语句
? ? ? ? 作用:用于跳出选择结构或者循环结构
break 使用的动机:
- ? ? ? ? 出现在 switch 条件语句中,作用是终止 case 并跳出 switch;
- ? ? ? ? 出现在循环语句中,作用是跳出当前的循环语句;
- ? ? ? ? 出现在嵌套循环中,跳出最近的内层循环语句;
break 在 switch 语句中的作用:
break 在循环结构中的作用:
break 在嵌套语句中的作用:
1.7.17 跳转语句_continue
? ? ? ? 作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
1.7.18 跳转语句_goto
? ? ? ? 作用:可以无条件跳转语句
? ? ? ? 语法:goto 标记;
? ? ? ? 解释:如果标记的名称存在,执行到 goto 语句时,会跳转到标记的位置
1. *****
2. *****
goto FLAG;
3. *****
4. *****
FLAG:
5. *****
(goto关键字的作用就是:
正常来说,如果没有goto关键字,那么程序是按照顺序来执行的,1 2 3 4 5;
但是如果加上关键字goto,那么就会出现程序跳转的情况;
也就是说现在程序的执行过程是 1 2 5
执行完第二条语句以后,出现关键字goto FLAG;(goto 关键字后通常接全大写的;)
FALG加冒号表示跳转到的位置;)
1.8 数组
? ? ? ? 所谓数组,就是一个集合,里面存放了相同类型的数据元素;
- ? ? ? ? 特点1:数组中每个数据元素都是相同的数据类型
- ? ? ? ? 特点2:数组是由连续的内存位置组成的
1.8.1 一维数组定义方式
? ? ? ? 一维数组定义的三种方式:
- ? ? ? ? 1. 数据类型? ?数组名[ 数组长度 ];
- ? ? ? ? 2. 数据类型? ?数组名[ 数组长度 ] = { 值1,值2 ……};
- ? ? ? ? 3.?数据类型? ?数组名[? ] = { 值1,值2 ……};
注:对于 数据类型? ?数组名[ 数组长度 ] = { 值1,值2 ……};如果在初始化数据的时候,没有全部填写完,会用 0 来填补空缺!
1. 数组元素的下标是从 0 开始索引的!!!
2. 定义数组的时候,必须有初始长度;如果没有初始长度,那么必须给数组元素进行初始化,通过元素初始化的方式告诉数组初始长度;
1.8.2 一维数组 - 数组名
一维数组名称的用途(这很重要,认真分析):
- ? ? ? ? 1. 可以统计整个数组在内存中的长度
- ? ? ? ? 2. 可以获取数组在内存中的首地址
????????这里可以很明显的发现:我们定义的数组类型是 int 类型,int 类型在内存中占用 4 个字节,这也说明了为什么整个数组在内存中占用 40 个字节;arr[0] 表示数组首元素所占的内存大小,单个元素也就是 int 类型,所占的内存大小为 4;让整个数组所占的内存大小除以单个元素所占的内存大小,得到了数组的长度:也就是 10;
? ? ? ? 通过上述程序可以发现:数组名 arr 和 &arr[0] 得到的地址是相同的,这再一次印证了数组名代表首元素的地址;
? ? ? ? 通过这里可以看出:数组的首元素和第二个元素之间差了 4 个字节,20231108~20231112;第二个元素和第三个元素;第三个元素和第四个元素都是如此;而 int 类型在内存中所占正好是 4 个字节;这也证明了数组中的元素在内存中是连续存放的!!!
1.8.3 一维数组案例 - 五只小猪称体重
? ? ? ? 案例描述:在一个数组中记录了五只小猪的体重,如 int arr[5] = {300,350,200,400,250};找出并打印最重的小猪体重!!!
1.8.4 一维数组案例 - 元素逆置
? ? ? ? 案例描述:声明一个 5 个元素的数组,并且将数组元素逆置(原数组元素为:1,3,2,5,4,逆置后的数组为:4,5,2,3,1)
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
int main()
{
// 1. 声明一个 5 个元素的数组,并且将数组元素逆置(例如 原数组元素为:1,3,2,5,4,逆置后的数组为:4,5,2,3,1)
// 首先逆置存在两种情况:判断数组元素是奇数还是偶数;
int arr[6] = { 300, 350, 200, 400, 250, 600 };
int num = sizeof(arr) / sizeof(arr[0]);
if (num%2==0) //偶数
{
int Select = 0;
for (int i = 0; i < num; i++)
{
if (i == num / 2)
{
break;
}
int start = arr[i];
int end = arr[num - i - 1];
Select = arr[i];
arr[i] = arr[num - i - 1];
arr[num - i - 1] = Select;
}
}
else //奇数
{
int Select = 0;
for (int i = 0; i < num; i++)
{
if (i == num / 2)
{
break;
}
int start = arr[i];
int end = arr[num - i - 1];
Select = arr[i];
arr[i] = arr[num - i - 1];
arr[num - i - 1] = Select;
}
}
for (int i = 0; i < num;i++)
{
cout << arr[i] << endl;
}
system("pause");
return 0;
}
上述算法略显繁琐,下面算法提供一种新的思路:
#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <string> // 用 C++ 风格的字符串需要包含这个头文件 #include <ctime> // time 系统时间头文件包含 using namespace std; int main() { // 1. 声明一个 5 个元素的数组,并且将数组元素逆置(例如 原数组元素为:1,3,2,5,4,逆置后的数组为:4,5,2,3,1) int arr[6] = { 300, 350, 200, 400, 250, 600 }; int num = sizeof(arr) / sizeof(arr[0]); // 打印原数组 cout << "元素逆置前:" << endl; for (int i = 0; i < num;i++) { cout << arr[i] << endl; } // 算法思路:首先定义数组的起始坐标和终止坐标 int start = 0; //数组起始坐标为 0 int end = num - 1; //数组终止坐标为数组元素个数-1 while (start<end) // 一定要保证数组的起始坐标小于终止坐标 // 否则互换两次相当于返回原数组 { // 互换起始坐标和终止坐标,需要借助一个中间变量 // 将起始坐标赋值给中间变量,让终止坐标覆盖起始坐标,再让中间变量覆盖终止坐标; int Temp = arr[start]; arr[start] = arr[end]; arr[end] = Temp; // 互换的过程中需要起始坐标不断向后移动,终止坐标不断向前移动; start++; end--; } // 打印逆置后的数组 cout << "元素逆置后:" << endl; for (int i = 0; i < num; i++) { cout << arr[i] << endl; } system("pause"); return 0; }
1.8.5 一维数组 - 冒泡排序
? ? ? ? 作用:最常用的排序算法,对数组内元素进行排序
算法思想:
- ????????1. 比较相邻的元素,如果第一个比第二个大,就交换它们两个。
- ? ? ? ? 2. 对每一对相邻的元素做同样的工作,执行完毕后,找到第一个最大值。
- ? ? ? ? 3. 重复以上的步骤,每次比较次数 -1,直到不需要比较。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
int main()
{
// 1. 将数组 {4,2,8,0,5,7,1,3,9,6} 进行升序排序;
int arr[] = { 4, 2, 8, 0, 5, 7, 1, 3, 9, 6 };
int num = sizeof(arr) / sizeof(arr[0]);
// 排序前的数组
cout << "排序前的数组:" << endl;
for (int i = 0; i < num; i++)
{
cout << arr[i] << " ";
}
// 本次采用冒泡排序的思想
for (int i = 0; i < num;i++) // 外循环记录排序的轮数,也就是说第一轮排序会排出一个最大值;(每一轮排序都会得到一个最大值)
// 那么需要 数组元素-1 轮才能排完
// 第一轮得到一个最大值,那么下一轮排序时就会少一个数;
// 10 个数: 8 7 6 5 4 3 2 1 0 轮
{
for (int j = 0; j < num-i-1; j++) // 第一轮得到一个最大值,那么下一轮排序时就会少一个数;
// 也就是说每一次对比的数都会少一次
// 第一次 10 个数对比 9 次;9个数8次;8个数7次;7个数6次;2个数一次; 9 8 7 6 5 4 3 2 1
// 也就是说外循环第0轮排序时,内循环循环9次;第1轮排序时,内循环循环8次;…… 第8轮循环时,内循环循环一次;
// 外循环 8 7 6 5 4 3 2 1 0,外循环从 0 到 8,9次;对应于 sizeof(arr) / sizeof(arr[0])-1;跳出外循环;i<num 等价于 sizeof(arr) / sizeof(arr[0])-1;
// 内循环 1 2 3 4 5 6 7 8 9,外循环0对应内循环9,外循环1对应内循环8;sizeof(arr) / sizeof(arr[0])-i-1;
{
if (arr[j]>arr[j + 1])
{
int Temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = Temp;
}
}
}
cout << endl;
// 排序后的数组
cout << "排序后的数组:" << endl;
for (int i = 0; i < num; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
1.8.6 二维数组定义方式
? ? ? ? 二维数组就是在一维数组的基础上,多加了一个维度;
二维数组定义的四种方式:
- ? ? ? ? 1. 数据类型? ?数组名[ 行数 ] [ 列数 ];
- ? ? ? ? 2. 数据类型? ?数组名[ 行数 ] [ 列数 ] = { { 数据1,数据2 },{ 数据3,数据4 } };
- ? ? ? ? 3.?数据类型? ?数组名[ 行数 ] [ 列数 ] = {? 数据1,数据2 ,数据3,数据4? };
- ? ? ? ? 4.?数据类型? ?数组名[? ] [ 列数 ] = {? 数据1,数据2 ,数据3,数据4? };
1. 数据类型? ?数组名[ 行数 ] [ 列数 ];
?
?2. 数据类型? ?数组名[ 行数 ] [ 列数 ] = { { 数据1,数据2 },{ 数据3,数据4 } };
3.?数据类型? ?数组名[ 行数 ] [ 列数 ] = {? 数据1,数据2 ,数据3,数据4? };
?4.?数据类型? ?数组名[? ] [ 列数 ] = {? 数据1,数据2 ,数据3,数据4? };
注:
? ? ? ? 二维数组的行可以省略,但是列一定不能省略!!!因为程序需要根据列数去计算对应的行数;所以列万万不能省略!!!
? ? ? ? 另外二维数组如果初始化的数组元素个数不足以平均分配,那么空缺的部分会用 0 来替补上!!!
1.8.7 二维数组 - 数组名
二维数组数组名的作用:
? ? ? ? 1. 查看二维数组所占内存空间的大小;
? ? ? ? 2. 获取二维数组首地址;
1. 查看二维数组所占内存空间的大小;
????????至于结果是如何获得的:大家应该很清晰了,int 类型的数组,每个元素所占内存空间的大小为 4字节;那个 6 个元素所占内存空间大小为 24字节;第一行 3 个元素占用 12 个字节;
2. 获取二维数组首地址;
? ? ? ? 通过上述程序可以发现:和一维数组一样,每个元素之间相差的都是 4 个字节,int 类型的字节大小!!!
1.8.8 二维数组案例 - 考试成绩统计
考试成绩统计:
? ? ? ? 案例描述:有三名同学(张三、李四、王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩:
1.9 函数
? ? ? ? 作用:将一段经常使用的代码封装起来,减少代码的重复性!
? ? ? ? 一个较大的程序,一般分为若干程序块,每个模块实现特定的功能!
1.9.1 函数的定义
函数的定义一般主要有 5 个步骤:
? ? ? ? 1. 返回值类型????????(看一下函数最后需要返回一个什么样的数据类型)
? ? ? ? 2. 函数名? ? ? ? (给函数起个名字,方便我们后续调用)
? ? ? ? 3. 参数表列? ? ? ? (看一看需不需要给函数输入参数,需要输入什么样的参数)
? ? ? ? 4. 函数体语句? ? ? ? (函数真正要实现功能的程序)
? ? ? ? 5. return 表达式? ? ? ? (写这个函数的时候,我们需不需要通过这个函数反馈给我们什么)
返回值类型 函数名(参数列表) { 函数体语句 return 表达式 }
1.9.2 函数的调用
? ? ? ? 功能:我们定义好的函数,如何去使用我们定义好的函数;
? ? ? ? 语法:函数名(参数)
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
// 定义加法函数
// 函数定义的时候,x 和 y 并没有真是数据;
// 它只是一个形式上的参数,简称形参
int Add(int x,int y)
{
int sum = 0;
sum = x + y;
return sum;
}
int main()
{
// 实现一个加法函数,功能是:传入两个整型数据,计算数据相加的结果,并且返回!!!
// 33 66 称为实际参数,简称实参;
// 当调用函数的时候,实际参数会传给形式参数;
int num = Add(33, 66);
cout << "num=" << num << endl;
system("pause");
return 0;
}
1.9.3 值传递
? ? ? ? 所谓值传递,就是函数调用时实参将数值传入给形参
? ? ? ? 值传递时,如果形参发生改变,并不会影响到实参
结论:值传递时,形参的改变并不会影响到实参!!!
????????(这一句结论体现在结果中,首先实参 a b 是 10 和 20,最后打印的结果还是 10 和 20;在函数中形参发生互换!!!但是实参还是没有发生变化)
1.9.4 常见的样式
常见函数的样式有 4 种:
- ? ? ? ? 1. 无参数无返回值
- ? ? ? ? 2. 有参数无返回值
- ? ? ? ? 3. 无参数有返回值
- ? ? ? ? 4. 有参数有返回值
// 函数常见样式
// 1. 无参数无返回值
void test01()
{
cout << "This is test01" << endl;
}
// 2. 有参数无返回值
void test02(int a)
{
cout << "This is test02 a=" <<a<< endl;
}
// 3. 无参数有返回值
int test03()
{
int b = 666;
cout << "This is test03 b=" << b << endl;
return b;
}
// 4. 有参数有返回值
int test04(int x)
{
cout << "This is test04 =" << x << endl;
return x;
}
int main()
{
test01();
test02(66);
test03();
test04(666);
system("pause");
return 0;
}
1.9.5 函数的声明
? ? ? ? 作用:告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
? ? ? ? 函数的声明可以多次,但是函数的定义只能有一次;
所谓函数的声明就是:
? ? ? ? 程序运行时按照顺序上 CPU 运行的,如果上述程序把 Compare 函数放在 main 函数之后,那么程序就会报错!显示 Compare 函数未被定义!!!
? ? ? ? 要解决这一问题,只需要在主函数 main 之上进行对应函数的声明即可!!!
1.9.6 函数的分文件编写
? ? ? ? 作用:让代码结构更加清晰
函数分文件编写一般有 4 个步骤:
? ? ? ? 1. 创建后缀名为 .h 的头文件
? ? ? ? 2. 创建后缀名为 .cpp 的源文件
? ? ? ? 3. 在头文件中写函数的声明
? ? ? ? 4. 在源文件中写函数的定义
1.10 指针
? ? ? ? 指针的作用:可以通过指针间接访问内存
- ? ? ? ? 内存编号是从 0 开始记录的,一般用十六进制数字表示
- ? ? ? ? 可以利用指针变量保存地址
1.10.1 指针的定义和使用
? ? ? ? 指针变量定义语法:数据类型 * 变量名;
? ? ? ? 通过上述代码可以看出:指针 p 其实就是地址;
? ? ? ? 通过上述代码结合上图我们再来深刻的认识什么是指针???首先定义变量 a,int a = 10;相当于在内存空间中开辟一个 4 字节大小内存空间,将这段内存空间起名为 a;
? ? ? ? 定义指针 p;&a 表示拿到变量 a 代表的内存,因为上图我们说到,内存空间是有自己的地址的:0x0000;&a 也就相当于拿到地址 0x0000;指针 p 指向这段地址;
? ? ? ? 所以也可以说指针其实也就是地址!!!
如何使用指针呢 ?
? ? ? ? 可以通过解引用的方式来找到指针指向的内存!!!
? ? ? ? 指针前加上 * 号就表示解引用!!!
1.10.2 指针所占内存空间
这一节主要是解决一个问题 ?
? ? ? ? 指针也是一个数据类型,那么这种数据类型占用多少的内存空间呢?
int *p;
// 我们知道指针指向的内存空间存储的是地址;
// 假设指针 p 指向的内存空间存储的地址是 0x0000;
// 在 C++ 中规定:
// 32位操作系统下,不管什么类型的指针,统一占用 4 个字节大小!!!
// 64位操作系统下,占用 8 个字节大小!!!
1.10.3 空指针
? ? ? ? 空指针:指针变量指向内存中编号为 0 的空间(就是说内存是从编号 0 开始依次向后进行编号的,当有指针指向起始编号为 0 的地址时,我们称它为空指针!)
? ? ? ? 用途:初始化指针变量(简单来说就是一开始你不知道指针应该指向哪里,就先让它指向 NULL,但是一旦到后期有地方可指以后,就让这个指针指向另外的地址!)
? ? ? ? 注意:空指针指向的内存是不可以访问的
int main()
{
// 空指针
// 1. 空指针用于给指针变量进行初始化
int* p = NULL;
// int* p; // 如果我们这样定义一个指针,那么这个指针指向哪里是没有被明确定义的;
// 2. 空指针是不可以进行访问的
*p = 100; // 指针 p 指向的是地址为 0 的空间,这段地址是不可以被赋值的!
// 事实上,0 ~ 255之间的内存编号是系统占用的,因此不可以进行访问!
system("pause");
return 0;
}
1.10.4 野指针
? ? ? ? 野指针:指针变量指向非法的内存空间
int main()
{
// 野指针
int* p = (int*)0x1100; // 这句代码的意思就是:定义一个 int* 类型的指针;
// 让这个指针指向地址 0x1100 对应的地址,但是 0x1100 只是一个十六进制的数
// 现在强制类型转换,把它转换成一个 int* 类型的指针,指针也就代表地址;
// 但是我们再想:0x1100 这个地址并不是我们开辟的,也不是我们申请的,那我们没有权利去调用这段内存上对应的数据
// 因此此时的指针 p 就称为野指针!!!
cout << *p <<endl;
system("pause");
return 0;
}
// 运行这段代码可以发现:系统崩溃!!!
// 在我们的程序中一定要避免出现野指针!!!
1.10.5 const 修饰指针
const 修饰指针有三种情况:
? ? ? ? 1. const 修饰指针 --- 常量指针
? ? ? ? 2. const 修饰变量 --- 指针常量
? ? ? ? 3. const 既修饰指针,又修饰常量
1.10.6 指针和数组
? ? ? ? 作用:利用指针访问数组中的元素
注:整型指针 ++ ,相当于在内存中向后偏移了 4 个字节;
1.10.7 指针和函数
? ? ? ? 作用:利用指针作函数的参数,可以修改实参的值
1.10.8 指针配合数组和函数案例
? ? ? ? 案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序
void Bubble_Sort(int* p,int sz)
{
for (int i = 0; i < sz;i++)
{
for (int j = 0; j < sz - i - 1; j++)
{
if (p[j]>p[j+1])
{
int Temp = p[j];
p[j] = p[j + 1];
p[j + 1] = Temp;
}
}
}
}
int main()
{
// 封装一个函数,利用冒泡排序,实现对整型数组的升序排序
int arr[] = { 6, 5, 3, 2, 9, 4, 7, 1, 8, 0, 10 };
int sz = sizeof(arr) / sizeof(arr[0]);
cout << "排序前数组为:" << endl;
for (int i = 0; i < sz; i++)
{
cout << arr[i] << " ";
}
cout << endl;
Bubble_Sort(arr,sz);
cout << "排序后数组为:" << endl;
for (int i = 0; i < sz; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
1.11 结构体
? ? ? ? 结构体属于用户自定义的数据类型,允许用户存储不同的数据类型(目前我们学习过的数据类型有:整型、浮点型、布尔型、字符型、字符串型等,这些都是系统给我们定义好的类型)
1.11.1 结构体的定义和使用
? ? ? ? 语法:struct 结构体名{ 结构体成员变量 };
通过结构体创建变量的方式有三种:
- ? ? ? ? struct? 结构体名 变量名
- ? ? ? ? struct? 结构体名 变量名 = { 成员1值,成员2值……}
- ? ? ? ? 定义结构体时顺便创建变量
// 创建结构体
// struct 结构体名称
struct Student
{
string name;
int age;
int score;
};
struct Stu
{
string name;
int age;
int score;
}s3; // 创建结构体时直接设置结构体变量
int main()
{
// 结构体创建变量的方式有三种:
// 1. struct? 结构体名 变量名
struct Student s1; // 创建结构体和创建一个变量是类似的,数据类型 + 变量名;其中 struct Student 就是数据类型,s1 是变量名
s1.name = "张三";
s1.age = 20;
s1.score = 99;
cout << "姓名为:" << s1.name << " " << "年龄为:" << s1.age << " " << "分数为: " << s1.score<< " " <<endl;
// 2. struct? 结构体名 变量名 = { 成员1值,成员2值……}
struct Student s2 = { "张三", 20, 99 };
cout << "姓名为:" << s2.name << " " << "年龄为:" << s2.age << " " << "分数为: " << s2.score << " " << endl;
// 3. 定义结构体时顺便创建变量
s3.name = "张三";
s3.age = 20;
s3.score = 99;
cout << "姓名为:" << s3.name << " " << "年龄为:" << s3.age << " " << "分数为: " << s3.score << " " << endl;
system("pause");
return 0;
}
其中,定义结构体时的关键字是 struct,不可以省略!
? ? ? ? ? ?创建结构体变量时,关键字 struct 可以省略!
? ? ? ? ? ?结构体变量利用操作符 “.” 访问成员!
1.11.2 结构体数组
? ? ? ? 作用:将自定义的结构体放入到数组中方便维护
? ? ? ? 语法:struct? 结构体名? 数组名[ 元素个数 ] = { { } ,{ } ……{ } }
// 创建结构体
// struct 结构体名称
struct Student
{
string name;
int age;
int score;
};
int main()
{
// 创建结构体数组
struct Student stuArray[3]=
{
{ "张三", 20, 98},
{ "李四", 21, 99 },
{ "王五", 22, 100 }
};
// 给结构体数组中的元素赋值
// 我们可以通过创建结构体数组的时候就给结构体数组赋值
// 当然也可以是单独的给其赋值
stuArray[2].name = "赵六"; // 修改王五的名字改为赵六
// 遍历结构体数组
for (int i = 0; i < 3;i++)
{
cout << "姓名为:" << stuArray[i].name << " " << "年龄为:" << stuArray[i].age << " " << "分数为:" << stuArray[i].score << " " << endl;
}
system("pause");
return 0;
}
1.11.3 结构体指针
? ? ? ? 作用:通过指针访问结构体中的成员
? ? ? ? 利用操作符 -> 可以通过结构体指针访问结构体属性
1.11.4 结构体嵌套结构体
? ? ? ? 作用:结构体中的成员可以是另一个结构体
? ? ? ? 例如:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体
// 定义学生结构体
struct student
{
string name;
int age;
int score;
};
// 定义一个老师结构体
// 每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体
struct teacher
{
int id; // 教师编号
string name; // 教师姓名
int age; // 年龄
struct student stu; // 老师辅导的学生
};
int main()
{
// 结构体嵌套结构体
struct teacher t;
t.name = "张三";
t.age = 35;
t.id = 223;
t.stu.name = "李四";
t.stu.age = 20;
t.stu.score = 100;
cout << "老师姓名为:" << t.name<< endl;
cout << "老师年龄为:" << t.age<< endl;
cout << "老师ID为:" << t.id<< endl;
cout << "老师辅导的学生姓名为:" << t.stu.name << endl;
cout << "老师辅导的学生年龄为:" << t.stu.age<< endl;
cout << "老师辅导的学生分数为:" << t.stu.score << endl;
system("pause");
return 0;
}
注意:
? ? ? ? 定义学生的结构体一定要定义在老师结构体的上面,因为程序是顺序执行的!一定要保证程序先看到学生结构体;
? ? ? ? 因为老师结构体中的一个成员变量是学生结构体,如果学生结构体不在老师结构体上面,那么程序顺序执行时将显示学生结构体成员变量不存在,进而程序无法运行!!!
1.11.5 结构体做函数参数
? ? ? ? 作用:将结构体作为参数向函数中传递
传递方式:
- ? ? ? ? 值传递
- ? ? ? ? 地址传递
// 定义学生结构体
struct student
{
string name;
int age;
int score;
};
// 1. 值传递
void PrintStudent1(struct student s)
{
cout << "学生的姓名为:" << s.name << endl;
cout << "学生的年龄为:" << s.age << endl;
cout << "学生的分数为:" << s.score << endl;
}
// 2. 地址传递
void PrintStudent2(struct student* s)
{
cout << "学生的姓名为:" << s->name << endl;
cout << "学生的年龄为:" << s->age << endl;
cout << "学生的分数为:" << s->score << endl;
}
int main()
{
// 结构体做函数参数
// 将学生传入到一个参数中,打印学生身上的所有信息
struct student s;
s.name = "张三";
s.age = 20;
s.score = 100;
PrintStudent1(s); // 1. 值传递
PrintStudent2(&s); // 2. 地址传递
system("pause");
return 0;
}
值传递和地址传递的区别:
? ? ? ? 在之前的学习中,我们已经学习过了两者的区别,这里再次回顾一下:
? ? ? ? 值传递过程中,形参的改变不会影响到实参。比如说,我在函数?void PrintStudent1(struct student s) 中修改了年龄大小,但是在 main 函数中打印年龄是不会变化的,如果在函数?void PrintStudent1(struct student s) 中打印年龄才会发送变化!!!
? ? ? ? 而地址传递过程中,形参的改变会影响到实参。重复上述过程,在函数?void PrintStudent2(struct student* s) 中修改年龄的大小,此时不管是在函数?void PrintStudent2(struct student* s) 中打印,还是在 main 函数中打印,年龄都会发生变化!!!
1.11.6 结构体中const使用场景
? ? ? ? 作用:用 const 来防止误操作的出现!!!????????
? ? ? ? 先来分析一下上述代码:上述是采用的值传递,可以发现,值传递过程中形参的改变是不影响实参的;值传递也存在明显的缺点,当值传递所要传递的数据量过大时,会出现过度占用内存空间的现象,因为值传递采用的是拷贝的方式,拷贝占用的内存字节数会过大!!!
? ? ? ? 此时我们采用地址传递的方式:地址传递形参的改变会影响到实参。但是地址传递不会过度占用内存,因为地址 (也就是指针) 只会占用 4 个字节!!!
// 定义学生结构体
struct student
{
string name;
int age;
int score;
};
// 1. 值传递
void PrintStudent1(const struct student* s)
{
// s->age = 100; // 当加上 const 修饰之后该句就会报错!!!
cout << "学生的年龄为:" << s->age << endl;
}
int main()
{
// 结构体做函数参数
// 将学生传入到一个参数中,打印学生身上的所有信息
struct student s;
s.name = "张三";
s.age = 20;
s.score = 100;
PrintStudent1(&s); // 1. 地址传递
cout << "main函数中打印学生的年龄为:" << s.age << endl;
system("pause");
return 0;
}
? ? ? ? 现在用 const 进行修饰,const 修饰起到了限制的作用,此时会发现?s->age = 100; 会报错!!!
? ? ? ? 这也是 const 显著的作用:防止误操作的出现!!!
1.11.7 结构体案例1
案例描述:学校正在做毕设项目,每名老师带领 5 个学生,总共有 3 名老师,需求如下:设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放 5 名学生的数组作为成员,学生的成员有姓名、考试分数,创建数组存放 3 名老师,通过函数给每个老师及所带的学生赋值,最终打印出老师数据以及老师所带的学生数据。
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
/* 学校正在做毕设项目,每名老师带领 5 个学生,总共有 3 名老师,需求如下:设计学生和老师的结构体,其中在老师的结构体中,
有老师姓名和一个存放 5 名学生的数组作为成员,学生的成员有姓名、考试分数,创建数组存放 3 名老师,
通过函数给每个老师及所带的学生赋值,最终打印出老师数据以及老师所带的学生数据。 */
// 创建学生结构体
struct Student
{
string Sname; // 学生姓名
int score; // 学生分数
};
// 创建老师结构体
struct Teacher
{
string Tname; // 老师姓名
struct Student stu[5]; // 学生数组
};
void allocationSpace(struct Teacher tArray[],int len)
{
string Teachername = "ABCDEF";
for (int i = 0; i < len;i++)
{
tArray[i].Tname = "Teacher_";
tArray[i].Tname = tArray[i].Tname + Teachername[i]; // 这两句代码实现往 Teacher_ 后面加上 ABCDEF,拼接
// 给每名老师所带的学生赋值
for (int j = 0; j < 5; j++)
{
tArray[i].stu[j].Sname = "Student_";
tArray[i].stu[j].Sname = tArray[i].stu[j].Sname + Teachername[j]; // 学生名字进行拼接
int Random_Score = rand() % 41 + 60; // 定义随机数 rand() % 41 表示1~40,+60表示61~100分
tArray[i].stu[j].score = Random_Score;
}
}
}
void Print(struct Teacher tArray[],int len)
{
for (int i = 0; i < len; i++)
{
cout << "老师姓名为:" << tArray [i].Tname<< endl;
for (int j = 0; j < 5; j++)
{
cout << "\t学生" << tArray[i].stu[j].Sname << "的分数为:" << tArray[i].stu[j].score << endl;
// \t 为转义字符,对齐,空出 TAB 个字符
}
}
}
int main()
{
// 定义随机数种子,使得每次输出的分数都不一样
srand((unsigned int)time(NULL));
// 1. 创建 3 名老师的数组
struct Teacher tArray[3];
// 2. 通过函数给 3 名老师的信息赋值,并给老师带的学生信息赋值
int len = sizeof(tArray) / sizeof(tArray[0]);
allocationSpace(tArray, len);
// 3. 打印老师和学生的信息
Print(tArray,len);
system("pause");
return 0;
}
1.11.8 结构体案例2
案例描述:设计一个英雄的结构体,包括成员姓名,年龄;创建结构体数组,数组中存放五名英雄。通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果!
五名英雄的信息如下: {"刘备",23,"男"}, {"关羽",22,"男"}, {"张飞",20,"男"}, {"赵云",21,"男"}, {"貂蝉",19,"女"},
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
/* 设计一个英雄的结构体,包括成员姓名,年龄;创建结构体数组,数组中存放五名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果! */
/* 五名英雄的信息如下:
{ "刘备", 23, "男" },
{ "关羽", 22, "男" },
{ "张飞", 20, "男" },
{ "赵云", 21, "男" },
{ "貂蝉", 19, "女" }, */
// 创建英雄结构体
struct Hero
{
string Hreo_Name; // 英雄名字
int Hero_Age; // 英雄年龄
string Hero_Sex; // 英雄性别
};
// 冒泡排序
void BubbleSort(struct Hero tArray[],int len)
{
for (int i = 0; i < len; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (tArray[j].Hero_Age > tArray[j + 1].Hero_Age)
{
struct Hero Temp = tArray[j];
tArray[j] = tArray[j+1];
tArray[j + 1] = Temp;
}
}
}
}
int main()
{
// 1. 创建结构体数组
struct Hero tArray[5]=
{
{ "刘备", 23, "男" },
{ "关羽", 22, "男" },
{ "张飞", 20, "男" },
{ "赵云", 21, "男" },
{ "貂蝉", 19, "女" }
};
int len = sizeof(tArray) / sizeof(tArray[0]);
// 2. 打印原始结构体数组
cout << "排序前:" << endl;
for (int i = 0; i < len;i++)
{
cout << "姓名为:" << tArray[i].Hreo_Name<<" " ;
cout << "年龄为:" << tArray[i].Hero_Age << " ";
cout << "性别为:" << tArray[i].Hero_Sex << endl;
}
cout << "排序后:" << endl;
// 3. 按照年龄进行冒泡排序
BubbleSort(tArray,len);
for (int i = 0; i < len; i++)
{
cout << "姓名为:" << tArray[i].Hreo_Name << " ";
cout << "年龄为:" << tArray[i].Hero_Age << " ";
cout << "性别为:" << tArray[i].Hero_Sex << endl;
}
system("pause");
return 0;
}
1.12 通讯管理系统
? ? ? ? 截止到目前:C++ 的基础知识已经了解了,通过上面的学习,我们已经可以实现一些简单的程序编写了!
? ? ? ? 为了检验我们学习的成果,我们来实现一个通讯录管理系统的编写!!!
1.12.1 系统功能介绍及代码展示
系统中需要实现的功能如下:
? ? ? ? 添加联系人:向通讯录添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录 1000 人
? ? ? ? 显示联系人:显示通讯录中所有联系人信息
? ? ? ? 删除联系人:按照姓名进行删除指定联系人
? ? ? ? 查找联系人:按照姓名查看指定联系人信息
? ? ? ? 修改联系人:按照姓名重新修改指定联系人
? ? ? ? 清空联系人:清空通讯录中所有信息
? ? ? ? 退出通讯录:退出当前使用通讯录
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string> // 用 C++ 风格的字符串需要包含这个头文件
#include <ctime> // time 系统时间头文件包含
using namespace std;
// 用 C++ 编写一个通讯录管理系统
// 添加联系人:向通讯录添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录 1000 人
// 显示联系人:显示通讯录中所有联系人信息
// 删除联系人:按照姓名进行删除指定联系人
// 查找联系人:按照姓名查看指定联系人信息
// 修改联系人:按照姓名重新修改指定联系人
// 清空联系人:清空通讯录中所有信息
// 退出通讯录:退出当前使用通讯录
#define MAX 1000 // 宏定义定义通讯录的最大存在人数
// 菜单函数
void showMenu()
{
cout << "***************************" << endl;
cout << "***** 1、添加联系人 *****" << endl;
cout << "***** 2、显示联系人 *****" << endl;
cout << "***** 3、删除联系人 *****" << endl;
cout << "***** 4、查找联系人 *****" << endl;
cout << "***** 5、修改联系人 *****" << endl;
cout << "***** 6、清空联系人 *****" << endl;
cout << "***** 0、退出通讯录 *****" << endl;
cout << "***************************" << endl;
}
// 退出功能
// 根据用户不同的选择,进入不同的功能,选用switch分支结构,将整个架构进行搭建
// 当用户选择0的时候,执行退出,选择其他先不做操作,也不会退出程序
// 这一步骤在main函数中实现
// 联系人结构体
struct People
{
string m_Name; // 姓名
int m_Sex; // 这里之所以不用string,是因为可能除啦男和女还有中性!
int m_Age; // 年龄
string m_Phone; // 电话
string m_Address; // 家庭住址
};
// 通讯录结构体
struct AddressBooks
{
struct People PeopleAddress[MAX]; // 定义结构体数组记录通讯录可以存在的最大人数
int m_Size; // 定义变量记录当前通讯录的人数
};
// 添加联系人
void AddPeople(struct AddressBooks* Chat)
{
if (Chat->m_Size==MAX) // 如果通讯录已满
{
cout << "通讯录已满,添加失败!" << endl;
}
else
{
// 添加姓名
cout << "请输入姓名:" << endl;
string name;
cin >> name; // 自己定义姓名
Chat->PeopleAddress[Chat->m_Size].m_Name = name; // 这里添加的数组个数为当前通讯录的人数
// 性别
cout << "请输入性别:" << endl;
cout << "1 --- 男" << endl;
cout << "2 --- 女" << endl;
int sex;
while (true) // while循环保证性别只能是男和女
{
cin >> sex;
if (sex == 1 || sex == 2)
{
Chat->PeopleAddress[Chat->m_Size].m_Sex = sex;
break;
}
cout << "请重新选择:" << endl;
}
// 年龄
cout << "请输入年龄:" << endl;
int age;
while (true)
{
cin >> age;
if (age>0 && age<150) // 同样保证年龄只能在0~150
{
Chat->PeopleAddress[Chat->m_Size].m_Age = age;
break;
}
cout << "请重新输入:" << endl;
}
// 电话
cout << "请输入电话:" << endl;
string Telephone;
cin >> Telephone;
Chat->PeopleAddress[Chat->m_Size].m_Phone = Telephone;
// 家庭住址
cout << "请输入家庭住址:" << endl;
string Address;
cin >> Address;
Chat->PeopleAddress[Chat->m_Size].m_Address = Address;
}
cout << "添加成功!" << endl;
// 通讯录人数加一,方便结构体数组进行下一个人信息的录入
Chat->m_Size++;
system("pause"); // 按任意键继续
system("cls"); // 清屏
}
// 显示联系人
void ShowPeople(AddressBooks* Chat)
{
if (Chat->m_Size == 0)
{
cout << "通讯录记录为0!" << endl;
}
else
{
for (int i = 0; i < Chat->m_Size; i++)
{
// 这里解释两点:
// 1. 之所以用 \t 转义字符,是因为如果每天信息都换行的话,那么当人数过多时,显示起来会很复杂,所以使用转移字符,空出 8 个字符大小的间隙
// 2. 在打印性别时:之所以使用三目操作符(Chat->PeopleAddress[i].m_Sex == 1 ? "男" : "女"),是因为我们输入性别时定义的是 1 2,分别表示男女
// 那么打印时会打印出 1 或者 2,为了使性别变成男或者女,这里使用三目操作符;加括号是为了提高运算优先级
cout << "姓名:" << Chat->PeopleAddress[i].m_Name << "\t";
cout << "性别:" << (Chat->PeopleAddress[i].m_Sex == 1 ? "男" : "女") << "\t";
cout << "年龄:" << Chat->PeopleAddress[i].m_Age << "\t";
cout << "电话:" << Chat->PeopleAddress[i].m_Phone << "\t";
cout << "住址:" << Chat->PeopleAddress[i].m_Address << endl;
}
}
system("pause");
system("cls");
}
// 检测联系人
// 因为后续不管是删除联系人还是查找、修改联系人都需要先找到这个联系人
// 如果联系人存在,返回联系人 所在数组的具体位置;如果不存在返回 -1;
int IsExist(AddressBooks* Chat,string name) // 参数1:通讯录;参数2:通过姓名检测联系人
{
for (int i = 0; i < Chat->m_Size;i++)
{
if (Chat->PeopleAddress[i].m_Name == name)
{
return i; // 找到这个人了,返回这个人在结构体数组中的位置
}
}
return -1; // 遍历整个结构体数组都没有找到这个姓名,返回-1;
}
// 删除联系人
void DeletePeople(AddressBooks* Chat)
{
cout << "请输入要删除的联系人姓名:" << endl;
string name;
cin >> name;
int ret = IsExist(Chat,name);
if (ret!=-1)
{
// 找到了
for (int i = ret; i < Chat->m_Size;i++) // 因为ret记录的是要删除人所在结构体数组的下角标
// 所以从这个下角标开始往后遍历;删除就是让后续的数据依次向前覆盖
// 例如:1 2 3 4 5 6,要删除4,只需要让5覆盖4的位置,让6覆盖5的位置;最后让数据-1即可;
{
Chat->PeopleAddress[i] = Chat->PeopleAddress[i + 1]; // 让结构体数组后一个数据覆盖前一个数据
}
Chat->m_Size--; // 人数--
cout << "删除成功!" << endl;
}
else
{
cout << "查无此人!" << endl;
}
system("pause");
system("cls");
}
// 查找联系人
void SearchPeople(AddressBooks* Chat)
{
cout << "请输入要查找的联系人姓名:" << endl;
string name;
cin >> name;
int ret = IsExist(Chat, name);
if (ret != -1)
{
cout << "姓名:" << Chat->PeopleAddress[ret].m_Name << "\t";
cout << "性别:" << (Chat->PeopleAddress[ret].m_Sex==1?"男":"女") << "\t";
cout << "年龄:" << Chat->PeopleAddress[ret].m_Age << "\t";
cout << "电话:" << Chat->PeopleAddress[ret].m_Phone << "\t";
cout << "住址:" << Chat->PeopleAddress[ret].m_Address << endl;
}
else
{
cout << "查无此人!" << endl;
}
system("pause");
system("cls");
}
// 修改指定的联系人信息
void ModifyPeople(AddressBooks* Chat)
{
cout << "请输入要修改的联系人姓名:" << endl;
string name;
cin >> name;
int ret = IsExist(Chat, name);
if (ret != -1)
{
// 姓名
string name;
cout << "请输入姓名:" << endl;
cin >> name;
Chat->PeopleAddress[ret].m_Name = name;
// 性别
cout << "请输入性别:" << endl;
cout << "1 --- 男" << endl;
cout << "2 --- 女" << endl;
int sex;
while (true) // while循环保证性别只能是男和女
{
cin >> sex;
if (sex == 1 || sex == 2)
{
Chat->PeopleAddress[ret].m_Sex = sex;
break;
}
cout << "请重新选择:" << endl;
}
// 年龄
cout << "请输入年龄:" << endl;
int age;
while (true)
{
cin >> age;
if (age>0 && age<150) // 同样保证年龄只能在0~150
{
Chat->PeopleAddress[ret].m_Age = age;
break;
}
cout << "请重新输入:" << endl;
}
// 电话
cout << "请输入电话:" << endl;
string Telephone;
cin >> Telephone;
Chat->PeopleAddress[ret].m_Phone = Telephone;
// 家庭住址
cout << "请输入家庭住址:" << endl;
string Address;
cin >> Address;
Chat->PeopleAddress[ret].m_Address = Address;
cout << "修改成功!" << endl;
}
else
{
cout << "查无此人!" << endl;
}
system("pause");
system("cls");
}
// 清空联系人
void CleanPeople(AddressBooks* Chat)
{
// 清空联系人比较简单,只需要逻辑上将通讯录的人数设置为 0 即可,访问不到联系人即可!
cout << "请慎重考虑是否清空联系人!" << endl;
cout << "确定清空请输入:1" << endl; // 判断是否真正的清空!
int num = 0;
while (true)
{
cin >> num;
if (num == 1)
{
Chat->m_Size = 0;
cout << "通讯录已清空!" << endl;
}
cout << "请输入正确的请求!" << endl;
}
system("pause");
system("cls");
}
int main()
{
// 创建结构体变量
AddressBooks Chat;
// 初始化通讯录当前的人员个数
Chat.m_Size = 0;
int Select = 0;
while (true) // 这里不要认为while(true)是死循环,永远退不出循环!当按下0时会退出程序
// 这里之所以使用while循环,是为了保证按下 123456 的时候不会执行完对应程序就退出
// 简单来说就是我添加完联系人之后不会退出程序,while循环会使得完成 case 1之后重新输入Select;进行后续的功能使用!
{
showMenu();
cin >> Select;
switch (Select)
{
case 1: // 1、添加联系人
AddPeople(&Chat); // 通过之前的学习,我们知道值传递形参的修改是不影响实参的,因此这个我们采用地址传递,通过形参的修改去影响具体的实参!
break;
case 2: // 2、显示联系人
ShowPeople(&Chat);
break;
case 3: // 3、删除联系人
//{ // 当switch后面代码比较多时,此时switch会报错,加上大括号,表示下述代码是一个代码段;
// cout << "请输入联系人的姓名:" << endl;
// string name;
// cin >> name;
// if (IsExist(&Chat, name) == -1)
// {
// cout << "查无此人!" << endl;
// }
// else
// {
// cout << "找到此人!" << endl;
// }
//}
DeletePeople(&Chat);
break;
case 4: // 4、查找联系人
SearchPeople(&Chat);
break;
case 5: // 5、修改联系人
ModifyPeople(&Chat);
break;
case 6: // 6、清空联系人
CleanPeople(&Chat);
break;
case 0: // 0、退出通讯录
cout << "欢迎下次使用!" << endl;
system("pause"); // 退出提示按任意键继续!!!
return 0;
break;
default:
break;
}
}
system("pause"); // 按任意键继续!
return 0;
}
上述代码基本上做了合理的注释,如果还有哪里不明白或者书写有误,欢迎留言更正!!!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!