C++[面向对象的程序设计]_基础入门(上)(万字总结)(建议收藏!!!)

目录

1. C++基础入门

1.1 变量 

1.2 常量

1.3 关键字

1.4 标识符命名规则

1.5 数据类型

1.5.1 整型

1.5.2 sizeof 关键字

1.5.3 实型(浮点型)

1.5.4 字符型

1.5.5 转义字符

1.5.6 字符串类型

1.5.7 布尔类型

1.5.8 数据的输入

1.6 运算符

1.6.1 算术运算符 - 加减乘除运算

1.6.2 算术运算符 - 取模运算

1.6.3 算术运算符 - 递增递减

1.6.4 赋值运算符

1.6.5 比较运算符

1.6.6 逻辑运算符 - 非

1.6.7 逻辑运算符 - 与

1.6.8 逻辑运算符 - 或

1.7 程序流程结构

1.7.1 选择结构_单行if语句

1.7.2 选择结构_多行if语句

1.7.3 选择结构_多条件if语句

1.7.4 选择结构_嵌套if语句

1.7.5 选择结构案例_三只小猪称体重

1.7.6 选择结构案例_三目运算符

1.7.7 选择结构_switch语句

1.7.8 循环结构_while语句

1.7.9 循环结构案例_猜数字

1.7.10 循环结构_dowhile语句

1.7.11 循环结构案例_水仙花数

1.7.12 循环结构_for循环

1.7.13 循环结构案例_敲桌子

1.7.14 嵌套循环

1.7.15 嵌套循环案例_乘法口诀表

1.7.16 跳转语句_break语句

1.7.17 跳转语句_continue

1.7.18 跳转语句_goto

1.8 数组

1.8.1 一维数组定义方式

1.8.2 一维数组 - 数组名

1.8.3 一维数组案例 - 五只小猪称体重

1.8.4 一维数组案例 - 元素逆置

1.8.5 一维数组 - 冒泡排序

1.8.6 二维数组定义方式

1.8.7 二维数组 - 数组名

1.8.8 二维数组案例 - 考试成绩统计

1.9 函数

1.9.1 函数的定义

1.9.2 函数的调用

1.9.3 值传递

1.9.4 常见的样式

1.9.5 函数的声明

1.9.6 函数的分文件编写

1.10 指针

1.10.1 指针的定义和使用

1.10.2 指针所占内存空间

1.10.3 空指针

1.10.4 野指针

1.10.5 const 修饰指针

1.10.6 指针和数组

1.10.7 指针和函数

1.10.8 指针配合数组和函数案例

1.11 结构体

1.11.1 结构体的定义和使用

1.11.2 结构体数组

1.11.3 结构体指针

1.11.4 结构体嵌套结构体

1.11.5 结构体做函数参数

1.11.6 结构体中const使用场景

1.11.7 结构体案例1

1.11.8 结构体案例2

1.12 通讯管理系统

1.12.1 系统功能介绍及代码展示


        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字节                                                                          (-2^{15} ~ 2^{15}-1

        int(整型)                        4字节                                                                          (-2^{31} ~ 2^{31}-1

        long(长整型)        Windows为4字节,Linux为4字节(32位OS),8字节(64位)  (-2^{31} ~ 2^{31}-1

        long long(长长整型)      8字节                                                                          (-2^{63} ~ 2^{63}-1

        我们知道数据类型本质的区别是:占用空间内存的大小不同!!!现在我们修改存储数据的大小,让其越界。比如说:短整型的范围是 -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 优点:结构清晰,执行效率高!

注意1switch语句中表达式类型只能是整型或者字符型

注意2case里如果没有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^{3}+5^{3}+3^{3}=153

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;
}

上述代码基本上做了合理的注释,如果还有哪里不明白或者书写有误,欢迎留言更正!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/223857.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ps快捷键和常见项目总结

处理以像素构成的位图的软件 Mac笔记本快捷键&#xff1a; 打开文件&#xff1a;commandO 图像缩放&#xff1a;command 多个文件切换&#xff1a;同一桌面中&#xff1a;command (英文状态下输入) 切换屏幕模式&#xff1a;F,全屏模式下Tab键可进行切换 首选项—性能&a…

python/matlab图像去雾/去雨综述

图像去雾和去雨是计算机视觉领域的两个重要任务&#xff0c;旨在提高图像质量和可视化效果。本文将综述图像去雾和去雨的算法、理论以及相关项目代码示例。 一、图像去雾算法 基于暗通道先验的方法&#xff1a; 这是广泛应用于图像去雾的经典算法之一。该方法基于一个观察&…

SpringMVC修炼之旅(2)基础入门

一、第一个程序 1.1环境配置 略 1.2代码实现 package com.itheima.controller;import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;//定义…

MySQL联合查询、最左匹配、范围查询导致失效

服务器版本 客户端&#xff1a;navicat premium16.0.11 联合索引 假设有如下表 联合索引就是同时把多列设成索引&#xff0c;如(empno&#xff0c;ename)在查询的时候就会先按照empno进行查询&#xff0c;再按照ename进行查询其中empno是全局有序&#xff0c;ename是局部有…

【银行测试】支付类测试关注点与异常点+支付平台...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、支付类的测试关…

Django回顾 - 6 Ajax

【1】Ajax 定义&#xff1a; 异步Javscript和XML 作用&#xff1a; Javascript语言与服务器(django)进行异步交互&#xff0c;传输的数据为XML&#xff08;当然&#xff0c;传输的数据不只是XML,现在更多使用json数据&#xff09; 同步交互和异步交互&#xff1a; 1、同步交互&…

Web server failed to start. Port 8081 was already in use.

netstat -aon|findstr "8081" taskkill /pid 20824 /f

现代雷达车载应用——第2章 汽车雷达系统原理 2.1节

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.1 基本雷达功能 雷达系统通过天线或天线阵列向空间辐射电磁能量。辐射的电磁能量“照亮”周围的目标。“被照亮”的目标拦截一些辐射能量&#xff0…

开发步骤、Java开发工具

目录 一、开发步骤 二、Java开发工具 JDK安装完毕&#xff0c;我们就可以开始开发第一个Java程序了&#xff0c;习惯性的成为HelloWorld。 一、开发步骤 Java程序开发三步骤&#xff1a;编写、编译、运行 -将Java代码编写到扩展名为.java的源文件中 -通过javac.exe命令对…

Kontakt v7.7.2(音频采样器)

Native Instruments Kontakt 7是一款强大的软件采样器&#xff0c;它允许用户从各种来源采样音频并进行编辑和处理。它包含大量预设采样库&#xff0c;包括乐器、合成器、鼓组和声音效果等。此外&#xff0c;Kontakt 7还允许用户创建自己的采样库&#xff0c;以便根据自己的需要…

CTF特训日记day7

复现华为杯研究生国赛的adv_lua题目 从题目描述来看&#xff0c;漏洞应该和bytearray相关 用IDA逆向一下然后直接字符串搜索bytearray 只有这里有bytearray字样&#xff0c;继续查找交叉引用&#xff1a; 可以看到一系列方法&#xff0c;显然都是为bytearray所注册的吗&am…

Xilinx FPGA平台DDR3设计详解(三):DDR3 介绍

本文介绍一下常用的存储芯片DDR3&#xff0c;包括DDR3的芯片型号识别、DDR3芯片命名、DDR3的基本结构等知识&#xff0c;为后续掌握FPGA DDR3的读写控制打下坚实基础。 一、DDR3芯片型​号 电路板上的镁光DDR3芯片上没有具体的型号名。 ​如果想知道具体的DDR3芯片型号&#…

电力智慧运维系统

电力智慧运维系统是以提高用户侧电力运行安全&#xff0c;降低运维成本为目标&#xff1b;采用智能化运维管理工具—“电易云”&#xff0c;帮助企业建立电力运维体系全方位的信息化、数字化平台&#xff0c;实现设备运行的数字化在线监控与线下维护处理的有机融合&#xff0c;…

【海思SS528 | VO】MPP媒体处理软件V5.0 | VO模块编程总结

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Netty中Channel的isWritable方法理解

目录 初见 深入 需要注意 对待超SIZE情况开源项目怎么做 1、seata中 2、SUMMER中 3、一些资料中 总结 初见 以下是包中注释 Returns true if and only if the I/O thread will perform the requested write operation immediately. Any write requests made when t…

如何解决syntaxerror: more than 255 arguments 报错

如何解决syntaxerror: more than 255 arguments 报错 问题背景解释解决方案 问题背景 今天拼接特征的时候&#xff0c;突然代码报错syntaxerror: more than 255 arguments &#xff0c;看了一下感觉这个报错非常有意思&#xff0c;估计平时也是没机会碰到&#xff0c;和大家分…

线程基础介绍

什么是线程&#xff1f; linux内核中是没有线程这个概念的&#xff0c;而是轻量级进程的概念&#xff1a;LWP。一般我们所说的线程概念是C库当中的概念。 1.1线程是怎样描述的&#xff1f; 线程实际上也是一个task_struct&#xff0c;工作线程拷贝主线程的task_struct&#…

推出ASM2824 PCIe交换机,预计M.2插槽将增加,E3SB40E00004EE、E2SB40E00000JE 40MHz晶振

一、ASM2824 PCIe交换机&#xff0c;预计M.2插槽将增加 ASM2824 PCI-Express Gen 3.0 x24交换机采用PCI-Express 3.0 x8&#xff0c;并提供四个PCI-Express 3.0 x4连接&#xff0c;其结构为PLX PEX8747的一半。从理论上讲&#xff0c;这将允许主板设计人员从英特尔Z390芯片组的…

C++类和对象——(8)类的组合

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 人生就像骑单车&#xff0c;想保持平衡…

香港科技大学广州|机器人与自主系统学域博士招生宣讲会—北京专场!!!(暨全额奖学金政策)

在机器人和自主系统领域实现全球卓越—机器人与自主系统学域 硬核科研实验室&#xff0c;浓厚创新产学研氛围&#xff01; 教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01; 一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; 时间&#xff1a;2023年12月09日…