[C.C++] [C++]一、C++基础编程

388 0
Honkers 2025-3-5 18:58:20 | 显示全部楼层 |阅读模式
<p>G:\Cpp\2023版C&#43;&#43;教程</p>
<p style="margin-left:.0001pt;text-align:center;">C&#43;&#43;语言程序设计</p>
<h2 style="text-align:justify;"><strong><strong><strong>第一部分</strong></strong><strong> </strong><strong><strong>基础篇</strong></strong></strong></h2>
<h3 style="text-align:justify;"><strong><strong><strong>一、什么是</strong></strong><strong><strong>C&#43;&#43;</strong></strong></strong></h3>
<h4 style="background-color:transparent;text-align:justify;"><strong><strong><strong>1.1 C&#43;&#43; </strong></strong><strong><strong>简介</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43; 是一门非常经典的高级编程语言。顾名思义&#xff0c;C&#43;&#43;可以看做是C语言的增强版&#xff0c;在C的基础上扩展了更多的功能&#xff1b;最主要的扩展&#xff0c;就是面向对象和泛型编程。</p>
<p style="margin-left:.0001pt;text-align:justify;">因此C&#43;&#43;融合了多种不同的编程方式&#xff1a;以C语言为代表的面向过程编程&#xff1b;面向对象编程&#xff1b;以及模板化的泛型编程。</p>
<p style="margin-left:.0001pt;text-align:justify;">可以说&#xff0c;C&#43;&#43;一门“大而全”的编程语言&#xff0c;你可以用它实现想要的任何功能&#xff1b;与此同时&#xff0c;学习C&#43;&#43;需要掌握的内容也会比较多。</p>
<h5 style="background-color:transparent;text-align:justify;"><strong><strong><strong>1.1.1 C</strong></strong><strong><strong>和</strong></strong><strong><strong>C&#43;&#43;</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">20世纪70年代&#xff0c;贝尔实验室的Dennis Ritchie为了开发UNIX操作系统&#xff0c;专门设计了一门结构化的高级语言&#xff0c;这就是大名鼎鼎的C语言。因为是为操作系统设计的语言&#xff0c;它本身是比较底层的&#xff0c;所以C具有低级语言的高运行效率、硬件访问能力&#xff0c;此外又融合了高级语言的通用性。</p>
<p style="margin-left:.0001pt;text-align:justify;">C语言语法清晰&#xff0c;具有非常好的结构化编程的特性。于是C语言快速地统治了底层的系统级编程&#xff0c;并成为了之后几十年内经典的教学语言。</p>
<p style="margin-left:.0001pt;text-align:justify;">C语言编程的整体思路是“过程式”的&#xff0c;也就是说&#xff0c;我们把想让计算机执行的操作按照步骤一步步定义好&#xff0c;然后用C语言写出来&#xff1b;所以我们写的代码&#xff0c;就是一个处理流程的描述。这种方式很容易理解&#xff0c;也可以非常方便地翻译成计算机能懂的机器语言&#xff1b;但是在面对大型项目、代码量非常大时&#xff0c;就会显得杂乱无章&#xff0c;代码的可读性就大大降低了。</p>
<p style="margin-left:.0001pt;text-align:justify;">于是另一种编程方式应运而生&#xff0c;这就是面向对象编程。这种方式的主要思路是先构建“对象”&#xff0c;然后通过定义好的对象行为&#xff0c;实现我们想要的操作。</p>
<p style="margin-left:.0001pt;text-align:justify;">贝尔实验室的 Bjarne Stroustrup&#xff08;比雅尼·斯特劳斯特鲁普&#xff09;&#xff0c;在20世纪80年代创建了一个新的面向对象语言——C&#43;&#43;。</p>
<p style="margin-left:.0001pt;text-align:justify;">名字一目了然&#xff0c;它是基于C的&#xff0c;扩展了C的功能&#xff1b;所以C&#43;&#43;是C语言的超集&#xff0c;所有C语言程序都可以在C&#43;&#43;的环境下运行。而扩展的部分&#xff0c;主要就是引入了面向对象的特性&#xff0c;并实现了对C的泛型编程支持。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;的出现极大地扩充了C的应用场景&#xff0c;为C语言的长盛不衰提供了很大的助力。所以我们平常看招聘要求的技术栈描述&#xff0c;往往是把C/C&#43;&#43;放在一起说的。</p>
<h5 style="text-align:justify;"><strong><strong><strong>1.1.2 C&#43;&#43; </strong></strong><strong><strong>的应用场景</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;完全兼容C&#xff0c;具有C面向硬件的特性&#xff1b;此外还拥有面向对象和泛型编程的扩展。所以C&#43;&#43;编写的程序运行效率高、功能强大&#xff0c;特别适合用在系统级应用场景上。所以我们经常可以看到&#xff0c;偏向底层、系统的开发&#xff0c;一般用的语言都是C&#43;&#43;。</p>
<ol><li>底层硬件&#xff0c;系统编程&#xff1a;JVM的底层&#xff0c;Python解释器的底层&#xff0c;都离不开C/C&#43;&#43;的身影&#xff1b;人工智能核心库的代码&#xff0c;也大多是C&#43;&#43;写的</li><li>嵌入式开发</li><li>游戏开发</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;除了这些实际应用场景外&#xff0c;由于C/C&#43;&#43;是经典的教学语言&#xff0c;因此计算机专业考研、考级、竞赛等场合往往也是把C&#43;&#43;作为第一语言的。无论学习还是工作&#xff0c;C&#43;&#43;都是一门非常有用的编程语言。</p>
<h4 style="text-align:justify;"><strong><strong><strong>1.2 C&#43;&#43; </strong></strong><strong><strong>标准</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;作为一门高级编程语言&#xff0c;在不同的硬件平台上有着良好的可移植性。这意味着我们不需要改动代码&#xff0c;写出来的程序就可以在不同的平台“翻译”成机器能读懂的语言。要实现这个目标&#xff0c;就必须对C&#43;&#43;编写的程序设定一些规范&#xff0c;这就是C&#43;&#43;的标准。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;之父Stroustrup写过一本《C&#43;&#43;编程语言》&#xff08;The C&#43;&#43; Programming Language&#xff09;&#xff0c;里面有一个参考手册&#xff0c;专门介绍了这门语言的特性和用法。这其实就是最初的C&#43;&#43;事实标准。</p>
<p style="margin-left:.0001pt;text-align:justify;">不过真正意义上的标准&#xff0c;还需要专门的组织认证。ANSI&#xff08;American National Standards Institute&#xff0c;美国国家标准局&#xff09;在制定了C语言标准之后&#xff0c;在90年代专门设了一个委员会来制定C&#43;&#43;的标准&#xff0c;并和ISO&#xff08;国际标准化组织&#xff09;一起创建了联合组织ANSI/ISO。1998年&#xff0c;第一个C&#43;&#43;国际标准终于出炉了&#xff1b;这个标准在2003年又做了一次技术修订。因此我们一般所说的C&#43;&#43;标准&#xff0c;第一版往往被叫做C&#43;&#43; 98/03。</p>
<p style="margin-left:.0001pt;text-align:justify;">跟大多数语言一样&#xff0c;C&#43;&#43;也在不停地发展更新。ISO在2011年批准了C&#43;&#43;新标准&#xff0c;这可以认为是C&#43;&#43;的2.0版本&#xff0c;一般被叫做C&#43;&#43; 11。C &#43;&#43; 11新增了很多新特性&#xff0c;极大地扩展了C&#43;&#43;的语言表达能力。此后在2014年和2017年&#xff0c;又出了两个新版本C&#43;&#43;标准&#xff0c;一般叫做C&#43;&#43; 14和C&#43;&#43; 17&#xff0c;不过这两个版本增加的内容并不多&#xff1b;真正意义上的下一个大版本是2020年的C&#43;&#43; 20&#xff0c;它再一次给C&#43;&#43;带来了大量的新特性。</p>
<h4 style="text-align:justify;"><strong><strong><strong>1.3 C&#43;&#43; </strong></strong><strong><strong>代码如何运行</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">我们用C&#43;&#43;写好的代码&#xff0c;其实就是符合特定语法规则的一些文字和符号。计算机是怎样识别出我们想要做的操作、并正确执行呢&#xff1f;</p>
<p style="margin-left:.0001pt;text-align:justify;">这就需要一个专门的翻译程序&#xff0c;把我们写的源代码&#xff0c;翻译成计算机能理解的机器语言。这个翻译的过程就叫做“编译”&#xff0c;而这个“翻译官”就叫做编译器。所以C&#43;&#43;是一门编译型的编程语言&#xff0c;这一点和C是一致的。</p>
<p style="margin-left:.0001pt;text-align:justify;">事实上&#xff0c;C&#43;&#43;代码的运行过程跟C程序代码也是一样的&#xff0c;大致可以分为下面几步&#xff1a;</p>
<ol><li>首先编写C&#43;&#43;程序&#xff0c;保存到文件中&#xff0c;这就是我们的源代码&#xff1b;</li><li>编译。用C&#43;&#43;编译器将源代码编译成机器语言&#xff0c;得到的这个结果叫做目标代码&#xff1b;</li><li>链接。C/C&#43;&#43;程序一般都会用到库&#xff08;library&#xff09;&#xff0c;这些库是已经实现好的目标代码&#xff0c;可以实现特定的功能&#xff08;比如在屏幕上把信息打印显示出来&#xff09;。这时我们就需要把之前编译好的目标代码&#xff0c;和所用到的库里的目标代码&#xff0c;组合成一个真正能运行的机器代码。这个过程叫做“链接”&#xff0c;得到的结果叫做可执行代码&#xff1b;</li><li>运行。可执行代码就是可以直接运行的程序&#xff0c;运行它就可以执行我们想要的操作了。</li></ol>
<p style="margin-left:.0001pt;text-align:left;"></p>
<h3 style="text-align:justify;"><strong><strong><strong>二、简单上手——</strong></strong><strong><strong>Hello World</strong></strong></strong></h3>
<h4 style="text-align:justify;"><strong><strong><strong>2.1 </strong></strong><strong><strong>开发环境和工具&#xff08;</strong></strong><strong><strong>Visual Studio</strong></strong><strong><strong>&#xff09;</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">写C&#43;&#43;程序其实很简单&#xff0c;直接用记事本写好代码&#xff0c;然后用一个编译器做编译运行就可以了&#xff1b;不过这意味这我们得自己保证语法正确&#xff0c;严重影响开发效率。所以实际应用中我们一般都会使用功能更强大的工具&#xff0c;除了提供编译器外&#xff0c;还可以给我们做语法检查和提醒&#xff0c;方便我们调试程序——这就是所谓的“集成开发环境”&#xff08;IDE&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">Windows系统环境下&#xff0c;最普遍、最好用的IDE就是Visual Studio了&#xff0c;这是微软官方的开发工具&#xff0c;功能非常强大。</p>
<p style="margin-left:.0001pt;text-align:justify;">打开Visual Studio的中文版官方网站 Visual Studio: 面向软件开发人员和 Teams 的 IDE 和代码编辑器&#xff0c;点击“下载Visual Studio”按钮&#xff0c;选择最新的免费社区版Community 2022。然后双击运行安装程序VisualStudioSetup.exe。</p>
<p style="margin-left:.0001pt;text-align:justify;">在安装引导程序中&#xff0c;选择自己需要的组件。我们直接选择“使用C&#43;&#43;的桌面开发”即可&#xff0c;这个选项会打包安装Windows下C&#43;&#43;开发的所有组件。注意不需要选“通用Windows平台开发”&#xff0c;这个还包含了.net平台&#xff0c;是针对C#开发的。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/b6c788c4fca44f2290ce22ef54c139a3.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">点击“安装”&#xff0c;引导程序会自动帮我们下载和安装所有需要的组件&#xff0c;这个过程可能需要花费一些时间。</p>
<p style="margin-left:.0001pt;text-align:justify;">如果选择了“安装后启动”&#xff0c;那么安装完成就会自动运行。开始的界面是登录微软账号&#xff0c;我们可以直接跳过。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/90ee7c43d6be425290f756d4dc1fd40b.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">择开发设置为“Visual C&#43;&#43;”&#xff0c;选择自己喜欢的界面主题色&#xff0c;然后点击启动。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/d2c624a882444ca5bfb920d221164ef5.png"  />
</p>
<h4 style="text-align:justify;"><strong><strong><strong>2.2 </strong></strong><strong><strong>写一个</strong></strong><strong><strong>Hello World</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">Visual Studio启动之后&#xff0c;我们首先应该创建一个项目。所谓“项目”&#xff0c;就是一个工作任务&#xff0c;需要实现相应的需求。点击“创建新项目”。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/2409fdb840df4712a5b326f25dfe37ae.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">直接选择一个空项目。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/587f83ac8ca9490eaca48f4af6d41f62.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">指定项目名称和保存位置。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/62c1966409404bcf9e7749615a307966.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">这里还有一个“解决方案”&#xff08;Solution&#xff09;的概念&#xff0c;其实就是一组有关联的项目&#xff0c;共同合作解决一个需求。</p>
<h5 style="text-align:justify;"><strong><strong><strong>2.2.1 </strong></strong><strong><strong>代码编写</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在打开的解决方案界面里&#xff0c;右键点击“源文件”文件夹图标&#xff0c;添加一个新建项。我们要添加的是一个C&#43;&#43;文件&#xff0c;命名为HelloWorld&#xff0c;后缀名是.cpp。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/1e618114ac2d4cc2a139083188a3d39f.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">下面就是一段最简单的代码&#xff0c;我们在屏幕上输出Hello World。</p>

<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">std::cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; std::endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">我们可以点击工具栏的按钮 </p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/3fdf7e8b28ca405c813291fd769e7de0.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;快捷键F5&#xff09;&#xff0c;用一个本地的调试器来“调试”代码&#xff1b;所谓的调试&#xff0c;就是查看具体的运行过程&#xff0c;我们可以用它来解决出现的问题。当然也可以点它旁边的三角按钮&#xff0c;这是不调试直接运行&#xff08;快捷键Ctrl&#43;F5&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">结果如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/eaf5ef42dabc482293581cdb842d94dd.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">界面上弹出了一个窗口&#xff0c;显示出了我们想要的信息“Hello World&#xff01;”。后面还跟着一串信息&#xff0c;这是调试控制台告诉我们&#xff0c;程序已经执行完毕正常退出了。随便一个键&#xff0c;就可以关闭这个窗口。</p>
<h5 style="text-align:justify;"><strong><strong><strong>2.2.2 </strong></strong><strong><strong>代码解读</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">这个简单的程序里&#xff0c;主要包括了这样几部分。</p>
<ol><li>第一行 #include&lt;iostream&gt;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">这是一个预处理指令&#xff0c;告诉编译器我们需要使用一个叫做iostream的库。因为我们需要输出信息&#xff0c;而系统的标准库提供了这样的功能&#xff0c;所以要用#include做一个引入的预处理。</p>
<ol><li>主函数 main()</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">接下来的主体&#xff0c;是一个“主函数”。</p>
<p style="margin-left:.0001pt;text-align:justify;">所谓的函数&#xff0c;就是包装好的一系列要执行的操作&#xff0c;可以返回一个结果。一个C&#43;&#43;程序可以包含很多函数&#xff0c;其中一个必须叫做main&#xff0c;它是执行程序的入口。也就是说&#xff0c;当我们运行这个程序的时候&#xff0c;操作系统就会找到这个“主函数”开始执行。</p>
<p style="margin-left:.0001pt;text-align:justify;">main() 的定义形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;"><em>statements</em></p>
<p style="margin-left:.0001pt;text-align:left;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">具体细分&#xff0c;第一行int main()叫做函数头&#xff0c;下面的花括号扩起来的部分叫函数体。函数头定义了函数的名字叫main&#xff0c;前面的int表示返回值是整数类型&#xff08;integer&#xff09;&#xff1b;后面的括号里面本应该写传入的参数列表&#xff0c;这里是空的。花括号包围的部分就是函数体&#xff0c;里面就是我们要执行的操作。</p>
<ol><li>语句</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">函数体里&#xff0c;每一步操作都是一个“语句”&#xff08;statement&#xff09;&#xff0c;用分号结尾。我们这里的语句&#xff0c;执行的就是输出Hello World的操作。</p>
<p style="margin-left:.0001pt;text-align:justify;">std::cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; std::endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">这是一个“表达式”。所谓表达式&#xff0c;一般由多个运算的对象和运算符组成&#xff0c;执行运算之后会得到一个计算结果。在这里&#xff0c;两个连在一起的小于号“&lt;&lt;”就是一个用来输出的运算符。它的使用规则是&#xff1a;左边需要一个“输出流”的对象&#xff0c;也就是输出到哪里&#xff1b;右边是要输出的内容&#xff0c;最简单的就是一个“字符串”&#xff0c;需要用双引号引起来。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以 std::cout &lt;&lt; &#34;Hello World!&#34; 的意思就是&#xff1a;将“Hello World&#xff01;”这串信息&#xff0c;输出到cout这个对象。cout就是一个输出流对象&#xff0c;iostream库里定义了它的功能&#xff0c;接收到信息之后就可以输出显示了。而cout前面的std是所谓的“命名空间”&#xff08;namespace&#xff09;&#xff0c;主要是为了避免还有别的cout对象重名起冲突。这里的双冒号“::”也是一个运算符&#xff0c;叫做作用域运算符&#xff0c;专门指明了我们用的cout是标准库std中的。如果不想总用双冒号&#xff0c;也可以直接加上一句&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:justify;">这样就可以直接用cout&#xff0c;不需要加std::了。</p>
<p style="margin-left:.0001pt;text-align:justify;">输出运算符 &lt;&lt; 得到的计算结果&#xff0c;还是它左边的那个输出流对象cout。这样一来&#xff0c;我们就可以在后面继续写入信息信息了。所以后面的 &lt;&lt; endl &#xff0c;其实就是把 endl 这个内容&#xff0c;又写入到cout中输出了。这个endl是一个“操作符”&#xff0c;表示结束一行&#xff0c;并把缓冲区的内容都刷到输出设备。</p>
<ol><li>返回值</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">最后一行语句就是返回一个值。大多数系统中&#xff0c;main的返回值是用来指示状态的。返回0表示成功&#xff0c;非0表示出错&#xff0c;具体值可以用来表示错误类型&#xff0c;这是由系统定义的。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们这里写了return 0&#xff0c;其实不写也是可以的&#xff0c;默认正常运行结束就会返回0。</p>
<h5 style="text-align:justify;"><strong><strong><strong>2.2.3 </strong></strong><strong><strong>注释</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">可以看到&#xff0c;纯粹的代码还是比较抽象的&#xff1b;特别是当代码越来越多、越来越复杂之后&#xff0c;就会变得越来越难理解。所以我们一般会插入一些解释说明的文字&#xff0c;这叫做“注释”。注释不会被执行&#xff0c;对代码的功能没有任何影响。</p>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;有两种注释的表示。一种是单行注释&#xff0c;用双斜线“//”&#xff0c;表示以它开始的当前行是注释内容&#xff1b;另一种是多行注释&#xff0c;使用一对“界定符”&#xff08;/* 和 */&#xff09;,在它们之间的所有内容都是注释。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">/*</p>
<p style="margin-left:.0001pt;text-align:left;"> * 主函数</p>
<p style="margin-left:.0001pt;text-align:left;"> * Hello World</p>
<p style="margin-left:.0001pt;text-align:left;"> */</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 输出一行信息</p>
<p style="margin-left:.0001pt;text-align:left;">std::cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; std::endl;</p>
<p style="margin-left:.0001pt;text-align:left;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>2.2.4 </strong></strong><strong><strong>代码的改进——简单的输入输出</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">我们之前写的代码非常简单&#xff0c;实现了输出Hello World的功能。不过输出显示用的是“调试控制台”&#xff0c;运行完成总会显示一行额外信息&#xff0c;能不能让它更纯粹地运行、不显示多余内容呢&#xff1f;</p>
<p style="margin-left:.0001pt;text-align:justify;">当然可以&#xff0c;调试台输出的信息本身就有提示&#xff0c;只要更改一下VS的设置。要在调试停止时自动关闭控制台&#xff0c;请启用“工具”-&gt;“选项”-&gt;“调试”-&gt;“调试停止时自动关闭控制台”。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/bb5aedceec494cefa77792d051d8aba8.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">不过出现了新的问题&#xff1a;再次运行的时候&#xff0c;窗口一闪而过&#xff0c;根本看不清输出了什么。为了查看输出结果&#xff0c;我们还是希望把窗口保持住、不要直接退出&#xff0c;这可以通过在main()函数中增加一句输入语句来实现&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 输出一行信息</p>
<p style="margin-left:.0001pt;text-align:left;">std::cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; std::endl;</p>
<p style="margin-left:.0001pt;text-align:left;">// 等待键盘输入</p>
<p style="margin-left:.0001pt;text-align:left;">std::cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里的cin跟cout刚好相反&#xff0c;它是一个输入流对象。调用它内部的函数get()&#xff0c;就可以读取键盘的输入&#xff1b;等待键盘输入的时候&#xff0c;窗口就会一直开着。这里的键盘输入是以回车作为结束标志的&#xff0c;所以运行看到结果之后&#xff0c;直接敲回车就可以退出了。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/b35d8cc1964341a0b3cd5832cfeca9b2.png"  />
</p>
<h4 style="text-align:justify;"><strong><strong><strong>2.3 </strong></strong><strong><strong>编译、链接和运行</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">我们之前写好C&#43;&#43;代码之后&#xff0c;是直接在Visual Studio里借助“本地windows调试器”运行的&#xff1b;而如果真正开发一个软件&#xff0c;显然不能总是依赖VS的调试器运行。真正应用中&#xff0c;我们最终要得到一个“可执行文件”&#xff0c;一般以.exe作为扩展名&#xff0c;双击就可以运行程序了。</p>
<p style="margin-left:.0001pt;text-align:justify;">怎样转换得到可执行文件呢&#xff1f;之前已经提到&#xff0c;C&#43;&#43;是一种编译型语言&#xff0c;在运行之前需要进行编译和链接。我们现在就用上节写好的Hello World代码&#xff0c;把这个过程具体说明一下。</p>
<p style="margin-left:.0001pt;text-align:justify;">首先我们可以在Visual Studio左侧的“解决方案资源管理器”里&#xff0c;右键点击创建的项目HelloWorld&#xff0c;选择“在文件资源管理器中打开文件夹”&#xff0c;就会进入保存项目的文件夹。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/6cf7f1a549b14897a97125692f3cdf0e.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">这里看到的helloworld.cpp&#xff0c;就是我们写好的C&#43;&#43;源代码文件。其它的文件都是VS生成的项目文件。另外还有一个x64文件夹&#xff0c;是之前我们在本地进行调试运行时生成的&#xff0c;里面有一个Debug子文件夹&#xff0c;保存了调试运行的相关信息和日志。如果我们右键HelloWorld项目名&#xff0c;然后选择“清理”&#xff0c;Debug里面就只剩下一些日志和空文件了。</p>
<p style="margin-left:.0001pt;text-align:justify;">源代码首先需要编译&#xff08;compile&#xff09;&#xff0c;得到目标代码。编译器当然是由Visual Studio提供的。我们首先点击一下源代码文件&#xff0c;然后在VS的菜单栏中选择“生成”-&gt; “编译”&#xff08;快捷键Ctrl&#43;F7&#xff09;&#xff0c;就可以进行编译了。在下方的“输出”窗口内&#xff0c;可以看到编译的结果信息。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/b5c8e89b240d40e88c61fa3562074b59.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">编译完成之后&#xff0c;再回到之前打开的项目文件夹&#xff0c;找到x64下的Debug目录&#xff0c;点进去之后就会发现多了几个文件&#xff0c;除了一些调试工具外&#xff0c;最重要的就是一个helloworld.obj&#xff0c;这就是编译生成的目标代码文件。</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/5476e6d148e64721887be2ae58b5deec.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">目标文件就是计算机能够直接运行的机器码。但是仅有helloworld.cpp源代码转换的机器码还不够。因为我们用到了iostream中的cout和cin对象进行输入输出操作&#xff0c;这就需要把iostream中对应的目标代码也提出来&#xff0c;组合成一个完整的、能直接运行的机器代码。这就是所谓的“链接”&#xff08;link&#xff09;过程&#xff0c;结果就会生成一个可执行文件。</p>
<p style="margin-left:.0001pt;text-align:justify;">在VS中&#xff0c;我们可以点击工具栏“生成” –&gt; “生成HelloWorld”&#xff08;快捷键Ctrl&#43;B&#xff09;&#xff1b;也可以直接右键HelloWorld项目名选择“生成”。在“输出”窗口可以清楚地看到&#xff0c;扩展名为.exe的可执行文件已经生成了。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/4f8f65837d3f42f3a1cb9d4e68f4beed.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">在对应的目录找到这个文件&#xff0c;双击运行&#xff0c;我们会发现跟之前在调试器中的运行结果是一样的&#xff0c;可以直接在窗口中显示“Hello World&#xff01;”&#xff0c;回车就会退出。这个.exe文件可以复制到任何位置&#xff0c;直接双击运行程序。</p>
<h4 style="text-align:justify;"><strong><strong><strong>2.4 初步认识函数</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">通过一个最简单的Hello World程序&#xff0c;我们已经了解了C&#43;&#43;基本的代码风格、简单的输入输出操作&#xff0c;以及程序编译运行的完整过程。利用这些知识我们可以为这个程序增加更多的功能&#xff0c;比如提示用户输入自己的名字XXX&#xff0c;然后显示“Hello&#xff0c; XXX”。</p>
<p style="margin-left:.0001pt;text-align:justify;">代码如下&#xff1a;</p>

<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 输出一行信息</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 提示输入姓名</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;请输入您的大名&#xff1a;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">// 用一个变量接收键盘输入</p>
<p style="margin-left:.0001pt;text-align:left;">string name;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; name;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 输出欢迎信息</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello, &#34; &lt;&lt; name &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 等待键盘输入</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();    </p>
<p style="margin-left:.0001pt;text-align:left;">// 这里写两次是因为之前输入信息时敲回车确认&#xff0c;会由第一个get捕捉到</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">但是这样代码就比较多了&#xff0c;可读性会变差。解决办法是&#xff0c;我们可以把中间一部分代码“包装”成函数&#xff0c;就像主函数一样。只不过这种函数不是启动直接调用的&#xff0c;而是需要在程序中明确地写出来什么时候调用。</p>
<p style="margin-left:.0001pt;text-align:justify;">代码如下&#xff1a;</p>

<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 定义一个函数</p>
<p style="margin-left:.0001pt;text-align:left;">void welcome()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;请输入您的大名&#xff1a;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">string name;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; name;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello, &#34; &lt;&lt; name &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 调用函数</p>
<p style="margin-left:.0001pt;text-align:left;">welcome();</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 等待键盘输入</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">这样每一部分处理逻辑都可以分块包装成函数&#xff0c;主函数的执行过程看起来就简单多了。当然&#xff0c;如果认为一个文件中有太多函数也会影响可读性&#xff0c;我们还可以把它们分开。比如新建一个叫做welcom.cpp的源文件&#xff0c;专门放刚才的welcome函数。而在主函数中&#xff0c;需要额外对它做一个“声明”&#xff0c;表示有这样一个函数&#xff0c;它的实现在另外的文件里。</p>

<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 声明一个函数</p>
<p style="margin-left:.0001pt;text-align:left;">void welcome();</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 调用函数</p>
<p style="margin-left:.0001pt;text-align:left;">welcome();</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">函数是C&#43;&#43;中基本的编程单元&#xff0c;也是“模块化编程”的核心思想&#xff0c;我们还会在后面的章节详细展开。</p>
<h3 style="text-align:justify;"><strong><strong><strong>三、变量和数据类型</strong></strong></strong></h3>
<p style="margin-left:.0001pt;text-align:justify;">一段程序的核心有两个方面&#xff1a;一个是要处理的信息&#xff0c;另一个就是处理的计算流程。计算机所处理的信息一般叫做“数据”&#xff08;data&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">对计算机来说&#xff0c;需要明确地知道把数据存放在哪里、以及需要多大的存储空间。在机器语言和汇编语言中&#xff0c;我们可能需要充分了解计算机底层的存储空间&#xff0c;这非常麻烦&#xff1b;而在C&#43;&#43;程序中&#xff0c;我们可以通过“声明变量”的方式来实现这些。</p>
<h4 style="text-align:justify;"><strong><strong><strong>3.1 </strong></strong><strong><strong>变量和常量</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">为了区分不同的数据&#xff0c;在程序中一般会给它们起个唯一的名字&#xff0c;这就是所谓的“变量”。在C&#43;&#43;中&#xff0c;“变量”其实就是记录了计算机内存中的一个位置标签&#xff0c;可以表示存放的数据对象。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/574054f4c85f47eda9869aa522f724c0.png"  />
</p>
<p style="margin-left:.0001pt;text-align:center;">    </p>
<h5 style="text-align:justify;"><strong><strong><strong>3.1.1 </strong></strong><strong><strong>变量的声明和赋值</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">想要使用变量&#xff0c;必须先做“声明”&#xff0c;也就是告诉计算机要用到的数据叫什么名字&#xff0c;同时还要指明保存数据所需要的空间大小。比如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">int a;</p>
<p style="margin-left:.0001pt;text-align:justify;">这里包含两个信息&#xff1a;一个是变量的名字&#xff0c;叫做“a”&#xff0c;它对应着计算机内存中的一个位置&#xff1b;另一个是变量占据的空间大小&#xff0c;这是通过前面的“int”来指明的&#xff0c;表示我们需要足够的空间来存放一个“整数类型”&#xff08;integer&#xff09;数据。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以变量声明的标准语法可以写成&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>数据类型 变量名;</em></p>
<p style="margin-left:.0001pt;text-align:justify;">变量名也可以有多个&#xff0c;用逗号分隔就可以。</p>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;可以处理各种不同类型的数据&#xff0c;这里的int就是最基本的一种“数据类型”&#xff08;data type&#xff09;&#xff0c;表示一般的整数。</p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;如果我们直接在代码中声明一个变量&#xff0c;然后打印输出的话就会报错&#xff0c;因为这个变量没有被“初始化”。也就是说&#xff0c;a这个变量现在可以表示内存中一个位置了&#xff0c;但是里面的数据是什么&#xff1f;这就需要让a有一个“初始值”&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">int a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">这个操作叫做“赋值”。需要说明的是&#xff0c;这里等号“&#61;”表示的是赋值操作&#xff0c;并不是数学上的“等于”。换句话说&#xff0c;我们还可以继续给a赋别的值&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">int a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">a &#61; 2;</p>
<p style="margin-left:.0001pt;text-align:justify;">现在a的值就是2了。a的值可以改变&#xff0c;所以它叫做“变量”。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">扩展知识&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;是一种静态类型&#xff08;statically typed&#xff09;语言&#xff0c;需要在编译阶段做类型检查&#xff08;type checking&#xff09;。也就是说所有变量在创建的时候必须指明类型&#xff0c;而且之后不能更改。对于复杂的大型程序来说&#xff0c;这种方式更有助于提前发现问题、提高运行效率。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">代码如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">a &#61; 2;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;现在 a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">要运行的话&#xff0c;可以右键项目名 -&gt; 设为启动项目&#xff0c;或者右键解决方案 -&gt; 设置启动项目。</p>
<p style="margin-left:.0001pt;text-align:justify;">注意&#xff0c;如果不给初始值&#xff0c;后面再赋值、再使用也是合法的&#xff1b;但一般不能不赋值、直接使用。因为在函数中定义的变量不被初始化&#xff0c;而在函数外部定义的变量会被默认初始化为0值。</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.1.2 </strong></strong><strong><strong>标识符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">每个变量都有一个名字&#xff0c;就是所谓的“变量名”。在C&#43;&#43;中&#xff0c;变量、函数、类都可以有自己专门的名字&#xff0c;这些名字被叫做“标识符”。</p>
<p style="margin-left:.0001pt;text-align:justify;">标识符由字母、数字和下划线组成&#xff1b;不能以数字开头&#xff1b;标识符是大小写敏感的&#xff0c;长度不限。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以下面的变量名都是合法而且不同的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">int b, B, B2, a1_B2;</p>
<p style="margin-left:.0001pt;text-align:justify;">此外&#xff0c;C&#43;&#43;中还对变量命名有一些要求和约定俗成的规范&#xff1a;</p>
<ol><li>不能使用C&#43;&#43;关键字&#xff1b;</li><li>不能用连续两个下划线开头&#xff0c;也不能以下划线加大写字母开头&#xff0c;这些被C&#43;&#43;保留给标准库使用&#xff1b;</li><li>函数体外的标识符&#xff0c;不能以下划线开头&#xff1b;</li><li>要尽量有实际意义&#xff08;不要定义a、b&#xff0c;而要定义name、age&#xff09;&#xff1b;</li><li>变量名一般使用小写字母&#xff1b;</li><li>自定义类名一般以大写字母开头&#xff1b;</li><li>如果包含多个单词&#xff0c;一般用下划线分隔&#xff0c;或者将后面的单词首字母大写&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">所谓的“关键字”&#xff0c;就是C&#43;&#43;保留的一些单词&#xff0c;供语言本身的语法使用。包括&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/1454a6da34a441a49b7333e2422dc085.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">以及C&#43;&#43;中使用的一些运算操作符的替代名&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/fd0c4882cca2406ea016170ae23b6ecb.png"  />
</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.1.3 </strong></strong><strong><strong>作用域</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">变量有了名字&#xff0c;那只要用这个名字就可以指代对应的数据。但是如果出现“重名”怎么办呢&#xff1f;</p>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;有“作用域”&#xff08;scope&#xff09;的概念&#xff0c;就是指程序中的某一段、某一部分。一般作用域都是以花括号{}作为分隔的&#xff0c;就像之前我们看到的函数体那样。</p>
<p style="margin-left:.0001pt;text-align:justify;">同一个名字在不同的作用域中&#xff0c;可以指代不同的实体&#xff08;变量、函数、类等等&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">定义在所有花括号外的名字具有“全局作用域”&#xff08;global scope&#xff09;&#xff0c;而在某个花括号内定义的名字具有“块作用域”。一般把具有全局作用域的变量叫做“全局变量”&#xff0c;具有块作用域的变量叫做“局部变量”。</p>
<p style="margin-left:.0001pt;text-align:justify;">测试代码如下&#xff1a;</p>

<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 全局作用域&#xff0c;全局变量</p>
<p style="margin-left:.0001pt;text-align:left;">int number &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 块作用域&#xff0c;局部变量</p>
<p style="margin-left:.0001pt;text-align:left;">int number &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 访问局部变量</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;number &#61; &#34; &lt;&lt; number &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">// 访问全局变量</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;number &#61; &#34; &lt;&lt; ::number &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">如果在嵌套作用域里出现重名&#xff0c;一般范围更小的局部变量会覆盖全局变量。如果要特意访问全局变量&#xff0c;需要加上双冒号:: &#xff0c;指明是默认命名空间。</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.1.4 </strong></strong><strong><strong>常量</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">用变量可以灵活地保存数据、访问数据。不过有的时候&#xff0c;我们希望保存的数据不能更改&#xff0c;这种特殊的变量就被叫做“常量”。在C&#43;&#43;中&#xff0c;有两种方式可以定义常量&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;使用符号常量</p>
<p style="margin-left:.0001pt;text-align:justify;">这种方式是在文件头用 #define 来定义常量&#xff0c;也叫作“宏定义”。</p>
<p style="margin-left:.0001pt;text-align:justify;">#define ZERO 0</p>
<p style="margin-left:.0001pt;text-align:justify;">跟#include一样&#xff0c;井号“#”开头的语句都是“预处理语句”&#xff0c;在编译之前&#xff0c;预处理器会查找程序中所有的“ZERO”&#xff0c;并把它替换成0。这种宏定义的方式是保留的C语言特性&#xff0c;在C&#43;&#43;中一般不推荐。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;使用const限定符</p>
<p style="margin-left:.0001pt;text-align:justify;">这种方式跟定义一个变量是一样的&#xff0c;只需要在变量的数据类型前再加上一个const关键字&#xff0c;这被称为“限定符”。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 定义常量</p>
<p style="margin-left:.0001pt;text-align:left;">const int Zero &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">// 不能修改常量值</p>
<p style="margin-left:.0001pt;text-align:justify;">//Zero &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">const修饰的对象一旦创建就不能改变&#xff0c;所以必须初始化。</p>
<p style="margin-left:.0001pt;text-align:justify;">跟使用 #define定义宏常量相比&#xff0c;const定义的常量有详细的数据类型&#xff0c;而且会在编译阶段进行安全检查&#xff0c;在运行时才完成替换&#xff0c;所以会更加安全和方便。</p>
<h4 style="text-align:justify;"><strong><strong><strong>3.2 </strong></strong><strong><strong>基本数据类型</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">定义变量时&#xff0c;不可或缺的一个要素就是数据类型。本质上讲&#xff0c;这就是为了实现计算需求&#xff0c;我们必须先定义好数据的样式&#xff0c;告诉计算机这些数据占多大空间&#xff0c;这就是所谓“数据类型”的含义。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;支持丰富的数据类型&#xff0c;它内置了一套基本数据类型&#xff0c;也为我们提供了自定义类型的机制。</p>
<p style="margin-left:.0001pt;text-align:justify;">接下来我们先介绍基本数据类型&#xff0c;主要包括算术类型和空类型&#xff08;void&#xff09;。其中算术类型又包含了整型和浮点型&#xff1b;而空类型不对应具体的值&#xff0c;只用在一些特定的场合&#xff0c;比如一个函数如果不返回任何值&#xff0c;我们可以让void作为它的返回类型。</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.1 </strong></strong><strong><strong>整型</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">整型&#xff08;integral type&#xff09;本质上来讲就是表示整数的类型。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们知道在计算机中&#xff0c;所有数据都是以二进制“0”“1”来表示的&#xff0c;每个叫做一位&#xff08;bit&#xff09;&#xff1b;计算机可寻址的内存最小单元是8位&#xff0c;也就是一个字节&#xff08;Byte&#xff09;。所以我们要访问的数据&#xff0c;都是保存在内存的一个个字节里的。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/a03993dc164f42b6a6d816bd6c36056c.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">一个字节能表示的最大数是28 &#61; 256&#xff0c;这对于很多应用来讲显然是不够的。不同的需求可能要表示的数的范围也不一样&#xff0c;所以C&#43;&#43;中定义了多个整数类型&#xff0c;它们的区别就在于每种类型占据的内存空间大小不同。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;定义的基本整型包括char、short、int、long&#xff0c;和C&#43;&#43; 11新增的long long类型&#xff0c;此外特殊的布尔类型bool本质上也是整型。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/1434033560b94f60868eece9468e2291.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中对它们占据的长度定义比较灵活&#xff0c;这样不同的计算机平台就可以有自己的实现了&#xff08;这跟C是一样的&#xff09;。由于char和bool相对特殊&#xff0c;我们先介绍其它四种。C&#43;&#43;标准中对它们有最小长度的要求&#xff0c;比如&#xff1a;</p>
<ol><li>short类型至少为16位&#xff08;2字节&#xff09;</li><li>int至少2字节&#xff0c;而且不能比short短</li><li>long至少4字节&#xff0c;而且不能比int短</li><li>long long至少8字节&#xff0c;而且不能比long短</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">现在一般系统中&#xff0c;short和long都选择最小长度&#xff0c;也就是short为16位、long为32位、long long为64位&#xff1b;而int则有不同选择。我们一般使用的电脑操作系统&#xff0c;比如Windows 7、Windows 10、Mac OS等等的实现中&#xff0c;int都是32位的。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以short能表示的数有216 &#61; 65536 个&#xff0c;考虑正负&#xff0c;能表示的范围就是-32768 ~ 32767&#xff1b;而int表示的数范围则为 - 231 ~ 231 - 1。&#xff08;大概是正负20亿&#xff0c;足够用了&#xff09;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">short a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的长度为&#xff1a;&#34; &lt;&lt; sizeof(a) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int b;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;b的长度为&#xff1a;&#34; &lt;&lt; sizeof(b) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">long c;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;c的长度为&#xff1a;&#34; &lt;&lt; sizeof(c) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">long long d;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;d的长度为&#xff1a;&#34; &lt;&lt; sizeof(d) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里我们用到了sizeof&#xff0c;这是一个运算符&#xff0c;可以返回某个变量占用的字节数。我们可以看到&#xff0c;变量占用的空间大小只跟类型有关&#xff0c;跟变量具体的值无关。</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.2 </strong></strong><strong><strong>无符号整型</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">整型默认是可正可负的&#xff0c;如果我们只想表示正数和0&#xff0c;那么所能表示的范围就又会增大一倍。以16位的short为例&#xff0c;本来表示的范围是-32768 ~ 32767&#xff0c;如果不考虑负数&#xff0c;那么就可以表示0 ~ 65535。C&#43;&#43;中&#xff0c;short、int、long、long long都有各自的“无符号”版本的类型&#xff0c;只要定义时在类型前加上unsigned就可以。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">short a &#61; 32768;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的长度为&#xff1a;&#34; &lt;&lt; sizeof a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">unsigned short a2 &#61; 32768;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a2 &#61; &#34; &lt;&lt; a2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;a2的长度为&#xff1a;&#34; &lt;&lt; sizeof a2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">上面的代码可以测试无符号数表示的范围。需要注意&#xff0c;当数值超出了整型能表示的范围&#xff0c;程序本身并不会报错&#xff0c;而是会让数值回到能表示的最小值&#xff1b;这种情况叫做“数据溢出”&#xff08;或者“算术溢出”&#xff09;&#xff0c;写程序时一定要避免。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">由于类型太多&#xff0c;在实际应用中使用整型可以只考虑三个原则&#xff1a;</p>
<ol><li>一般的整数计算&#xff0c;全部用int&#xff1b;</li><li>如果数值超过了int的表示范围&#xff0c;用long long&#xff1b;</li><li>确定数值不可能为负&#xff0c;用无符号类型&#xff08;比如统计人数、销售额等&#xff09;&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.3 char</strong></strong><strong><strong>类型</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">如果我们只需要处理很小的整数&#xff0c;也可以用另外一种特殊的整型类型——char&#xff0c;它通常只占一个字节&#xff08;8位&#xff09;。不过char类型一般并不用在整数计算&#xff0c;它更重要的用途是表示字符&#xff08;character&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">计算机底层的数据都是二进制位表示的&#xff0c;这用来表示一个整数当然没有问题&#xff0c;可怎么表示字母呢&#xff1f;这就需要将常用的字母、以及一些特殊符号对应到一个个的数字上&#xff0c;然后保存下来&#xff0c;这就是“编码”的过程 。</p>
<p style="margin-left:.0001pt;text-align:justify;">最常用的字符编码集就是ASCII码&#xff0c;它用0~127表示了128个字符&#xff0c;这包括了所有的大小写字母、数字、标点符号、特殊符号以及一些计算机的控制符。比如字母“A”的编码是65&#xff0c;数字字符“0”的编码是48。</p>
<p style="margin-left:.0001pt;text-align:justify;">在程序中如果使用char类型的变量&#xff0c;我们会发现&#xff0c;打印出来就是一个字符&#xff1b;而它的底层是一个整数&#xff0c;也可以做整数计算。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">char ch &#61; 65;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;65对应的字符为&#xff1a;&#34; &lt;&lt; ch &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">char ch2 &#61; ch &#43; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;66对应的字符为&#xff1a;&#34; &lt;&lt; ch2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">char类型用来表示整数时&#xff0c;到底是有符号还是无符号呢&#xff1f;之前的所有整型&#xff0c;默认都是有符号的&#xff0c;而char并没有默认类型&#xff0c;而是需要C&#43;&#43;编译器根据需要自己决定。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以把char当做小整数时&#xff0c;有两种显式的定义方式&#xff1a;signed char 和 unsigned char&#xff1b;至于char定义出来的到底带不带符号&#xff0c;就看编译器的具体实现了。</p>
<p style="margin-left:.0001pt;text-align:justify;">另外&#xff0c; C&#43;&#43;还对字符类型进行了“扩容”&#xff0c;提供了一种“宽字符”类型wchar_t。wchar_t会在底层对应另一种整型&#xff08;比如short或者int&#xff09;&#xff0c;具体占几个字节要看系统中的实现。</p>
<p style="margin-left:.0001pt;text-align:justify;">wchar_t会随着具体实现而变化&#xff0c;不够稳定&#xff1b;所以在C&#43;&#43;11新标准中&#xff0c;还为Unicode字符集提供了专门的扩展字符类型&#xff1a;char16_t和char32_t&#xff0c;分别长16位和32位。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/36714611634c4e4baaeed6bca94f70b1.png"  />
</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.</strong></strong><strong><strong>4</strong></strong><strong><strong> bool</strong></strong><strong><strong>类型</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在程序中&#xff0c;往往需要针对某个条件做判断&#xff0c;结果只有两种&#xff1a;“成立”和“不成立”&#xff1b;如果用逻辑语言来描述&#xff0c;就是“真”和“假”。真值判断是二元的&#xff0c;所以在C语言中&#xff0c;可以很简单地用“1”表示“真”&#xff0c;“0”表示“假”。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;支持C语言中的这种定义&#xff0c;同时为了让代码更容易理解&#xff0c;引入了一种新的数据类型——布尔类型bool。bool类型只有两个取值&#xff1a;true和false&#xff0c;这样就可以非常明确地表示逻辑真假了。bool类型通常占用8位&#xff08;1个字节&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">bool bl &#61; true;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;bl &#61; &#34; &lt;&lt; bl &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;bool类型长度为&#xff1a;&#34; &lt;&lt; sizeof bl &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以看到&#xff0c;true和false可以直接赋值给bool类型的变量&#xff0c;打印输出的时候&#xff0c;true就是1&#xff0c;false就是0&#xff0c;这跟C语言里的表示其实是一样的。</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.</strong></strong><strong><strong>5</strong></strong><strong> </strong><strong><strong>浮点类型</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">跟整数对应&#xff0c;浮点数用来表示小数&#xff0c;主要有单精度float和双精度double两种类型&#xff0c;double的长度不会小于float。通常&#xff0c;float会占用4个字节&#xff08;32位&#xff09;&#xff0c;而double会占用8个字节&#xff08;64位&#xff09;。此外&#xff0c;C&#43;&#43;还提供了一种扩展的高精度类型long double&#xff0c;一般会占12或16个字节。</p>
<p style="margin-left:.0001pt;text-align:justify;">除了一般的小数&#xff0c;在C&#43;&#43;中&#xff0c;还提供了另外一种浮点数的表示法&#xff0c;那就是科学计数法&#xff0c;也叫作“E表示法”。比如&#xff1a;5.98E24表示5.98×1024&#xff1b;9.11e-31表示9.11×10-31。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 浮点类型</p>
<p style="margin-left:.0001pt;text-align:left;">float f &#61; 3.14;</p>
<p style="margin-left:.0001pt;text-align:left;">double pi &#61; 5.2e-3;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;f &#61; &#34; &lt;&lt; f &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;pi &#61; &#34; &lt;&lt; pi &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这就极大地扩展了我们能表示的数的范围。一般来讲&#xff0c;float至少有6位有效数字&#xff0c;double至少有15位有效数字。所以浮点类型不仅能表示小数&#xff0c;还可以表示&#xff08;绝对值&#xff09;非常大的整数。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;float和double具体能表示的范围&#xff0c;可以查找float.h这个头文件&#xff09;</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.6 字面值常量</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">我们在给一个变量赋值的时候&#xff0c;会直接写一个整数或者小数&#xff0c;这个数据就是显式定义的常量值&#xff0c;叫做“字面值常量”。每个字面值常量也需要计算机进行保存和处理&#xff0c;所以也都是有数据类型的。字面值的写法形式和具体值&#xff0c;就决定了它的类型。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;整型字面值</p>
<p style="margin-left:.0001pt;text-align:justify;">整型字面值就是我们直接写的一个整数&#xff0c;比如30。这是一个十进制数。而计算机底层是二进制的&#xff0c;所以还支持我们把一个数写成八进制和十六进制的形式。以0开头的整数表示八进制数&#xff1b;以0x或者0X开头的代表十六进制数。例如&#xff1a;</p>
<ol><li>30    十进制数</li><li>036   八进制数</li><li>0x1E  十六进制数</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">这几个数本质上都是十进制的30&#xff0c;在计算机底层都是一样的。</p>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;一个整型字面值&#xff0c;默认就是int类型&#xff0c;前提是数值在int能表示的范围内。如果超出int范围&#xff0c;那么就需要选择能够表示这个数的、长度最小的那个类型。</p>
<p style="margin-left:.0001pt;text-align:justify;">具体来说&#xff0c;对于十进制整型字面值&#xff0c;如果int不够那么选择long&#xff1b;还不够&#xff0c;就选择long long&#xff08;不考虑无符号类型&#xff09;&#xff1b;而八进制和十六进制字面值&#xff0c;则会优先用无符号类型unsigned int&#xff0c;不够的话再选择long&#xff0c;之后依次是unsigned long、long long和unsigned long long。</p>
<p style="margin-left:.0001pt;text-align:justify;">这看起来非常复杂&#xff0c;很容易出现莫名其妙的错误。所以一般我们在定义整型字面值时&#xff0c;会给它加上一个后缀&#xff0c;明确地告诉计算机这个字面值是什么类型。</p>
<ol><li>默认什么都不加&#xff0c;是int类型&#xff1b;</li><li>l或者L&#xff0c;表示long类型&#xff1b;</li><li>ll或者LL&#xff0c;表示long long类型&#xff1b;</li><li>u或者U&#xff0c;表示unsigned无符号类型&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">我们一般会用大写L&#xff0c;避免跟数字1混淆&#xff1b;而u可以和L或LL组合使用。例如9527uLL就表示这个数是unsigned long long类型。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;浮点型字面值</p>
<p style="margin-left:.0001pt;text-align:justify;">前面已经提到&#xff0c;可以用一般的小数或者科学计数法表示的数&#xff0c;来给浮点类型赋值&#xff0c;这样的数就都是“浮点型字面值”。浮点型字面值默认的类型是double。如果我们希望明确指定类型&#xff0c;也可以加上相应的后缀&#xff1a;</p>
<ol><li>f或者F&#xff0c;表示float类型</li><li>l或者L&#xff0c;表示long double类型</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">这里因为本身数值是小数或者科学计数法表示&#xff0c;所以L不会跟long类型混淆。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/9f6e5b4462ee45699c85fc8887a454aa.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;字符和字符串字面值</p>
<p style="margin-left:.0001pt;text-align:justify;">字符就是我们所说的字母、单个数字或者符号&#xff0c;字面值用单引号引起来表示。字符字面值默认的类型就是char&#xff0c;底层存储也是整型。</p>
<p style="margin-left:.0001pt;text-align:justify;">而多个字符组合在一起&#xff0c;就构成了“字符串”。字符串字面值是一串字符&#xff0c;用双引号引起来表示。</p>
<ol><li>‘ A ’   字符字面值</li><li>“Hello World!”    字符串字面值</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">字符串是字符的组合&#xff0c;所以字符串字面值的类型&#xff0c;本质上是char类型构成的“数组”&#xff08;array&#xff09;。关于数组的介绍&#xff0c;我们会在后面章节详细展开。</p>
<ul><li>转义字符</li></ul>
<p style="margin-left:.0001pt;text-align:justify;">有一类比较特殊的字符字面值&#xff0c;我们是不能直接使用的。在ASCII码中我们看到&#xff0c;除去字母、数字外还有很多符号&#xff0c;其中有一些本身在C&#43;&#43;语法中有特殊的用途&#xff0c;比如单引号和双引号&#xff1b;另外还有一些控制字符。如果我们想要使用它们&#xff0c;就需要进行“转义”&#xff0c;这就是“转义字符”。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中规定的转义字符有&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/1edbc30784814acf9653dd6bc0eeb0ba.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">其中&#xff0c;经常用到的就是符号中的问号、双引号、单引号、反斜线&#xff0c;还有换行符和制表符。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 转义字符</p>
<p style="margin-left:.0001pt;text-align:left;">char tchar &#61; &#39;\n&#39;;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;tchar &#61; &#34; &lt;&lt; tchar &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello World!\t\&#34;Hello C&#43;&#43;!\&#34;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;4&#xff09;布尔字面值</p>
<p style="margin-left:.0001pt;text-align:justify;">布尔字面值非常简单&#xff0c;只有两个&#xff1a;true和false。</p>
<h5 style="text-align:justify;"><strong><strong><strong>3.2.7 类型转换</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">我们在使用字面值常量给变量赋值时会有一个问题&#xff0c;如果常量的值超出了变量类型能表示的范围&#xff0c;或者把一个浮点数赋值给整型变量&#xff0c;会发生什么&#xff1f;</p>
<p style="margin-left:.0001pt;text-align:justify;">这时程序会进行自动类型转换。也就是说&#xff0c;程序会自动将一个常量值&#xff0c;转换成变量的数据类型&#xff0c;然后赋值给变量。</p>

<p style="margin-left:.0001pt;text-align:left;">// 1. 整数值赋给bool类型</p>
<p style="margin-left:.0001pt;text-align:left;">bool b &#61; 25;    // b值为true&#xff0c;打印为1</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 2. bool类型赋值给算术整型</p>
<p style="margin-left:.0001pt;text-align:left;">short s &#61; false;    // s值为0</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 3. 浮点数赋给整数类型</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 3.14;    // i值为3</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 4. 整数值赋给浮点类型</p>
<p style="margin-left:.0001pt;text-align:left;">float f &#61; 10;    // f值为10.0&#xff0c;打印为10</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 5. 赋值超出整型范围</p>
<p style="margin-left:.0001pt;text-align:left;">unsigned short us &#61; 65536;    // us值为0</p>
<p style="margin-left:.0001pt;text-align:left;">s &#61; 32768;    // s值为-32768</p>

<p style="margin-left:.0001pt;text-align:justify;">转换规则可以总结如下&#xff1a;</p>
<ol><li>非布尔类型的算术值赋给布尔类型&#xff0c;初始值为0则结果为 false , 否则结果为true &#xff1b;</li><li>布尔值赋给非布尔类型&#xff0c;初始值为 false 则结果为0&#xff0c;初始值为 true 则结果为1&#xff1b;</li><li>浮点数赋给整数类型&#xff0c;只保留浮点数中的整数部分&#xff0c;会带来精度丢失&#xff1b;</li><li>整数值赋给浮点类型&#xff0c;小数部分记为0。如果保存整数需要的空间超过了浮点类型的容量&#xff0c;可能会有精度丢失。</li><li>给无符号类型赋值&#xff0c;如果超出它表示范围&#xff0c;结果是初始值对无符号类型能表示的数值总数取模后的余数。</li></ol>
<ol><li>给有符号类型赋值&#xff0c;如果超出它表示范围&#xff0c;结果是未定义的&#xff08; undefined &#xff09;。此时&#xff0c;程序可能继续工作&#xff0c;也可能崩溃。</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中的数据类型转换&#xff0c;是一个比较复杂的话题。我们这里先了解一下变量赋值时的自动类型转换&#xff0c;关于更加复杂的转换&#xff0c;我们会在下一章继续介绍。</p>
<h3 style="text-align:justify;"><strong><strong><strong>四、运算符</strong></strong></strong></h3>
<p style="margin-left:.0001pt;text-align:justify;">有了数据之后&#xff0c;就可以对数据对象进行各种计算了。在编程语言中&#xff0c;可以通过“运算符”来表示想要进行的计算。</p>
<h4 style="text-align:justify;"><strong><strong><strong>4.1 </strong></strong><strong><strong>表达式和运算符</strong></strong></strong></h4>
<h5 style="text-align:justify;"><strong><strong><strong>4.1.1 基本概念</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在程序中&#xff0c;一个或多个运算对象的组合叫做“表达式”&#xff08;expression&#xff09;&#xff0c;我们可以把它看成用来做计算的“式子”。对一个表达式进行计算&#xff0c;可以得到一个结果&#xff0c;有时也把它叫做表达式的值。</p>
<p style="margin-left:.0001pt;text-align:justify;">前面讲到的字面值常量和变量&#xff0c;就是最简单的表达式&#xff1b;表达式的结果就是字面值和变量的值。而多个字面值和变量&#xff0c;可以通过一些符号连接组合在一起&#xff0c;表示进行相应的计算&#xff0c;这就可以得到更加复杂的表达式&#xff0c;比如 a &#43; 1。像“&#43;”这些符号就被叫做“运算符”&#xff08;operator&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中定义的运算符&#xff0c;可以是像“&#43;”这样连接两个对象&#xff0c;称为“二元运算符”&#xff1b;也可以只作用于一个对象&#xff0c;称为“一元运算符”。另外&#xff0c;还有一个比较特殊的运算符可以作用于三个对象&#xff0c;那就是三元运算符了。</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.1.2 运算</strong></strong><strong><strong>优先级和结合律</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">如果在一个表达式中&#xff0c;使用多个运算符组合了多个运算对象&#xff0c;就构成了更加复杂的“复合表达式”&#xff0c;比如 a &#43; 1 - b。对于复合表达式&#xff0c;很显然我们应该分步来做计算&#xff1b;而计算顺序&#xff0c;是由所谓的“优先级”和“结合律”确定的。</p>
<p style="margin-left:.0001pt;text-align:justify;">简单来说&#xff0c;就是对不同的运算符赋予不同的“优先级”&#xff0c;我们会优先执行高优先级的运算、再执行低优先级的运算。如果优先级相同&#xff0c;就按照“结合律”来决定执行顺序。这其实跟数学的综合算式是一样的&#xff0c;我们会定义乘除的优先级要高于加减&#xff0c;平级运算从左往右&#xff0c;所以对于算式&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">1 &#43; 2 – 3 × 4</p>
<p style="margin-left:.0001pt;text-align:justify;">我们会先计算高优先级的 3×4&#xff0c;然后按照从左到右的结合顺序计算1&#43;2&#xff0c;最后做减法。另外&#xff0c;如果有括号&#xff0c;那就要先把括起来的部分当成一个整体先做计算&#xff0c;然后再考虑括号外的结合顺序&#xff0c;这一点在C&#43;&#43;表达式中同样适用。</p>
<h4 style="text-align:justify;"><strong><strong><strong>4.2 </strong></strong><strong><strong>算术运算</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">最简单的运算符&#xff0c;就是表示算术计算的加减乘除&#xff0c;这一类被称为“算术运算符”。C&#43;&#43;支持的算术运算符如下&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/b0ba786414d24178b162cfcf260c6c1a.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意的是&#xff0c;同一个运算符&#xff0c;在不同的场合可能表达不同的含义。比如“-”&#xff0c;可以是“减号”也可以是“负号”&#xff1a;如果直接放在一个表达式前面&#xff0c;就是对表达式的结果取负数&#xff0c;这是一元运算符&#xff1b;如果连接两个表达式&#xff0c;就是两者结果相减&#xff0c;是二元运算符。</p>
<p style="margin-left:.0001pt;text-align:justify;">算术运算符相关规则如下&#xff1a;</p>
<ol><li>一元运算符&#xff08;正负号&#xff09;优先级最高&#xff1b;接下来是乘、除和取余&#xff1b;最后是加减&#xff1b;</li><li>算术运算符满足左结合律&#xff0c;也就是说相同优先级的运算符&#xff0c;将从左到右按顺序进行组合&#xff1b;</li><li>算术运算符可以用来处理任意算术类型的数据对象&#xff1b;</li><li>不同类型的数据对象进行计算时&#xff0c;较小的整数类型会被“提升”为较大的类型&#xff0c;最终转换成同一类型进行计算&#xff1b;</li><li>对于除法运算“/”&#xff0c;执行计算的结果跟操作数的类型有关。如果它的两个操作数&#xff08;也就是被除数和除数&#xff09;都是整数&#xff0c;那么得到的结果也只能是整数&#xff0c;小数部分会直接舍弃&#xff0c;这叫“整数除法”&#xff1b;当至少有一个操作数是浮点数时&#xff0c;结果就会是浮点数&#xff0c;保留小数部分&#xff1b;</li><li>对于取余运算“%”&#xff08;或者叫“取模”&#xff09;&#xff0c;两个操作数必须是整数类型&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 除法</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 20, b &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; a / b &#61; &#34; &lt;&lt; a / b &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; -a / b &#61; &#34; &lt;&lt; -a / b &lt;&lt; endl;    // 负数向0取整</p>
<p style="margin-left:.0001pt;text-align:left;">float a2 &#61; 20;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; a2 / b &#61; &#34; &lt;&lt; a2 / b &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 取模</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; a % b &#61; &#34; &lt;&lt; a % b &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; -a % b &#61; &#34; &lt;&lt; -a % b &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">在这里&#xff0c;同样是除法运算符“/”,针对不同类型的数据对象&#xff0c;其实会做不同的处理。使用相同的符号、根据上下文来执行不同操作&#xff0c;这是C&#43;&#43;提供的一大特色功能&#xff0c;叫做“运算符重载”&#xff08;operator overloading&#xff09;。</p>
<h4 style="text-align:justify;"><strong><strong><strong>4.3 赋值</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">将一个表达式的结果&#xff0c;传递给某个数据对象保存起来&#xff0c;这个过程叫做“赋值”。</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>3</strong></strong><strong><strong>.</strong></strong><strong><strong>1</strong></strong><strong> </strong><strong><strong>赋值运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;用等号“&#61;”表示一个赋值操作&#xff0c;这里的“&#61;”就是赋值运算符。需要注意的是&#xff0c;赋值运算符的左边&#xff0c;必须是一个可修改的数据对象&#xff0c;比如假设我们已经定义了一个int类型的变量a&#xff0c;那么</p>
<p style="margin-left:.0001pt;text-align:justify;">a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">这样赋值是对的&#xff0c;但</p>
<p style="margin-left:.0001pt;text-align:justify;">1 &#61; a;</p>
<p style="margin-left:.0001pt;text-align:justify;">就是错误的。因为a是一个变量&#xff0c;可以赋值&#xff1b;而 1只是一个字面值常量&#xff0c;不能再对它赋值。</p>
<p style="margin-left:.0001pt;text-align:left;">int a, b;</p>
<p style="margin-left:.0001pt;text-align:left;">a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">//1 &#61; a;    // 错误&#xff1a;表达式必须是可修改的左值</p>
<p style="margin-left:.0001pt;text-align:left;">a &#61; b &#43; 5;</p>
<p style="margin-left:.0001pt;text-align:justify;">//b &#43; 5 &#61; a;    // 错误&#xff1a;表达式必须是可修改的左值</p>
<p style="margin-left:.0001pt;text-align:left;">const int c &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:justify;">//c &#61; a &#43; b;    // 错误&#xff1a;表达式必须是可修改的左值</p>
<p style="margin-left:.0001pt;text-align:justify;">所以像变量a这样的可以赋值的运算对象&#xff0c;在C&#43;&#43;中被叫做“左值”&#xff08;lvalue&#xff09;&#xff1b;对应的&#xff0c;放在赋值语句右面的表达式就是“右值”&#xff08;rvalue&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">赋值运算有以下一些规则&#xff1a;</p>
<ol><li>赋值运算的结果&#xff0c;就是它左侧的运算对象&#xff1b;结果的类型就是左侧运算对象的类型&#xff1b;</li><li>如果赋值运算符两侧对象类型不同&#xff0c;就把右侧的对象转换成左侧对象的类型&#xff1b;</li><li>C&#43;&#43; 11新标准提供了一种新的语法&#xff1a;用花括号{}括起来的数值列表&#xff0c;可以作为赋值右侧对象。这样就可以非常方便地对一个数组赋值了&#xff1b;</li><li>赋值运算满足右结合律。也就是说可以在一条语句中连续赋值&#xff0c;结合顺序是从右到左&#xff1b;</li><li>赋值运算符优先级较低&#xff0c;一般都会先执行其它运算符&#xff0c;最后做赋值&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:left;">a &#61; {2};</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[] &#61; {1,2,3,4,5};    // 用花括号对数组赋值</p>
<p style="margin-left:.0001pt;text-align:justify;">a &#61; b &#61; 20;    // 连续赋值</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.3.2 复合赋值运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">实际应用中&#xff0c;我们经常需要把一次计算的结果&#xff0c;再赋值给参与运算的某一个变量。最简单的例子就是多个数求和&#xff0c;比如我们要计算a、b、c的和&#xff0c;那么可以专门定义一个变量sum&#xff0c;用来保存求和结果&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">int sum &#61; a;    // 初始值是a</p>
<p style="margin-left:.0001pt;text-align:left;">sum &#61; sum &#43; b;    // 叠加b</p>
<p style="margin-left:.0001pt;text-align:justify;">sum &#61; sum &#43; c;    // 叠加c</p>
<p style="margin-left:.0001pt;text-align:justify;">要注意赋值运算符“&#61;”完全不是数学上“等于”的意思&#xff0c;所以上面的赋值语句sum &#61; sum &#43; b; 说的是“计算sum &#43; b的结果&#xff0c;然后把它再赋值给sum”。</p>
<p style="margin-left:.0001pt;text-align:justify;">为了更加简洁&#xff0c;C&#43;&#43;提供了一类特殊的赋值运算符&#xff0c;可以把要执行的算术运算“&#43;”跟赋值“&#61;”结合在一起&#xff0c;用一个运算符“&#43;&#61;”来表示&#xff1b;这就是“复合赋值运算符”。</p>
<p style="margin-left:.0001pt;text-align:justify;">复合赋值一般结合的是算术运算符或者位运算符。每种运算符都有对应的组合形式&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/fb82c2c0031e4c3b98ac393c3eac166a.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">关于位运算符&#xff0c;我们会在稍后介绍。</p>
<p style="margin-left:.0001pt;text-align:justify;">这样上面的代码可以改写为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">int sum &#61; a;    // 初始值是a</p>
<p style="margin-left:.0001pt;text-align:left;">sum &#43;&#61; b;    // 完全等价于 sum &#61; sum &#43; b;</p>
<p style="margin-left:.0001pt;text-align:justify;">sum &#43;&#61; c;</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>3</strong></strong><strong><strong>.3 </strong></strong><strong><strong>递增递减运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;为数据对象的“加一”“减一”操作&#xff0c;提供了更加简洁的表达方式&#xff0c;这就是递增和递减运算符&#xff08;也叫“自增”“自减”运算符&#xff09;。“递增”用两个加号“&#43;&#43;”表示&#xff0c;表示“对象值加一&#xff0c;再赋值给原对象”&#xff1b;“递减”则用两个减号“--”表示。</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;a;    // a递增&#xff0c;相当于 a &#43;&#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">--b;    // b递减&#xff0c;相当于 b -&#61; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">递增递减运算符各自有两种形式&#xff1a;“前置”和“后置”&#xff0c;也就是说写成“&#43;&#43;a”和“a&#43;&#43;”都是可以的。它们都表示“a &#61; a &#43; 1”&#xff0c;区别在于表达式返回的结果不同&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">前置时&#xff0c;对象先加1&#xff0c;再将更新之后的对象值作为结果返回&#xff1b;</p>
<p style="margin-left:.0001pt;text-align:justify;">后置时&#xff0c;对象先将原始值作为结果返回&#xff0c;再加1&#xff1b;</p>
<p style="margin-left:.0001pt;text-align:justify;">这要特别注意&#xff1a;如果我们单独使用递增递减运算符&#xff0c;那前置后置效果都一样&#xff1b;但如果运算结果还要进一步做计算&#xff0c;两者就有明显不同了。</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 0, j;</p>
<p style="margin-left:.0001pt;text-align:left;">j &#61; &#43;&#43;i;   // i &#61; 1&#xff0c;j &#61; 1</p>
<p style="margin-left:.0001pt;text-align:justify;">j &#61; i--;     // i &#61; 0, j &#61; 1</p>
<p style="margin-left:.0001pt;text-align:justify;">在实际应用中&#xff0c;一般都是希望用改变之后的对象值&#xff1b;所以为了避免混淆&#xff0c;我们通常会统一使用前置的写法。</p>
<h4 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>4</strong></strong><strong> </strong><strong><strong>关系和逻辑运算</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">在程序中&#xff0c;不可缺少的一类运算就是逻辑和关系运算&#xff0c;因为我们往往需要定义“在某种条件发生时&#xff0c;执行某种操作”。判断条件是否发生&#xff0c;这就是一个典型的逻辑判断&#xff1b;得到的结果或者为“真”&#xff08;true&#xff09;&#xff0c;或者为“假”。很显然&#xff0c;这类运算的结果应该是布尔类型。</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>4</strong></strong><strong><strong>.1 </strong></strong><strong><strong>关系运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">最简单的一种条件&#xff0c;就是判断两个算术对象的大小关系&#xff0c;对应的运算符称为“关系运算符”。包括&#xff1a;大于“&gt;”、小于“&lt;”、等于“&#61;&#61;”、不等于“!&#61;”、大于等于“&gt;&#61;”、小于等于“&lt;&#61;”。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/553d55168de74a39896000951e3ee526.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">这里要注意区分的是&#xff0c;在C&#43;&#43;语法中一个等号“&#61;”表示的是赋值&#xff0c;两个等号“&#61;&#61;”才是真正的“等于”。</p>
<p style="margin-left:.0001pt;text-align:left;">1 &lt; 2;    // true</p>
<p style="margin-left:.0001pt;text-align:left;">3 &gt;&#61; 5;    // false</p>
<p style="margin-left:.0001pt;text-align:justify;">10 &#61;&#61; 4 &#43; 6;    // true</p>
<p style="margin-left:.0001pt;text-align:justify;">(10 !&#61; 4) &#43; 6;    // 7</p>
<p style="margin-left:.0001pt;text-align:justify;">关系运算符的相关规则&#xff1a;</p>
<ol><li>算术运算符的优先级高于关系运算符&#xff0c;而如果加上括号就可以调整计算顺序&#xff1b;</li><li>关系运算符的返回值为布尔类型&#xff0c;如果参与算术计算&#xff0c;true的值为1&#xff0c;false的值为0&#xff1b;</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>4</strong></strong><strong><strong>.2 </strong></strong><strong><strong>逻辑运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">一个关系运算符的结果是一个布尔类型&#xff08;ture或者false&#xff09;&#xff0c;就可以表示一个条件的判断&#xff1b;如果需要多个条件的叠加&#xff0c;就可以用逻辑“与或非”将这些布尔类型组合起来。这样的运算符叫做“逻辑运算符”。</p>
<ol><li>逻辑非&#xff08;&#xff01;&#xff09;&#xff1a;一元运算符&#xff0c;将运算对象的值取反后返回&#xff0c;真值反转&#xff1b;</li><li>逻辑与&#xff08;&amp;&amp;&#xff09;&#xff1a;二元运算符&#xff0c;两个运算对象都为true时结果为true&#xff0c;否则结果为false&#xff1b;</li><li>逻辑或&#xff08;||&#xff09;&#xff1a;二元运算符&#xff0c;两个运算对象只要有一个为true结果就为true&#xff0c;都为false则结果为false&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:left;">1 &lt; 2 &amp;&amp; 3 &gt;&#61; 5;    // false</p>
<p style="margin-left:.0001pt;text-align:left;">1 &lt; 2 || 3 &gt;&#61; 5;    // true</p>
<p style="margin-left:.0001pt;text-align:justify;">!(1 &lt; 2 || 3 &gt;&#61; 5);    // false</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以把逻辑运算符和关系运算符的用法、优先级和结合律总结如下&#xff08;从上到下优先级递减&#xff09;&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/e0e87a1bc11e4bd797431e30f21d3520.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意的规则有&#xff1a;</p>
<ol><li>如果将一个算术类型的对象作为逻辑运算符的操作数&#xff0c;那么值为0表示false&#xff0c;非0值表示true&#xff1b;</li><li>逻辑与和逻辑或有两个运算对象&#xff0c;在计算时都是先求左侧对象的值&#xff0c;再求右侧对象的值&#xff1b;如果左侧对象的值已经能决定最终结果&#xff0c;那么右侧就不会执行计算&#xff1a;这种策略叫做“<strong>短路求值</strong>”&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:left;">i &#61; -1;</p>
<p style="margin-left:.0001pt;text-align:left;">1 &lt; 2 &amp;&amp; &#43;&#43;i;  // false</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; i &#61; &#34; &lt;&lt; i &lt;&lt; endl;       // i &#61; 0</p>
<p style="margin-left:.0001pt;text-align:left;">1 &lt; 2 || &#43;&#43;i;  // true</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; i &#61; &#34; &lt;&lt; i &lt;&lt; endl;       // i &#61; 0</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>4</strong></strong><strong><strong>.3 </strong></strong><strong><strong>条件运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;还从C语言继承了一个特殊的运算符&#xff0c;叫做“条件运算符”。它由“?”和“:”两个符号组成&#xff0c;需要三个运算表达式&#xff0c;形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>条件判断表达式 ? 表达式1 : 表达式2</em></p>
<p style="margin-left:.0001pt;text-align:justify;">它的含义是&#xff1a;计算条件判断表达式的值&#xff0c;如果为true就执行表达式1&#xff0c;返回求值结果&#xff1b;如果为false则跳过表达式1&#xff0c;执行表达式2&#xff0c;返回求值结果。这也是C&#43;&#43;中唯一的一个三元运算符。</p>
<p style="margin-left:.0001pt;text-align:left;">i &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; ((1 &lt; 2 &amp;&amp; &#43;&#43;i) ? &#34;true&#34; : &#34;false&#34;) &lt;&lt; endl;</p>
<ol><li>条件运算符的优先级比较低&#xff0c;所以输出的时候需要加上括号</li><li>条件运算符满足右结合律</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">事实上&#xff0c;条件运算符等同于流程控制中的分支语句if...else...&#xff0c;只用一条语句就可以实现按条件分支处理&#xff0c;这就让代码更加简洁。关于分支语句&#xff0c;我们会在后面详细介绍。</p>
<h4 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>5</strong></strong><strong> </strong><strong><strong>位运算符</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">之前介绍的所有运算符&#xff0c;主要都是针对算术类型的数据对象进行操作的&#xff1b;所有的算术类型&#xff0c;占用的空间都是以字节&#xff08;byte&#xff0c;8位&#xff09;作为单位来衡量的。在C&#43;&#43;中&#xff0c;还有一类非常底层的运算符&#xff0c;可以直接操作到具体的每一位&#xff08;bit&#xff09;数据&#xff0c;这就是“位运算符”。</p>
<p style="margin-left:.0001pt;text-align:justify;">位运算符可以分为两大类&#xff1a;移位运算符&#xff0c;和位逻辑运算符。下面列出了所有位运算符的优先级和用法。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/ad4f93ced3c94aa889469ae62f201316.png"  />
</p>
<h5 style="text-align:justify;"><strong><strong><strong>4</strong></strong><strong><strong>.5.1</strong></strong><strong><strong>移位运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">算术类型的数据对象&#xff0c;都可以看做是一组“位”的集合。那么利用“移位运算符”&#xff0c;就可以让运算对象的所有位&#xff0c;整体移动指定的位数。</p>
<p style="margin-left:.0001pt;text-align:justify;">移位运算符有两种&#xff1a;左移运算符“&lt;&lt;”和右移运算符“&gt;&gt;”。这个符号我们并不陌生&#xff0c;之前做输入输出操作的时候用的就是它&#xff0c;不过那是标准IO库里定义的运算符重载版本。</p>
<p style="margin-left:.0001pt;text-align:justify;">下面是移位运算符的一个具体案例&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/139eaba23e974de48118708a49a27200.png"  />
</p>
<ol><li>较小的整数类型&#xff08;char、short以及bool&#xff09;会自动提升成int类型再做移位&#xff0c;得到的结果也是int类型</li><li>左移运算符“&lt;&lt;”将操作数左移之后&#xff0c;在右侧补0&#xff1b;</li><li>右移运算符“&gt;&gt;”将操作数右移之后&#xff0c;对于无符号数就在左侧补0&#xff1b;对于有符号数的操作则要看运行的机器环境&#xff0c;有可能补符号位&#xff0c;也有可能直接补0&#xff1b;</li><li>由于有符号数右移结果不确定&#xff0c;一般只对无符号数执行位移操作&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">unsigned char bits &#61; 0xb5;    // 181</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; hex;    // 以十六进制显示</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;0xb5 左移2位&#xff1a;&#34; &lt;&lt; (bits &lt;&lt; 2) &lt;&lt; endl;    // 0x 0000 02d4</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;0xb5 左移8位&#xff1a;&#34; &lt;&lt; (bits &lt;&lt; 8) &lt;&lt; endl;    // 0x 0000 b500</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;0xb5 左移31位&#xff1a;&#34; &lt;&lt; (bits &lt;&lt; 31) &lt;&lt; endl;    // 0x 8000 0000</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;0xb5 右移3位&#xff1a;&#34; &lt;&lt; (bits &gt;&gt; 3) &lt;&lt; endl;    // 0x 0000 0016</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; dec;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; (200 &lt;&lt; 3) &lt;&lt; endl;    // 乘8操作</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; (-100 &gt;&gt; 2) &lt;&lt; endl;    // 除4操作&#xff0c;一般右移是补符号位</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>4</strong></strong><strong><strong>.5.2 </strong></strong><strong><strong>位逻辑运算符</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">计算机存储的每一个“位”&#xff08;bit&#xff09;都是二进制的&#xff0c;有0和1两种取值&#xff0c;这跟布尔类型的真值表达非常类似。于是自然可以想到&#xff0c;两个位上的“0”或“1”都可以执行类似逻辑运算的操作。</p>
<p style="margin-left:.0001pt;text-align:justify;">位逻辑运算符有&#xff1a;按位取反“~”&#xff0c;位与“&amp;”&#xff0c;位或“|”和位异或“^”。</p>
<ol><li>按位取反“~”&#xff1a;一元运算符&#xff0c;类似逻辑非。对每个位取反值&#xff0c;也就是把1置为0、0置为1&#xff1b;</li><li>位与“&amp;”&#xff1a;二元运算符&#xff0c;类似逻辑与。两个数对应位上都为1&#xff0c;结果对应位为1&#xff1b;否则结果对应位为0&#xff1b;</li><li>位或“|”&#xff1a;二元运算符&#xff0c;类似逻辑或。两个数对应位上只要有1&#xff0c;结果对应位就为1&#xff1b;如果全为0则结果对应位为0&#xff1b;</li><li>位异或“^”&#xff1a;两个数对应位相同&#xff0c;则结果对应位为0&#xff1b;不同则结果对应位为0&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">下面是位逻辑运算符的一个具体案例&#xff1a;</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/98d30cf372bf454d8b25bb60cd206fab.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 位逻辑运算</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; (~5) &lt;&lt; endl;    // ~ (0... 0000 0101) &#61; 1... 1111 1010,  -6</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; (5 &amp; 12) &lt;&lt; endl;   // 0101 &amp; 1100 &#61; 0100, 4</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; (5 | 12) &lt;&lt; endl;   // 0101 | 1100 &#61; 1101, 13</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; (5 ^ 12) &lt;&lt; endl;    // 0101 &amp; 1100 &#61; 1001, 9</p>
<h4 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>5</strong></strong><strong> </strong><strong><strong>类型转换</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;不同类型的数据对象&#xff0c;是可以放在一起做计算的。这就要求必须有一个机制&#xff0c;能让有关联的两种类型可以互相转换。在上一章已经介绍过变量赋值时的自动类型转换&#xff0c;接下来我们会对类型转换做更详细的展开。</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>5</strong></strong><strong><strong>.1 </strong></strong><strong><strong>隐式类型转换</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">大多数情况&#xff0c;C&#43;&#43;编译器可以自动对类型进行转换&#xff0c;不需要我们干涉&#xff0c;这种方式叫做“隐式类型转换”。</p>
<p style="margin-left:.0001pt;text-align:justify;">隐式类型转换主要发生在算术类型之间&#xff0c;基本思路就是将长度较小的类型转换成较大的类型&#xff0c;这样可以避免丢失精度。隐式类型转换不仅可以在变量赋值时发生&#xff0c;也可以在运算表达式中出现。例如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">short s &#61; 15.2 &#43; 20;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; s &#61; &#34; &lt;&lt; s &lt;&lt; endl;    // s &#61; 35</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; 15.2 &#43; 20 结果长度为&#xff1a;&#34; &lt;&lt; sizeof(15.2 &#43; 20) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; s 长度为&#xff1a;&#34; &lt;&lt; sizeof(s) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">对于这条赋值语句&#xff0c;右侧是两个字面值常量相加&#xff0c;而且类型不同&#xff1a;15.2是double类型&#xff0c;20是int类型。当它们相加时&#xff0c;会将int类型的20转换为double类型&#xff0c;然后执行double的加法操作&#xff0c;得到35.2。</p>
<p style="margin-left:.0001pt;text-align:justify;">这个结果用来初始化变量s&#xff0c;由于s是short类型&#xff0c;所以还会把double类型的结果35.2再去掉小数部分&#xff0c;转换成short类型的35。所以s最终的值为35。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">隐式类型转换的一般规则可以总结如下&#xff1a;</p>
<ol><li>在大多数算术运算中&#xff0c;较小的整数类型(如bool、char、short)都会转换成int类型。这叫做“整数提升”&#xff1b;&#xff08;而对于wchar_t等较大的扩展字符类型&#xff0c;则根据需要转换成int、unsigned int、long、unsigned long、long long、unsigned long long中能容纳它的最小类型&#xff09;</li><li>当表达式中有整型也有浮点型时&#xff0c;整数值会转换成相应的浮点类型&#xff1b;</li><li>在条件判断语句中&#xff0c;其它整数类型会转换成布尔类型&#xff0c;即0为false、非0为true&#xff1b;</li><li>初始化变量时&#xff0c;初始值转换成变量的类型&#xff1b;</li><li>在赋值语句中&#xff0c;右侧对象的值会转换成左侧对象的类型&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">此外&#xff0c;要尽量避免将较大类型的值赋给较小类型的变量&#xff0c;这样很容易出现精度丢失或者数据溢出。</p>
<p style="margin-left:.0001pt;text-align:left;">s &#61; 32767;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; s &#43; 1 &#61; &#34; &lt;&lt; s &#43; 1 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">short s2 &#61; s &#43; 1;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; s2 &#61; &#34; &lt;&lt; s2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">另外还要注意&#xff0c;如果希望判断一个整型变量a是否在某个范围&#xff08;0&#xff0c; 100&#xff09;内&#xff0c;不能直接写&#xff1a;0 &lt; a &lt; 100;</p>
<p style="margin-left:.0001pt;text-align:justify;">由于小于运算符“&lt;”满足左结合律&#xff0c;要先计算0 &lt; a&#xff0c;得到一个布尔类型的结果&#xff0c;再跟后面的100进行比较。此时布尔类型做整数提升&#xff0c;不管值是真&#xff08;1&#xff09;还是假&#xff08;0&#xff09;&#xff0c;都会满足 &lt; 100 的判断&#xff0c;因此最终结果一定是true。</p>
<p style="margin-left:.0001pt;text-align:justify;">要想得到正确的结果&#xff0c;需要将两次关系判断拆开&#xff0c;写成逻辑与的关系。</p>
<p style="margin-left:.0001pt;text-align:left;">a &#61; -1;              </p>
<p style="margin-left:.0001pt;text-align:left;">0 &lt; a &lt; 100;         // 不论a取什么值&#xff0c;总是true</p>
<p style="margin-left:.0001pt;text-align:justify;">0 &lt; a &amp;&amp; a &lt; 100;    // false</p>
<h5 style="text-align:justify;"><strong><strong><strong>4.</strong></strong><strong><strong>5</strong></strong><strong><strong>.2 </strong></strong><strong><strong>强制类型转换</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">除去自动进行的隐式类型转换&#xff0c;我们也可以显式地要求编译器对数据对象的类型进行更改。这种转换叫做“强制类型转换”&#xff08;cast&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如对于除法运算&#xff0c;我们知道整数除法和浮点数除法是不同的。如果希望对一组整数求一个平均数&#xff0c;直接相加后除以个数是无法得到想要的结果的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 求平均数</p>
<p style="margin-left:.0001pt;text-align:left;">int total &#61; 20, num &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">double avg &#61; total / num;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; avg &#61; &#34; &lt;&lt; avg &lt;&lt; endl;    // avg &#61; 3</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">因为两个int类型的数相除&#xff0c;执行的是整数除法&#xff0c;得到3&#xff1b;再转换成double类型对avg做初始化&#xff0c;得到是3.0。如果想要更准确的结果&#xff0c;就必须将int类型强制转换成double&#xff0c;做浮点数除法。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中可以使用不同的方式进行强制类型转换。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;C语言风格</p>
<p style="margin-left:.0001pt;text-align:justify;">最经典的强转方式来自C语言&#xff0c;格式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">(<em>类型名称</em>)<em> 值</em></p>
<p style="margin-left:.0001pt;text-align:justify;">把要强制转成的类型&#xff0c;用一个小括号括起来&#xff0c;放到要转换的对象值前面就可以了。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;C&#43;&#43;函数调用风格</p>
<p style="margin-left:.0001pt;text-align:justify;">这种方式跟C语言的强转类似&#xff0c;只不过看起来更像是调用了一个函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>类型名称</em> (<em>值</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;">要转成的类型名就像是一个函数&#xff0c;调用的时候&#xff0c;后面小括号里是传递给它的参数。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;C&#43;&#43;强制类型转换运算符</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;还引入了4个强制类型转换运算符&#xff0c;这种新的转换方式比前两种传统方式要求更为严格。通常在类型转换中用到的运算符是static_cast&#xff0c;用法如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">static_cast&lt;<em>类型名称</em>&gt; (<em>值</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;">static_cast运算符后要跟一个尖括号&#xff0c;里面是要转换成的类型。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">有了这些强转的方式&#xff0c;就可以解决之前求平均数的问题了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// C语言风格</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; avg &#61; &#34; &lt;&lt; (double) total / num &lt;&lt; endl;  </p>
<p style="margin-left:.0001pt;text-align:left;">// C&#43;&#43;函数风格  </p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; avg &#61; &#34; &lt;&lt; double (total) / num &lt;&lt; endl;   </p>
<p style="margin-left:.0001pt;text-align:left;">// C&#43;&#43;强转运算符</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; avg &#61; &#34; &lt;&lt; static_cast&lt;double&gt;(total) / num &lt;&lt; endl;    </p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">强制类型转换会干扰正常的类型检查&#xff0c;带来很多风险&#xff0c;所以通常要尽量避免使用强制类型转换。</p>
<h3 style="text-align:justify;"><strong><strong><strong>五、流程控制语句</strong></strong></strong></h3>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;程序执行的流程结构可以有三种&#xff1a;顺序、分支和循环。除了最简单的顺序结构是默认的&#xff0c;分支和循环都需要使用专门的“流程控制语句”来定义。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/5be2249539054a16a06ef41fea2ac8e5.png"  />
</p>
<h4 style="text-align:justify;"><strong><strong><strong>5.1 </strong></strong><strong><strong>语句</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中表示一步操作的一句代码&#xff0c;就叫做“语句”&#xff08;statement&#xff09;&#xff0c;大多数语句都是以分号“;”结尾的。C&#43;&#43;程序运行的过程&#xff0c;其实就是找到主函数&#xff0c;然后从上到下顺序执行每条语句的过程。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.1.1 </strong></strong><strong><strong>简单语句</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">使用各种运算符&#xff0c;作用到数据对象上&#xff0c;就得到了“表达式”&#xff1b;一个表达式末尾加上分号&#xff0c;就构成了“表达式语句”&#xff08;expression statement&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">表达式语句表示&#xff0c;要执行表达式的计算过程&#xff0c;并且丢弃最终返回的结果。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 0;    // 变量定义并初始化语句</p>
<p style="margin-left:.0001pt;text-align:left;">a &#43; 1;        // 算术表达式语句&#xff0c;无意义</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;a;          // 递增语句&#xff0c;a的值变为1</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34; a &#61; &#34; &lt;&lt; a &lt;&lt; endl;    // 输出语句</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">其中第二行a &#43; 1; 是没什么意义的&#xff0c;因为它只是执行了加法操作&#xff0c;却没有把结果保存下来&#xff08;赋值给别的变量&#xff09;&#xff0c;a的值也没有改变&#xff0c;也没有任何附带效果&#xff08;比如最后一句的输出&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">最简单的语句&#xff0c;其实是“空语句”&#xff0c;就是只有一个分号的语句&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">;      // 空语句</p>
<p style="margin-left:.0001pt;text-align:justify;">这看起来好像没什么用。不过有时候&#xff0c;可能程序在语法上需要有一条语句&#xff0c;而逻辑上什么都不用做&#xff1b;这时就应该用一条空语句来填充。</p>
<p style="margin-left:.0001pt;text-align:justify;">初学C&#43;&#43;&#xff0c;一定不要忘记语句末尾的分号&#xff1b;当然&#xff0c;对于不需要分号的场景&#xff0c;也尽量避免多写分号。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.1.2 复合语句</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">简单语句从上到下按顺序依次执行&#xff0c;这非常符合我们对计算机运行的预期。但是很多场景下&#xff0c;简单的顺序结构远远不能满足逻辑需要&#xff1a;比如我们可能需要按照条件判断&#xff0c;做程序的分支执行&#xff1b;也可能需要将一段代码循环执行多次。这就需要一些“流程控制语句”&#xff08;比如if、while、for等&#xff09;来表达更加复杂的操作了。</p>
<p style="margin-left:.0001pt;text-align:justify;">而对于流程控制语句&#xff0c;逻辑上来说只是一条语句&#xff1b;事实上却可能包含了多条语句、复杂的操作。这就需要用一个花括号“{}”&#xff0c;把这一组语句序列包成一个整体&#xff0c;叫做“复合语句”&#xff08;compound statement&#xff09;&#xff0c;也叫做“块”&#xff08;block&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">while (i &lt; 5) {</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; i;</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;i;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里的while表示一个循环&#xff0c;后面只能跟要循环执行的一条语句&#xff1b;如果我们想写两条语句&#xff0c;就要用花括号括起来&#xff0c;构成“块”。</p>
<p style="margin-left:.0001pt;text-align:justify;">对于复合语句&#xff08;块&#xff09;需要注意&#xff1a;</p>
<ol><li>花括号后面不需要再加分号&#xff0c;块本身就是一条语句&#xff1b;</li><li>块内可以声明变量&#xff0c;变量的作用域仅限于块内部&#xff1b;</li><li>只有一对花括号、内部没有任何语句的块叫做“空块”&#xff0c;等价于空语句&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h4 style="text-align:justify;"><strong><strong><strong>5.2 条件</strong></strong><strong><strong>分支</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">很多情况下&#xff0c;我们为程序的执行会提供“岔路”的选择机会。一般都是&#xff1a;满足某种条件就执行A操作&#xff0c;满足另一种条件就执行B操作……这样的程序结构叫做“条件分支”。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;提供了两种按条件分支执行的控制语句&#xff1a;if和switch。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.2.1 if</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">if语句主要就是判断一个条件是否为真&#xff08;true&#xff09;&#xff0c;如果为真就执行下面的语句&#xff0c;如果为假则跳过。具体形式可以分为两种&#xff1a;一种是单独一个if&#xff0c;一般称为“单分支”&#xff1b;另一种是if … else …&#xff0c;称为“双分支”。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/6940ecae17264e849304fe44bc34db5e.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;单分支</p>
<p style="margin-left:.0001pt;text-align:justify;">单分支是最简单的if用法&#xff0c;判断的条件用小括号括起来跟在if后面&#xff0c;然后是如果条件为真要执行的语句。基本形式为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">if (<em>条件判断</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句</em></p>
<p style="margin-left:.0001pt;text-align:justify;">如果条件为假&#xff0c;那么这段代码就会被完全跳过。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以举一个简单示例&#xff0c;判断输入的年龄数值&#xff0c;然后输出一句欢迎词&#xff1a;</p>

<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;请输入您的芳龄&#xff1a;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">int age;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; age;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">if ( age &gt;&#61; 18 )</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;欢迎您&#xff0c;成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">通常会用一个花括号将if后面的语句括起来&#xff0c;成为一个“块”。现在块里只有一条语句&#xff0c;所以花括号是可以省略的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">if ( age &gt;&#61; 18 )</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;欢迎您&#xff0c;成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">如果要执行的是多条语句&#xff0c;花括号就不能省略&#xff1b;否则if后面其实就只有第一条语句。为了避免漏掉括号出现错误&#xff0c;一般if后面都会使用花括号。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;双分支</p>
<p style="margin-left:.0001pt;text-align:justify;">双分支就是在if分支的基础上&#xff0c;加了else分支&#xff1b;条件为真就执行if后面的语句&#xff0c;条件为假就执行else后面的语句。基本形势如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">if (<em>条件判断</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句1</em></p>
<p style="margin-left:.0001pt;text-align:justify;">else</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句2</em></p>
<p style="margin-left:.0001pt;text-align:justify;">if分支和else分支&#xff0c;两者肯定会选择一个执行。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以在之前程序的基础上&#xff0c;增加一个else分支&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">if ( age &gt;&#61; 18 )</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;欢迎您&#xff0c;成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;本程序不欢迎未成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以回忆起来&#xff0c;之前介绍过的唯一一个三元运算符——条件运算符&#xff0c;其实就可以实现类似的功能。所以条件运算符可以认为是if … else 的一个语法糖。</p>
<p style="margin-left:.0001pt;text-align:justify;">以下两条语句跟上面的if…else是等价的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 条件运算符的等价写法</p>
<p style="margin-left:.0001pt;text-align:left;">age &gt;&#61; 18 ? cout &lt;&lt; &#34;欢迎您&#xff0c;成年人&#xff01;&#34; &lt;&lt; endl : cout &lt;&lt; &#34;本程序不欢迎未成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; (age &gt;&#61; 18 ? &#34;欢迎您&#xff0c;成年人&#xff01;&#34; : &#34;本程序不欢迎未成年人&#xff01;&#34;) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;嵌套分支&#xff08;多分支&#xff09;</p>
<p style="margin-left:.0001pt;text-align:justify;">程序中的分支有可能不只两个&#xff0c;这时就需要对if分支或者else分支再做条件判断和拆分了&#xff0c;这就是“嵌套分支”。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/2331435ea7f24c5a969c4ff21f58f286.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">简单来说&#xff0c;就是if或者else分支的语句块里&#xff0c;继续使用if或者if…else按条件进行分支。这是一种“分层”的条件判断。</p>

<p style="margin-left:.0001pt;text-align:left;">if ( age &gt;&#61; 18 )</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;欢迎您&#xff0c;成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">if (age &lt; 35)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;加油&#xff0c;年轻人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;本程序不欢迎未成年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">if (age &gt;&#61; 12)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;少年&#xff0c;好好学习&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;小朋友&#xff0c;别玩电脑&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>

<p style="margin-left:.0001pt;text-align:justify;">嵌套分支如果比较多&#xff0c;代码的可读性会大幅降低。所以还有一种更加简单的嵌套分支写法&#xff0c;那就是if … else if …&#xff0c;具体形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">if (<em>条件判断1</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句1</em></p>
<p style="margin-left:.0001pt;text-align:justify;">else if (<em>条件判断2</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句2</em></p>
<p style="margin-left:.0001pt;text-align:justify;">else if (<em>条件判断3</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句3</em></p>
<p style="margin-left:.0001pt;text-align:justify;"><em>…</em></p>
<p style="margin-left:.0001pt;text-align:justify;">else</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句n</em></p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这种分支的嵌套&#xff0c;本质上只能对else分支进行&#xff0c;而且只能在最底层的分支中才能执行语句。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/4c3099cb05f44e4dab05864599d8f1d6.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">测试代码如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">if (age &lt; 12) {</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;小朋友&#xff0c;别玩电脑&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else if (age &lt; 18)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;少年&#xff0c;好好学习&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else if (age &lt; 35)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;加油&#xff0c;年轻人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else if (age &lt; 60)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;加油&#xff0c;中年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;好好休息&#xff0c;老年人&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.2.2 switch</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在一些应用场景中&#xff0c;要判断的条件可能不是范围&#xff0c;而是固定的几个值。比如考试成绩只分“A”“B”“C”“D”四个档位&#xff0c;分别代表“优秀”“良好”“及格”“不及格”。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/e9e491c4aeb141bbb6095559c4dd7881.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">这个时候如果用if … else会显得非常繁琐&#xff0c;而swith语句就是专门为了这种分支场景设计的。</p>
<p style="margin-left:.0001pt;text-align:justify;">switch语法基本形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">swith (<em>表达式</em>){</p>
<p style="margin-left:.0001pt;text-align:justify;">case <em>值1</em>:</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句1</em></p>
<p style="margin-left:.0001pt;text-align:justify;">break;</p>
<p style="margin-left:.0001pt;text-align:justify;">case <em>值2</em>:</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句2</em></p>
<p style="margin-left:.0001pt;text-align:justify;">break;</p>
<p style="margin-left:.0001pt;text-align:justify;">…</p>
<p style="margin-left:.0001pt;text-align:justify;">default:</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句n</em></p>
<p style="margin-left:.0001pt;text-align:justify;">break;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里switch后面的括号里是一个表达式&#xff0c;对它求值&#xff0c;然后转换成整数类型跟下面每个case后面的值做比较&#xff1b;如果相等&#xff0c;就进入这个case指定的分支&#xff0c;执行后面的语句&#xff0c;直到swith语句结束或者遇到break退出。需要注意的是&#xff1a;</p>
<ol><li>case关键字和后面对应的值&#xff0c;合起来叫做一个“case标签”&#xff1b;case标签必须是一个<strong>整型</strong>的常量表达式&#xff1b;</li><li>任何两个case标签不能相同&#xff1b;</li><li>break语句的作用是“中断”&#xff0c;会直接跳转到switch语句结构的外面&#xff1b;</li><li>如果没有break语句&#xff0c;那么匹配某个case标签之后&#xff0c;程序会从上到下一直执行下去&#xff1b;这会执行多个标签下面的语句&#xff0c;可能发生错误&#xff1b;</li><li>如果没有匹配上任何case标签的值&#xff0c;程序会执行default标签后面的语句&#xff1b;default是可选的&#xff0c;表示“默认要执行的操作”。</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以利用swith写一个判断考试成绩档位&#xff0c;输入一句相应的话&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;请输入您的成绩&#xff1a;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">char score;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; score;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">switch (score)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">case &#39;A&#39;:</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;成绩优秀&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">case &#39;B&#39;:</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;成绩良好&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">case &#39;C&#39;:</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;恭喜&#xff01;及格了&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">case &#39;D&#39;:</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;欢迎下次再来&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">default:</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;错误的成绩输入&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h4 style="text-align:justify;"><strong><strong><strong>5.3 </strong></strong><strong><strong>循环</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">可以重复执行一组操作的语句叫做“循环”&#xff0c;有时也叫作“迭代”。循环一般不能无限进行下去&#xff0c;所以会设置一个终止的判断条件。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中的循环语句&#xff0c;有while、do while和for三种。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.3.1 while</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">while只需要给定一个判断条件&#xff0c;只要条件为真&#xff0c;就重复地执行语句。形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">while (<em>条件</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句</em></p>
<p style="margin-left:.0001pt;text-align:justify;">要执行的语句往往会有多条&#xff0c;这就需要用花括号将它们括起来。这个块一般被称为“循环体”。</p>
<p style="margin-left:.0001pt;text-align:justify;">一般来说&#xff0c;用来控制while循环的条件中一定会包含变量&#xff0c;通常叫做“循环变量”&#xff1b;而它或者在条件中变化&#xff0c;或者在循环体中变化&#xff0c;这样才能保证循环能够终止退出。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如我们可以用一个循环输出10次“Hello World”&#xff0c;并且打印出当前循环次数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;循环开始...\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">while (i &lt;&#61; 10)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;现在是第&#34; &lt;&lt; i &lt;&lt; &#34;次循环\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;i;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;循环结束!&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意&#xff0c;循环体最后的 &#43;&#43;i一定不能漏掉。如果没有这条语句&#xff0c;i的值就不会更改&#xff0c;循环就永远不会退出。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.3.2 do while</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">do while和while非常类似&#xff0c;区别在于do while是先执行循环体中的语句&#xff0c;然后再检查条件是否满足。所以do while至少会执行一次循环体。</p>
<p style="margin-left:.0001pt;text-align:justify;">do while语法形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">do</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句</em></p>
<p style="margin-left:.0001pt;text-align:justify;">while (<em>条件</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以接着之前while循环的代码继续测试&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">do</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;现在是倒数第&#34; &lt;&lt; --i &lt;&lt; &#34;次循环&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;GoodBye World!\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">} while (i &gt; 1);</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">由于之前的变量i已经做了10次递增&#xff0c;因此do wihle开始时i的值为11。进入循环体直接输出内容&#xff0c;每次i递减1&#xff0c;直到i &#61; 1时退出循环。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.3.3 for</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">通过while和do while可以总结出来&#xff0c;一个循环主要有这样几个要素&#xff1a;</p>
<ol><li>一个条件&#xff0c;用来控制循环退出&#xff1b;</li><li>一个循环体&#xff0c;用来定义循环要执行的操作&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">而一般情况下&#xff0c;我们都是通过一个循环变量来控制条件的&#xff0c;这个变量需要随着循环迭代次数的增加而变化。while和do while的循环变量&#xff0c;都是在循环体外单独定义的。</p>
<p style="margin-left:.0001pt;text-align:justify;">for是用法更加明确的循环语句。它可以把循环变量的定义、循环条件以及循环变量的改变都放在一起&#xff0c;统一声明出来。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;经典for循环</p>
<p style="margin-left:.0001pt;text-align:justify;">for循环的经典语法形式是&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">for (<em>初始化语句</em>; <em>条件</em>;<em> 表达式</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句</em></p>
<p style="margin-left:.0001pt;text-align:justify;">关键字for和它后面括号里的部分&#xff0c;叫做“for语句头”。</p>
<p style="margin-left:.0001pt;text-align:justify;">for语句头中有三部分&#xff0c;用分号分隔&#xff0c;主要作用是&#xff1a;</p>
<ol><li>初始化语句负责初始化一个变量&#xff0c;这个变量值会随着循环迭代而改变&#xff0c;一般就是“循环变量”&#xff1b;</li><li>中间的条件是控制循环执行的关键&#xff0c;为真则执行下面的循环体语句&#xff0c;为假则退出。条件一般会以循环变量作为判断标准&#xff1b;</li><li>最后的表达式会在本次循环完成之后再执行&#xff0c;一般会对循环变量进行更改&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">这三个部分并不是必要的&#xff0c;根据需要都可以进行省略。如果省略某个部分&#xff0c;需要保留分号表示这是一个空语句。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以用for循环语句&#xff0c;实现之前输出10次“Hello World”的需求&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 1; i &lt;&#61; 10; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello World!&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;现在是第&#34; &lt;&lt; i &lt;&lt; &#34;次循环&#xff01;\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;范围for循环</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43; 11新标准引入了一种更加简单的for循环&#xff0c;这种语句可以直接遍历一个序列的所有元素。这种for循环叫做“范围for循环”。语法形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">for (声明:<em> 序列表达式</em>)</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>语句</em></p>
<p style="margin-left:.0001pt;text-align:justify;">这里for语句头中的内容就很简单了&#xff0c;只需要声明一个变量&#xff0c;后面跟上一个冒号&#xff08;注意不是分号&#xff09;&#xff0c;再跟上一个序列的表达式就可以了。所谓“序列”&#xff0c;其实就是一组相同类型的数据对象排成了一列来统一处理&#xff1b;所以这个声明的意思&#xff0c;其实就是从序列中依次取出所有元素&#xff0c;每次都赋值给这个变量。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以范围for循环的特点就是&#xff0c;不需要循环变量&#xff0c;直接就可以访问序列中的所有元素。</p>
<p style="margin-left:.0001pt;text-align:left;">// 范围for循环</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num : {3, 6, 8, 10})</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;序列中现在的数据是&#xff1a;&#34; &lt;&lt; num &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里用花括号把一组数括起来&#xff0c;就构成了最简单的序列&#xff1a;{3, 6, 8, 10}。后面将要介绍的数组&#xff0c;以及vector、string等类型的对象&#xff0c;也都是序列。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.3.4 循环嵌套</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">循环语句和分支语句一样&#xff0c;也是可以进行嵌套的。具体可以while循环中嵌套while&#xff0c;可以for循环中嵌套for&#xff0c;也可以while、do while和for混合嵌套。因为for的循环变量定义更明确&#xff0c;所以一般用for的循环嵌套会多一些。</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; 3; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; 5; j&#43;&#43;) </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;Hello World! i &#61; &#34; &lt;&lt; i &lt;&lt; &#34;, j &#61; &#34; &lt;&lt; j &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">循环嵌套之后&#xff0c;内层语句执行的次数&#xff0c;将是外层循环次数和内层循环次数的乘积。这会带来大量的时间消耗&#xff0c;使程序运行变慢&#xff0c;所以使用嵌套循环要非常谨慎。</p>
<p style="margin-left:.0001pt;text-align:justify;">下面是一个使用双重for循环&#xff0c;打印输出“九九乘法表”的例子。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main() </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// i表示行数&#xff0c;j表示列数</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 1; i &lt; 10; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 1; j &lt;&#61; i; j&#43;&#43;) {</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; j &lt;&lt; &#34; × &#34; &lt;&lt; i &lt;&lt; &#34; &#61; &#34; &lt;&lt; i * j &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里使用内外两层for循环&#xff0c;实现了一个二维“表”的输出。后面我们会看到&#xff0c;循环嵌套对于处理多维数据非常有用。</p>
<h4 style="text-align:justify;"><strong><strong><strong>5.4 </strong></strong><strong><strong>跳转</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">在流程控制语句中还有一类“跳转语句”&#xff0c;主要用来中断当前的执行过程。C&#43;&#43;中有四种跳转语句&#xff1a;break&#xff0c;continue&#xff0c;goto以及return。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.4.1 break</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">break语句表示要“跳出”当前的流程控制语句&#xff0c;它只能出现在switch或者循环语句&#xff08;while、do while、for&#xff09;中。当代码中遇到break时&#xff0c;会直接中断距离最近的switch或者循环&#xff0c;跳转到外部继续执行。</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">while (true)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; Hello World! &#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; 这是第&#34; &lt;&lt; &#43;&#43;i &lt;&lt; &#34;次输出\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">if (i &gt;&#61; 5)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">如果循环条件永远为真&#xff0c;那么循环体中一定要有break&#xff0c;保证在某种情况下程序可以退出循环。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.4.2 continue</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">continue语句表示“继续”执行循环&#xff0c;也就是中断循环中的本次迭代、并开始执行下一次迭代。很明显&#xff0c;continue只能用在循环语句中&#xff0c;同样针对最近的一层循环有效。</p>
<p style="margin-left:.0001pt;text-align:justify;">continue非常适合处理需要“跳过”某些情况的场合。</p>
<p style="margin-left:.0001pt;text-align:left;">// 逢7过</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num &#61; 1; num &lt; 100; num&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">// 如果是7的倍数&#xff0c;或者数字中有7&#xff0c;则跳过</p>
<p style="margin-left:.0001pt;text-align:left;">if (num % 7 &#61;&#61; 0 || num % 10 &#61;&#61; 7 || num / 10 &#61;&#61; 7)</p>
<p style="margin-left:.0001pt;text-align:left;">continue;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 如果是10的倍数&#xff0c;则换行</p>
<p style="margin-left:.0001pt;text-align:left;">if (num % 10 &#61;&#61; 0)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">上面模拟了一个经典的小游戏“逢7过”&#xff0c;如果遇到7的倍数比如7、14、21&#xff0c;或者数字中有7比如17、27、71&#xff0c;都要跳过。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.4.3 goto</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">goto语句表示无条件地跳转到程序中的另一条语句。goto的语法形式为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">goto <em>标签</em>;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里的“标签”可以认为是一条语句的“名字”&#xff0c;跟变量类似&#xff0c;只不过它是指代一条语句的标识符。定义标签也非常简单&#xff0c;只要在一条语句前写出标识符&#xff0c;然后跟上冒号就可以了&#xff0c;比如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">begin:  int a &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">下面是一个具体的例子&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">int x &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;程序开始...&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">begin:</p>
<p style="margin-left:.0001pt;text-align:left;">do</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; x &#61; &#34; &lt;&lt; &#43;&#43;x &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">} while (x &lt; 10);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">if (x &lt; 15) {</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;回到原点&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">goto begin;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;程序结束&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">由于goto可以任意跳转&#xff0c;所以它非常灵活&#xff0c;也非常危险。一般在代码中不要使用goto。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.4.4 return</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">return是用来终止函数运行并返回结果的。之前的Hello World程序中就曾经介绍&#xff0c;主函数最后的那句 return 0; 就是结束主函数并返回结果&#xff0c;一般这句可以省略。</p>
<p style="margin-left:.0001pt;text-align:justify;">而在自定义的函数中&#xff0c;同样可以用return来返回。</p>
<h4 style="text-align:justify;"><strong><strong><strong>5.5 应用案例</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">综合利用分支和循环语句&#xff0c;就可以实现很多有趣的功能。</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.5.1 判断质数</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">质数也叫素数&#xff0c;是指一个大于1的自然数&#xff0c;因数只有1和它自身。质数是数论中一个经典的概念&#xff0c;很多著名定理和猜想都跟它有关&#xff1b;质数也是现代密码学的基础。</p>
<p style="margin-left:.0001pt;text-align:justify;">判断一个数是否为质数没有什么规律可言&#xff0c;我们可以通过验证小于它的每个数能否整除&#xff0c;来做暴力求解。下面是一段判断质数、并输出0~100内所有质数的程序&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">//  定义一个判断质数的函数&#xff0c;用return返回判断结果</p>
<p style="margin-left:.0001pt;text-align:left;">bool isPrime(int num)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 2;</p>
<p style="margin-left:.0001pt;text-align:left;">while (i &lt; num)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (num % i &#61;&#61; 0)     return false;</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;i;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">return true;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;请输入一个自然数&#xff08;不超过20亿&#xff09;&#xff1a;&#34; &lt;&lt; endl; </p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int num;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; num;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">if (isPrime(num))</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;是质数&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;不是质数&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;\n&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;0 ~ 100 内的质数有&#xff1a;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 2; i &lt;&#61; 100; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (isPrime(i))</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; i &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.5.2 猜数字</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">猜数字是一个经典的小游戏&#xff0c;程序随机生成一个0~100的数字&#xff0c;然后由用户输入来猜测。如果猜对&#xff0c;输出结果并退出&#xff1b;如果不对&#xff0c;则提示偏大还是偏小。我们可以对猜的次数做限制&#xff0c;比如一共5次机会。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;猜数字&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;规则&#xff1a;输入0~100的整数&#xff0c;有5次机会\n&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 以当前时间为随机数种子&#xff0c;生成一个0~100的伪随机数</p>
<p style="margin-left:.0001pt;text-align:left;">srand(time(0));     </p>
<p style="margin-left:.0001pt;text-align:left;">int target &#61; rand() % 100;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 0;    // 猜的次数</p>
<p style="margin-left:.0001pt;text-align:left;">while (n &lt; 5)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;请输入0~100的整数&#xff1a;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">int num;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; num;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">if (num &#61;&#61; target)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;恭喜你&#xff0c;猜对了&#xff01; 幸运数字是&#xff1a;&#34; &lt;&lt; target &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else if (num &gt; target)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;数字太大了&#xff01;再猜一遍&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;数字太小了&#xff01;再猜一遍&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;n;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">if (n &#61;&#61; 5)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;已经猜过5遍&#xff0c;没有猜中&#xff01;欢迎下次再来&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>5.5.3 爱心曲线</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">利用流程控制语句也可以绘制二维图形。只要知道函数表达式&#xff0c;就可以画出相应的曲线了。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以尝试绘制传说中的“爱心曲线”。一个典型的爱心曲线函数如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/3e416da8880c4462bef8569c6ccbe75c.png"  />
</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/5ff76d394c874a0e9ebcf5a5bedf70d5.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">曲线是一个封闭图形&#xff0c;与坐标轴的四个交点坐标为</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/2a59168d7845418da523a55305b7ef1a.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">和</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/3ab26cc45e0546bd89438777fce07032.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff0c;我们知道坐标(x, y)满足 </p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/a527779b97e34291b62db1b8da93136a.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;"> 的点都在“爱心”内部&#xff0c;而满足</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/6c7aa86ae73f4571a5953e493ef8e429.png"  />
 的点都在“爱心”外部。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以我们可以取边长为</p>
<p style="margin-left:.0001pt;text-align:justify;">
<img alt=""  src="https://i-blog.csdnimg.cn/direct/8a5e301553a4495689518bc7ebbe82cb.png"  />
的正方形区域作为“画板”&#xff0c;扫描范围内所有点&#xff1b;在曲线内部的点用“*”填充&#xff0c;外部的点则用空格填充。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 爱心曲线方程 (x^2&#43;y^2-a)^3 - x^2 y^3 &#61; 0</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义绘图边界</p>
<p style="margin-left:.0001pt;text-align:left;">double bound &#61; 1.3 * sqrt(a);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// x、y坐标变化步长</p>
<p style="margin-left:.0001pt;text-align:left;">double step &#61; 0.05;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">for ( double y &#61; bound; y &gt;&#61; -bound; y -&#61; step)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (double x &#61; -bound; x &lt;&#61; bound; x &#43;&#61; step)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">double result &#61; pow((pow(x, 2) &#43; pow(y, 2) - a), 3) - pow(x, 2) * pow(y, 3);</p>
<p style="margin-left:.0001pt;text-align:left;">if (result &lt;&#61; 0)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;*&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; &#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h3 style="text-align:justify;"><strong><strong><strong>六、复合数据类型</strong></strong></strong></h3>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中不仅有基本数据类型&#xff0c;还提供了更加灵活和丰富的复合数据类型。</p>
<h4 style="text-align:justify;"><strong><strong><strong>6.1 </strong></strong><strong><strong>数组</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">在程序中为了处理方便&#xff0c;常常需要把具有相同类型的数据对象按有序的形式排列起来&#xff0c;形成“一组”数据&#xff0c;这就是“数组”&#xff08;array&#xff09;。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/aae63fcac18a43fdab391634e2b43f30.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">数组中的数据&#xff0c;在内存中是连续存放的&#xff0c;每个元素占据相同大小的空间&#xff0c;就像排好队一样。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.1.1 数组的定义</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">数组的定义形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>数据类型</em> <em>数组名</em>[<em>元素个数</em>];</p>
<ol><li>首先需要声明类型&#xff0c;数组中所有元素必须具有相同的数据类型&#xff1b;</li><li>数组名是一个标识符&#xff1b;后面跟着中括号&#xff0c;里面定义了数组中元素的个数&#xff0c;也就是数组的“长度”&#xff1b;</li><li>元素个数也是类型的一部分&#xff0c;所以必须是确定的&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:left;">int a1[10];          // 定义一个数组a1&#xff0c;元素类型为int&#xff0c;个数为10</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">const int n &#61; 4;</p>
<p style="margin-left:.0001pt;text-align:left;">double a2[n];        // 元素个数可以是常量表达式</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 5;</p>
<p style="margin-left:.0001pt;text-align:justify;">//int a3;      // 错误&#xff0c;元素个数不能为变量</p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意&#xff0c;并没有通用的“数组”类型&#xff0c;所以上面的a1、a2的类型分别是“int数组”和“double数组”。这也是为什么我们把数组叫做“复合数据类型”。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.1.2 数组的初始化</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">之前在讲到for循环时&#xff0c;提到过使用范围for循环可以遍历一个“序列”&#xff0c;用花括号括起来的一组数就是一个序列。所以在给数组赋值时&#xff0c;也可以使用这样的序列。</p>
<p style="margin-left:.0001pt;text-align:left;">int a3[4] &#61; {1,2,3,4};</p>
<p style="margin-left:.0001pt;text-align:left;">float a4[] &#61; {2.5, 3.8, 10.1};    // 正确&#xff0c;初始值说明了元素个数是3</p>
<p style="margin-left:.0001pt;text-align:left;">short a5[10] &#61; {3,6,9};    // 正确&#xff0c;指定了前三个元素&#xff0c;其余都为0</p>
<p style="margin-left:.0001pt;text-align:justify;">//long a6[2] &#61; {3,6,9};    // 错误&#xff0c;初始值太多</p>
<p style="margin-left:.0001pt;text-align:justify;">//int a6[4] &#61; a3;          // 错误&#xff0c;不能用另一个数组对数组赋值</p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意的是&#xff1a;</p>
<ol><li>对数组做初始化&#xff0c;要使用花括号{}括起来的数值序列&#xff1b;</li><li>如果做了初始化&#xff0c;数组定义时的元素个数可以省略&#xff0c;编译器可以根据初始化列表自动推断出来&#xff1b;</li><li>初始值的个数&#xff0c;不能超过指定的元素个数&#xff1b;</li><li>初始值的个数&#xff0c;如果小于元素个数&#xff0c;那么会用列表中的值初始化靠前的元素&#xff1b;剩余元素用默认值填充&#xff0c;整型的默认值就是0&#xff1b;</li><li>如果没有做初始化&#xff0c;数组中元素的值都是未定义的&#xff1b;这一点和普通的局部变量一致&#xff1b;</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>6.1.3 数组的访问</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;访问数组元素</p>
<p style="margin-left:.0001pt;text-align:justify;">数组元素在内存中是连续存放的&#xff0c;它们排好了队之后就会有一个队伍中的编号&#xff0c;称为“索引”&#xff0c;也叫“下标”&#xff1b;通过下标就可以快速访问每个元素了&#xff0c;具体形式为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>数组名</em>[<em>元素下标</em>]</p>
<p style="margin-left:.0001pt;text-align:justify;">这里也是用了中括号来表示元素下标位置&#xff0c;被称为“下标运算符”。比如a[2]就表示数组a中下标为2的元素&#xff0c;可以取它的值输出&#xff0c;也可以对它赋值。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">int a[] &#61; {1,2,3,4,5,6,7,8};</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a[2] &#61; &#34; &lt;&lt; a[2] &lt;&lt; endl;    // a[2] &#61; 3</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">a[2] &#61; 36;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;a[2] &#61; &#34; &lt;&lt; a[2] &lt;&lt; endl;    // a[2] &#61; 36</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意的是&#xff1a;</p>
<ol><li>数组的下标从0开始&#xff1b;</li><li>因此a[2]访问的并不是数组a的第2个元素&#xff0c;而是第三个元素&#xff1b;一个长度为10的数组&#xff0c;下标范围是0~9&#xff0c;而不是1~10&#xff1b;</li><li>合理的下标&#xff0c;不能小于0&#xff0c;也不能大于 (数组长度 - 1)&#xff1b;否则就会出现数组下标越界&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;数组的大小</p>
<p style="margin-left:.0001pt;text-align:justify;">所有的变量&#xff0c;都会在内存中占据一定大小的空间&#xff1b;而数据类型就决定了它具体的大小。而对于数组这样的“复合类型”&#xff0c;由于每个元素类型相同&#xff0c;因此占据空间大小的计算遵循下面的简单公式&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">数组所占空间 &#61; 数据类型所占空间大小 * 元素个数</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这样一来&#xff0c;即使定义的时候没有指定数组元素个数&#xff0c;现在也可以计算得出了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">     // a是已定义的数组</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a所占空间大小&#xff1a;&#34; &lt;&lt; sizeof(a) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;每个元素所占空间大小&#xff1a;&#34; &lt;&lt; sizeof(a[0]) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">// 获取数组长度</p>
<p style="margin-left:.0001pt;text-align:left;">int aSize &#61; sizeof(a) / sizeof(a[0]);</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;数组a的元素个数&#xff1a;&#34; &lt;&lt; aSize &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里为了获取数组的长度&#xff0c;我们使用了sizeof运算符&#xff0c;它可以返回一个数据对象在内存中占用的大小&#xff08;以字节为单位&#xff09;&#xff1b;数组总大小&#xff0c;除以每个数据元素的大小&#xff0c;就是元素个数。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;遍历数组</p>
<p style="margin-left:.0001pt;text-align:justify;">如果想要依次访问数组中所有的元素&#xff0c;就叫做“遍历数组”。我们当然可以用下标去挨个读取&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a[0] &#61; &#34; &lt;&lt; a[0] &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a[1] &#61; &#34; &lt;&lt; a[1] &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">…</p>
<p style="margin-left:.0001pt;text-align:justify;">但这样显然太麻烦了。更好的方式是使用for循环&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 获取数组长度</p>
<p style="margin-left:.0001pt;text-align:left;">int aSize &#61; sizeof(a) / sizeof(a[0]);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; aSize; i&#43;&#43; )</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a[&#34; &lt;&lt; i &lt;&lt; &#34;] &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">循环条件如果写一个具体的数&#xff0c;很容易出现下标越界的情况&#xff1b;而如果知道了数组长度&#xff0c;直接让循环变量i小于它就可以了。</p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;这种写法还是稍显麻烦。C&#43;&#43; 11标准给我们提供了更简单的写法&#xff0c;就是之前介绍过的范围for循环&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num: a )</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;这种情况下就无法获取元素对应的下标了。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>1</strong></strong><strong><strong>.</strong></strong><strong><strong>4</strong></strong><strong> </strong><strong><strong>多维数组</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">之前介绍的数组只是数据最简单的排列方式。如果数据对象排列成的不是“一队”&#xff0c;而是一个“方阵”&#xff0c;那显然就不能只用一个下标来表示了。我们可以对数组进行扩展&#xff0c;让它从“一维”变成“二维”甚至“多维”。</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[3][4];         // 二维数组,有三个元素&#xff0c;每个元素是一个长度为4的int数组</p>
<p style="margin-left:.0001pt;text-align:justify;">int arr2[2][5][10];    // 三维数组</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中本质上没有“多维数组”这种东西&#xff0c;所谓的“多维数组”&#xff0c;其实就是“数组的数组”。</p>
<ol><li>二维数组int arr[3][4]表示&#xff1a;arr是一个有三个元素的数组&#xff0c;其中的每个元素都是一个int数组&#xff0c;包含4个元素&#xff1b;</li><li>三维数组int arr2[2][5][10]表示&#xff1a;arr2是一个长度为2的数组&#xff0c;其中每个元素都是一个二维数组&#xff1b;这个二维数组有5个元素&#xff0c;每个元素都是一个长度为10的int数组&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">一般最常见的就是二维数组。它有两个“维度”&#xff0c;第一个维度表示数组本身的长度&#xff0c;第二个表示每个元素的长度&#xff1b;一般分别把它们叫做“行”和“列”。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;多维数组的初始化</p>
<p style="margin-left:.0001pt;text-align:justify;">和普通的“一维”数组一样&#xff0c;多维数组初始化时&#xff0c;也可以用花括号括起来的一组数。使用嵌套的花括号可以让不同的维度更清晰&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>数据类型 数组名</em>[<em>行数</em>][列数] &#61; {数据1, 数据2, 数据3, …};</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;"><em>数据类型 数组名</em>[<em>行数</em>][列数] &#61; {</p>
<p style="margin-left:.0001pt;text-align:justify;">{数据11, 数据12, 数据13, …},</p>
<p style="margin-left:.0001pt;text-align:justify;">{数据21, 数据22, 数据23, …},</p>
<p style="margin-left:.0001pt;text-align:justify;">…</p>
<p style="margin-left:.0001pt;text-align:justify;">};</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意&#xff1a;</p>
<ol><li>内嵌的花括号不是必需的&#xff0c;因为数组中的元素在内存中连续存放&#xff0c;可以用一个花括号将所有数据括在一起&#xff1b;</li><li>初始值的个数&#xff0c;可以小于数组定义的长度&#xff0c;其它元素初始化为0值&#xff1b;这一点对整个二维数组和每一行的一维数组都适用&#xff1b;</li><li>如果省略嵌套的花括号&#xff0c;当初始值个数小于总元素个数时&#xff0c;会按照顺序依次填充&#xff08;填满第一行&#xff0c;才填第二行&#xff09;&#xff1b;其它元素初始化为0值&#xff1b;</li><li>多维数组的维度&#xff0c;可以省略第一个&#xff0c;由编译器自动推断&#xff1b;即二维数组可以省略行数&#xff0c;但不能省略列数。</li></ol>
<p style="margin-left:.0001pt;text-align:left;">// 嵌套的花括号的初始化</p>
<p style="margin-left:.0001pt;text-align:left;">int ia[3][4] &#61; {</p>
<p style="margin-left:.0001pt;text-align:left;">{1,2,3,4},</p>
<p style="margin-left:.0001pt;text-align:left;">{5,6,7,8},</p>
<p style="margin-left:.0001pt;text-align:left;">{9,10,11,12}</p>
<p style="margin-left:.0001pt;text-align:left;">};</p>
<p style="margin-left:.0001pt;text-align:left;">//  只有一层花括号的初始化</p>
<p style="margin-left:.0001pt;text-align:left;">int ia2[3][4] &#61; { 1,2,3,4,5,6,7,8,9,10,11,12 };</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 部分初始化&#xff0c;其余补0</p>
<p style="margin-left:.0001pt;text-align:left;">int ia3[3][4] &#61; {</p>
<p style="margin-left:.0001pt;text-align:left;">{1,2,3},</p>
<p style="margin-left:.0001pt;text-align:left;">{5,6}</p>
<p style="margin-left:.0001pt;text-align:left;">};</p>
<p style="margin-left:.0001pt;text-align:left;">int ia4[3][4] &#61; {1,2,3,4,5,6};</p>
<p style="margin-left:.0001pt;text-align:left;">// 省略行数&#xff0c;自动推断</p>
<p style="margin-left:.0001pt;text-align:justify;">int ia5[][4] &#61; {1,2,3,4,5};</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;访问数据</p>
<p style="margin-left:.0001pt;text-align:justify;">也可以用下标运算符来访问多维数组中的数据&#xff0c;数组的每一个维度&#xff0c;都应该有一个对应的下标。对于二维数组来说&#xff0c;就是需要指明“行号”“列号”&#xff0c;这相当于数据元素在二维矩阵中的坐标。</p>
<p style="margin-left:.0001pt;text-align:left;">// 访问ia的第二行、第三个数据</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ia[1][2] &#61; &#34; &lt;&lt; ia[1][2] &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">// 修改ia的第一行、第二个数据</p>
<p style="margin-left:.0001pt;text-align:justify;">ia[0][1] &#61; 19;</p>
<p style="margin-left:.0001pt;text-align:justify;">同样需要注意&#xff0c;行号和列号都是从0开始、到 (元素个数 - 1) 结束。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;遍历数组</p>
<p style="margin-left:.0001pt;text-align:justify;">要想遍历数组&#xff0c;当然需要使用for循环&#xff0c;而且要扫描每一个维度。对于二维数组&#xff0c;我们需要对行和列分别进行扫描&#xff0c;这是一个双重for循环&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;二维数组总大小&#xff1a;&#34; &lt;&lt; sizeof(ia) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;二维数组每行大小&#xff1a;&#34; &lt;&lt; sizeof(ia[0]) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;二维数组每个元素大小&#xff1a;&#34; &lt;&lt; sizeof(ia[0][0]) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 二维数组行数</p>
<p style="margin-left:.0001pt;text-align:left;">int rowCnt &#61; sizeof(ia) / sizeof(ia[0]);</p>
<p style="margin-left:.0001pt;text-align:left;">// 二维数组列数</p>
<p style="margin-left:.0001pt;text-align:left;">int colCnt &#61; sizeof(ia[0]) / sizeof(ia[0][0]);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; rowCnt; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; colCnt; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; ia[j] &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">同样&#xff0c;这里利用了sizeof运算符&#xff1a;</p>
<ol><li>行数 &#61; 二维数组总大小 / 每行大小</li><li>列数 &#61; 每行大小 / 每个元素大小</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;也可以使用范围for循环&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">for (auto &amp; row : ia)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (auto num : row)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里的外层循环使用了auto关键字&#xff0c;这也是C&#43;&#43; 11新引入的特性&#xff0c;它可以自动推断变量的类型&#xff1b;后面的&amp;是定义了一个“引用”。关于这部分内容&#xff0c;会在后面继续介绍。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.1.5 数组的简单排序算法</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">数组排序指的是给定一个数组&#xff0c;要求把其中的元素按照从小到大&#xff08;或从大到小&#xff09;顺序排列。</p>
<p style="margin-left:.0001pt;text-align:justify;">这是一个非常经典的需求&#xff0c;有各种不同的算法可以实现。我们这里介绍两种最基本、最简单的排序算法。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;选择排序</p>
<p style="margin-left:.0001pt;text-align:justify;">选择排序是一种简单直观的排序算法。</p>
<p style="margin-left:.0001pt;text-align:justify;">它的工作原理&#xff1a;首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;大&#xff09;元素&#xff0c;然后追加到已排序序列的末尾。以此类推&#xff0c;直到所有元素均排序完毕。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/1a98c410dc464fc78db5710f78df476d.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">选择排序可以使用双重for循环很容易地实现&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[] &#61; {5, 9, 2, 7, 4, 3, 12, 6, 1, 5, 7};</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int size &#61; sizeof(arr) / sizeof(arr[0]);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">//  选择排序</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; size; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; i &#43; 1; j &lt; size; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (arr[j] &lt; arr)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">//  如果arr[j]更小&#xff0c;就和arr交换位置</p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; arr;</p>
<p style="margin-left:.0001pt;text-align:left;">arr &#61; arr[j];</p>
<p style="margin-left:.0001pt;text-align:left;">arr[j] &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">//  输出</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num : arr) </p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;冒泡排序</p>
<p style="margin-left:.0001pt;text-align:justify;">冒泡排序也是一种简单的排序算法。</p>
<p style="margin-left:.0001pt;text-align:justify;">它的基本原理是&#xff1a;重复地扫描要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的大小顺序错误&#xff0c;就把它们交换过来。这样&#xff0c;一次扫描结束&#xff0c;我们可以确保最大&#xff08;小&#xff09;的值被移动到序列末尾。这个算法的名字由来&#xff0c;就是因为越小的元素会经由交换&#xff0c;慢慢“浮”到数列的顶端。</p>
<p>
<img alt=""  src="https://i-blog.csdnimg.cn/direct/fb51f95685ba445c861e1ea013a7f7ec.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">冒泡排序的代码实现也非常简单&#xff0c;同样是使用双重for循环&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 冒泡排序</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; size; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; size - i - 1; j&#43;&#43;) </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (arr[j] &gt; arr[j&#43;1])</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; arr[j&#43;1];</p>
<p style="margin-left:.0001pt;text-align:left;">arr[j&#43;1] &#61; arr[j];</p>
<p style="margin-left:.0001pt;text-align:left;">arr[j] &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h4 style="text-align:justify;"><strong><strong><strong>6.2 模板类vector</strong></strong><strong><strong>简介</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">数组尽管很灵活&#xff0c;但使用起来还是很多不方便。为此&#xff0c;C&#43;&#43;语言定义了扩展的“抽象数据类型”&#xff08;Abstract Data Type&#xff0c; ADT&#xff09;&#xff0c;放在“标准库”中。</p>
<p style="margin-left:.0001pt;text-align:justify;">对数组功能进行扩展的一个标准库类型&#xff0c;就是“容器”vector。顾名思义&#xff0c;vector“容纳”着一堆数据对象&#xff0c;其实就是一组类型相同的数据对象的集合。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.2.1 头文件和命名空间</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">vector是标准库的一部分。要想使用vector&#xff0c;必须在程序中包含&lt;vector&gt;头文件&#xff0c;并使用std命名空间。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;vector&gt;</p>
<p style="margin-left:.0001pt;text-align:justify;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:justify;">在vector头文件中&#xff0c;对vector这种类型做了定义&#xff1b;使用#include引入它之后&#xff0c;并指定命名空间std之后&#xff0c;我们就可以在代码中直接使用vector了。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.2.2 vector的基本用法</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">vector其实是C&#43;&#43;中的一个“类模板”&#xff0c;是用来创建类的“模子”。所以在使用时还必须提供具体的类型信息&#xff0c;也就是说&#xff0c;这个容器中到底要容纳什么类型的数据对象&#xff1b;具体的形式是在vector后面跟一个尖括号&lt;&gt;&#xff0c;里面填入具体类型信息。</p>
<p style="margin-left:.0001pt;text-align:justify;">vector&lt;int&gt; v;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;初始化</p>
<p style="margin-left:.0001pt;text-align:justify;">跟数组相比&#xff0c;vector的初始化更加灵活方便&#xff0c;可以应对各种不同的需求。</p>
<p style="margin-left:.0001pt;text-align:left;">// 默认初始化&#xff0c;不含任何元素</p>
<p style="margin-left:.0001pt;text-align:left;">vector&lt;int&gt; v1;</p>
<p style="margin-left:.0001pt;text-align:left;">// 列表初始化</p>
<p style="margin-left:.0001pt;text-align:left;">vector&lt;char&gt; v2 &#61; {&#39;a&#39;, &#39;b&#39;, &#39;c&#39;};</p>
<p style="margin-left:.0001pt;text-align:left;">// 省略等号的列表初始化</p>
<p style="margin-left:.0001pt;text-align:left;">vector&lt;short&gt; v3{1,2,3,4,5};</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 只定义长度&#xff0c;元素初值默认初始化&#xff0c;容器中有5个0</p>
<p style="margin-left:.0001pt;text-align:left;">vector&lt;int&gt; v4(5);</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义长度和初始值&#xff0c;容器中有5个100</p>
<p style="margin-left:.0001pt;text-align:justify;">vector&lt;long&gt; v5(5, 100);</p>
<p style="margin-left:.0001pt;text-align:justify;">这里有几种不同的初始化方式&#xff1a;</p>
<ol><li>默认初始化一个vector对象&#xff0c;就是一个空容器&#xff0c;里面不含任何元素&#xff1b;</li><li>C&#43;&#43; 11之后可以用花括号括起来的列表&#xff0c;对vector做初始化&#xff1b;等号可以省略&#xff1b;这种方式是把一个列表拷贝给了vector&#xff0c;称为“拷贝初始化”</li><li>可以用小括号表示初始化vector的长度&#xff0c;并且可以给所有元素指定相同的初始值&#xff1b;这种方式叫做“直接初始化”</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;访问元素</p>
<p style="margin-left:.0001pt;text-align:justify;">vector是包含了数据对象的“容器”&#xff0c;在这个容器集合中&#xff0c;每个数据对象都会有一个编号&#xff0c;用来做方便快速的访问&#xff1b;这个编号就是“索引”&#xff08;index&#xff09;。同样可以用下标操作符来获取对应索引的元素&#xff0c;这一点跟数组非常相似。</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;v5[2] &#61; &#34; &lt;&lt; v5[2] &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">v5[4] &#61; 32;</p>
<p style="margin-left:.0001pt;text-align:justify;">//v5[5] &#61; 16;    // 严重错误&#xff01;不能越界访问索引</p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意&#xff1a;</p>
<ol><li>vector内元素的索引&#xff0c;也是从0开始&#xff1b;</li><li>vector索引最大值为 (vector长度 - 1)&#xff0c;不能越界访问&#xff1b;如果直接越界访问并赋值&#xff0c;有可能导致非常严重的后果&#xff0c;出现安全问题</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;遍历所有元素</p>
<p style="margin-left:.0001pt;text-align:justify;">vector中有一个可以调用的函数size()&#xff0c;只要调用它就能直接得到vector的长度&#xff08;即元素个数&#xff09;&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 获取vector的长度</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; v5.size() &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">调用的方式是一个vector对象后面跟上一个点&#xff0c;再跟上size()。这种基于对象来调用的函数叫做“成员函数”。</p>
<p style="margin-left:.0001pt;text-align:justify;">这样我们就可以非常方便地用for循环遍历元素了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; v5.size(); i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; v5 &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;用范围for循环同样非常简单&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num: v5)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;添加元素</p>
<p style="margin-left:.0001pt;text-align:justify;">vector的长度并不是固定的&#xff0c;所以可以向一个定义好的vector添加元素。</p>
<p style="margin-left:.0001pt;text-align:left;">// 在定义好的vector中添加元素</p>
<p style="margin-left:.0001pt;text-align:left;">v5.push_back(69);</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num : v5)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里的push_back同样是一个成员函数&#xff0c;调用它的时候在小括号里传入想要添加的数值&#xff0c;就可以让vector对象中增加一个元素了。</p>
<p style="margin-left:.0001pt;text-align:justify;">这就使得我们在创建vector对象时不需要知道元素个数&#xff0c;使用更加灵活&#xff0c;避免了数组中的缺陷。</p>
<p style="margin-left:.0001pt;text-align:justify;">下面的代码创建了一个空vector&#xff0c;并使用添加元素的方式给它赋值为倒序的10~1&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">vector&lt;int&gt; vec;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 10; i &gt; 0; i--)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">vec.push_back(i);</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.2.3 vector和数组的区别</strong></strong></strong></h5>
<ol><li>数组是更加底层的数据类型&#xff1b;长度固定&#xff0c;功能较少&#xff0c;安全性没有保证&#xff1b;但性能更好&#xff0c;运行更高效&#xff1b;</li><li>vector是模板类&#xff0c;是数组的上层抽象&#xff1b;长度不定&#xff0c;功能强大&#xff1b;缺点是运行效率较低&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">除了vector之外&#xff0c;C&#43;&#43; 11 还新增了一个array模板类&#xff0c;它跟数组更加类似&#xff0c;长度是固定的&#xff0c;但更加方便、更加安全。所以在实际应用中&#xff0c;一般推荐对于固定长度的数组使用array&#xff0c;不固定长度的数组使用vector。</p>
<h4 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>3</strong></strong><strong> </strong><strong><strong>字符串</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">字符串我们并不陌生。之前已经介绍过&#xff0c;一串字符连在一起就是一个“字符串”&#xff0c;比如用双引号引起来的“Hello World&#xff01;”就是一个字符串字面值。</p>
<p style="margin-left:.0001pt;text-align:justify;">字符串其实就是所谓的“纯文本”&#xff0c;就是各种文字、数字、符号在一起表达的一串信息&#xff1b;所以字符串就是C&#43;&#43;中用来表达和处理文本信息的数据类型。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>3</strong></strong><strong><strong>.1 </strong></strong><strong><strong>标准库类型string</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;的标准库中&#xff0c;提供了一种用来表示字符串的数据类型string&#xff0c;这种类型能够表示长度可变的字符序列。和vector类似&#xff0c;string类型也定义在命名空间std中&#xff0c;使用它必须包含string头文件。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;string&gt;</p>
<p style="margin-left:.0001pt;text-align:justify;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;定义和初始化string</p>
<p style="margin-left:.0001pt;text-align:justify;">我们已经接触过C&#43;&#43;中几种不同的初始化方式&#xff0c;string也是一个标准库类型&#xff0c;它的初始化与vector非常相似。</p>
<p style="margin-left:.0001pt;text-align:left;">// 默认初始化&#xff0c;空字符串</p>
<p style="margin-left:.0001pt;text-align:left;">string s1;</p>
<p style="margin-left:.0001pt;text-align:left;">// 用另一个字符串变量&#xff0c;做拷贝初始化</p>
<p style="margin-left:.0001pt;text-align:left;">string s2 &#61; s1;</p>
<p style="margin-left:.0001pt;text-align:left;">// 用一个字符串字面值&#xff0c;做拷贝初始化</p>
<p style="margin-left:.0001pt;text-align:left;">string s3 &#61; &#34;Hello World!&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">// 用一个字符串字面值&#xff0c;做直接初始化</p>
<p style="margin-left:.0001pt;text-align:left;">string s4(&#34;hello world&#34;);</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义字符和重复的次数&#xff0c;做直接初始化&#xff0c;得到 hhhhhhhh</p>
<p style="margin-left:.0001pt;text-align:justify;">string s5(8, &#39;h&#39;);</p>
<p style="margin-left:.0001pt;text-align:justify;">初始化方式主要有&#xff1a;</p>
<ol><li>默认初始化&#xff0c;得到的就是一个空字符串&#xff1b;</li><li>拷贝初始化&#xff0c;用赋值运算符&#xff08;等号“&#61;”&#xff09;表示&#xff1b;可以使用另一个string对象&#xff0c;也可以使用字符串字面值常量&#xff1b;</li><li>直接初始化&#xff0c;用括号表示&#xff1b;可以在括号中传入一个字符串&#xff0c;也可以传入字符和重复的次数</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">可以发现&#xff0c;字符串也可以看做数据元素的集合&#xff1b;它里面的元素&#xff0c;就是字符。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;处理字符串中的字符</p>
<p style="margin-left:.0001pt;text-align:justify;">通过初始化已经可以看出&#xff0c;string的行为与vector非常类似。string同样也可以通过下标运算符访问内部的每个字符。字符的“索引”&#xff0c;就是在字符串中的位置。</p>
<p style="margin-left:.0001pt;text-align:left;">string str &#61; &#34;hello world&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">// 获取第3个字符</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;str[2] &#61; &#34; &lt;&lt; str[2] &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">// 将第1个字符改为&#39;H&#39;</p>
<p style="margin-left:.0001pt;text-align:left;">str[0] &#61; &#39;H&#39;;</p>
<p style="margin-left:.0001pt;text-align:left;">// 将最后一个字符改为&#39;D&#39;</p>
<p style="margin-left:.0001pt;text-align:left;">str[str.size() - 1] &#61; &#39;D&#39;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;str &#61; &#34; &lt;&lt; str &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">字符串内字符的访问&#xff0c;跟vector内元素的访问类似&#xff0c;需要注意&#xff1a;</p>
<ol><li>string内字符的索引&#xff0c;也是从0开始&#xff1b;</li><li>string同样有一个成员函数size&#xff0c;可以获取字符串的长度&#xff1b;</li><li>索引最大值为 (字符串长度 - 1)&#xff0c;不能越界访问&#xff1b;如果直接越界访问并赋值&#xff0c;有可能导致非常严重的后果&#xff0c;出现安全问题&#xff1b;</li><li>如果希望遍历字符串的元素&#xff0c;也可以使用普通for循环和范围for循环&#xff0c;依次获取每个字符</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">比如&#xff0c;我们可以考虑遍历所有字符&#xff0c;将小写字母换成大写&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 遍历字符串中字符&#xff0c;将小写字母变成大写</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; str.size(); i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">str &#61; toupper(str);</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里又调用了string的一个函数toupper&#xff0c;可以把传入的字符转换成大写并返回。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;字符串相加</p>
<p style="margin-left:.0001pt;text-align:justify;">string本身的长度是不定的&#xff0c;可以通过“相加”的方式扩展一个字符串。</p>
<p style="margin-left:.0001pt;text-align:left;">// 字符串相加</p>
<p style="margin-left:.0001pt;text-align:left;">string str1 &#61; &#34;hello&#34;, str2(&#34;world&#34;);</p>
<p style="margin-left:.0001pt;text-align:left;">string str3 &#61; str1 &#43; str2;                  // str3 &#61; &#34;helloworld&#34;</p>
<p style="margin-left:.0001pt;text-align:left;">string str4 &#61; str1 &#43; &#34;, &#34; &#43; str2 &#43; &#34;!&#34;;    // str4 &#61; &#34;hello, world!&#34;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:justify;">//string str5 &#61; &#34;hello, &#34; &#43; &#34;world!&#34;;        // 错误&#xff0c;不能将两个字符串字面值相加</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意&#xff1a;</p>
<ol><li>字符串相加使用加号“&#43;”来表示&#xff0c;这是算术运算符“&#43;”的运算符重载&#xff0c;含义是“字符串拼接”&#xff1b;</li><li>两个string对象&#xff0c;可以直接进行字符串相加&#xff1b;结果是将两个字符串拼接在一起&#xff0c;得到一个新的string对象返回&#xff1b;</li><li>一个string对象和一个字符串字面值常量&#xff0c;可以进行字符串相加&#xff0c;同样是得到一个拼接后的string对象返回&#xff1b;</li><li>两个字符串字面值常量&#xff0c;不能相加&#xff1b;</li><li>多个string对象和多个字符串字面值常量&#xff0c;可以连续相加&#xff1b;前提是按照左结合律&#xff0c;每次相加必须保证至少有一个string对象&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;4&#xff09;比较字符串</p>
<p style="margin-left:.0001pt;text-align:justify;">string类还提供几种用来做字符串比较的运算符&#xff0c;“&#61;&#61;”和“!&#61;”用来判断两个字符串是否完全一样&#xff1b;而“&lt;”“&gt;”“&lt;&#61;”“&gt;&#61;”则用来比较两个字符串的大小。这些都是关系型运算符的重载。</p>
<p style="margin-left:.0001pt;text-align:left;">str1 &#61; &#34;hello&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">str2 &#61; &#34;hello world!&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">str3 &#61; &#34;hehehe&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">str1 &#61;&#61; str2;    // false</p>
<p style="margin-left:.0001pt;text-align:left;">str1 &lt; str2;     // true</p>
<p style="margin-left:.0001pt;text-align:justify;">str1 &gt;&#61; str3;     // true</p>
<p style="margin-left:.0001pt;text-align:justify;">字符串比较的规则为&#xff1a;</p>
<ol><li>如果两个字符串长度相同&#xff0c;每个位置包含的字符也都相同&#xff0c;那么两者“相等”&#xff1b;否则“不相等”&#xff1b;</li><li>如果两个字符串长度不同&#xff0c;而较短的字符串每个字符都跟较长字符串对应位置字符相同&#xff0c;那么较短字符串“小于”较长字符串&#xff1b;</li><li>如果两个字符串在某一位置上开始不同&#xff0c;那么就比较这两个字符的ASCII码&#xff0c;比较结果就代表两个字符串的大小关系</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>3</strong></strong><strong><strong>.2 </strong></strong><strong><strong>字符数组&#xff08;C风格字符串&#xff09;</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">通过对string的介绍可以发现&#xff0c;字符串就是一串字符的集合&#xff0c;本质上其实就是一个“字符的数组”。</p>
<p style="margin-left:.0001pt;text-align:justify;">在C语言中&#xff0c;确实是用char[]类型来表示字符串的&#xff1b;不过为了区分纯粹的“字符数组”和“字符串”&#xff0c;C语言规定&#xff1a;字符串必须以空字符结束。空字符的ASCII码为0&#xff0c;专门用来标记字符串的结尾&#xff0c;在程序中写作’\0’。</p>
<p style="margin-left:.0001pt;text-align:left;">// str1没有结尾空字符&#xff0c;并不是一个字符串</p>
<p style="margin-left:.0001pt;text-align:left;">char str1[5] &#61; {&#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;};</p>
<p style="margin-left:.0001pt;text-align:left;">// str2是一个字符串</p>
<p style="margin-left:.0001pt;text-align:left;">char str2[6] &#61; { &#39;h&#39;,&#39;e&#39;,&#39;l&#39;,&#39;l&#39;,&#39;o&#39;,&#39;\0&#39;};</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;str1 &#61; &#34; &lt;&lt; str1 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;str2 &#61; &#34; &lt;&lt; str2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">如果每次用到字符串都要这样定义&#xff0c;对程序员来说就非常不友好了。所以字符串可以用另一种更方便的形式定义出来&#xff0c;那就是使用双引号&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">char str3[] &#61; &#34;hello&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">//char str3[5] &#61; &#34;hello&#34;;    // 错误&#xff0c;&#34;hello&#34;的长度为6</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;str3 &#61; &#34; &lt;&lt; str3 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">这就是我们所熟悉的字符串“字面值常量”。这里需要注意的是&#xff0c;我们不需要再考虑末尾的空字符&#xff0c;编译器会自动帮我们补全&#xff1b;但真实的字符串的长度&#xff0c;依然要包含空字符&#xff0c;所以上面的字符串“hello”长度不是5、而是6。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">所以&#xff0c;C&#43;&#43;中的字符串字面值常量&#xff0c;为了兼容C依然定义为字符数组&#xff08;char[]&#xff09;类型&#xff0c;这和string是两种不同类型&#xff1b;两者的区别&#xff0c;跟数组和vector的区别类似&#xff0c;char[]是更底层的类型。一般情况下&#xff0c;使用string会带来更多方便&#xff0c;也会更加安全。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>6.3.3 读取输入的字符串</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">程序中往往需要一些交互操作&#xff0c;如果想获取从键盘输入的字符串&#xff0c;可以使用多种方法。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09; 使用输入操作符读取单词</p>
<p style="margin-left:.0001pt;text-align:justify;">标准库中提供了iostream&#xff0c;可以使用内置的cin对象&#xff0c;调用重载的输入操作符&gt;&gt;来读取键盘输入。</p>
<p style="margin-left:.0001pt;text-align:left;">string str;</p>
<p style="margin-left:.0001pt;text-align:left;">//  读取键盘输入&#xff0c;遇到空白符停止</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; str;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; str;</p>
<p style="margin-left:.0001pt;text-align:justify;">这种方式的特点是&#xff1a;忽略开始的空白符&#xff0c;遇到下一个空白符&#xff08;空格、回车、制表等&#xff09;就会停止。所以如果我们输入“hello world”&#xff0c;那么读取给str的只有“hello”&#xff1a;这相当于读取了一个“单词”。</p>
<p style="margin-left:.0001pt;text-align:justify;">剩下的内容“world”其实也没有丢&#xff0c;而是保存在了输入流的“输入队列”里。如果我们想读取更多的输入信息&#xff0c;就需要使用更多的string对象来获取&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">string str1, str2;</p>
<p style="margin-left:.0001pt;text-align:left;">cin &gt;&gt; str1 &gt;&gt; str2;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; str1 &lt;&lt; str2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">这样&#xff0c;如果输入“hello world”&#xff0c;就可以输出“helloworld”。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;使用getline读取一行</p>
<p style="margin-left:.0001pt;text-align:justify;">如果希望直接读取一整行输入信息&#xff0c;可以使用getline函数来替代输入操作符。</p>
<p style="margin-left:.0001pt;text-align:left;">string str3;</p>
<p style="margin-left:.0001pt;text-align:left;">getline(cin, str3);</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;str3 &#61; &#34; &lt;&lt; str3 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">getline函数有两个参数&#xff1a;一个是输入流对象cin&#xff0c;另一个是保存字符串的string对象&#xff1b;它会一直读取输入流中的内容&#xff0c;直到遇到换行符为止&#xff0c;然后把所有内容保存到string对象中。所以现在可以完整读取一整行信息了。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;使用get读取字符</p>
<p style="margin-left:.0001pt;text-align:justify;">还有一种方法&#xff0c;是调用cin的get函数读取一个字符。</p>
<p style="margin-left:.0001pt;text-align:left;">char ch;</p>
<p style="margin-left:.0001pt;text-align:left;">ch &#61; cin.get();         // 将捕获到的字符赋值给ch</p>
<p style="margin-left:.0001pt;text-align:justify;">cin.get(ch);            // 直接将ch作为参数传给get</p>
<p style="margin-left:.0001pt;text-align:justify;">有两种方式&#xff1a;</p>
<ol><li>调用cin.get()函数&#xff0c;不传参数&#xff0c;得到一个字符赋给char类型变量&#xff1b;</li><li>将char类型变量作为参数传入&#xff0c;将捕获的字符赋值给它&#xff0c;返回的是istream对象</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">get函数还可以读取一行内容。这种方式跟getline很相似&#xff0c;也可以读取一整行内容&#xff0c;以回车结束。主要区别在于&#xff0c;它需要把信息保存在一个char[]类型的字符数组中&#xff0c;调用的是cin的成员函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// get读取一整行</p>
<p style="margin-left:.0001pt;text-align:left;">char str4[20];</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get(str4, 20);</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;str4 &#61; &#34; &lt;&lt; str4 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// get读取一个字符</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();    // 先读取之前留下的回车符</p>
<p style="margin-left:.0001pt;text-align:justify;">cin.get();    // 再等待下一次输入</p>
<p style="margin-left:.0001pt;text-align:justify;">get函数同样需要传入两个参数&#xff1a;一个是保存信息的字符数组&#xff0c;另一个是字符数组的长度。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里还要注意跟getline的另一个区别&#xff1a;键盘输入总是以回车作为结束的&#xff1b;getline会把最后的回车符丢弃&#xff0c;而get会将回车符保留在输入队列中。</p>
<p style="margin-left:.0001pt;text-align:justify;">这样的效果是&#xff0c;下次再调用get试图读取一行数据时&#xff0c;会因为直接读到了回车符而返回空行。这就需要再次调用get函数&#xff0c;捕获下一个字符&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();    // 先读取之前留下的回车符</p>
<p style="margin-left:.0001pt;text-align:justify;">cin.get();    // 再等待下一次输入</p>
<p style="margin-left:.0001pt;text-align:justify;">这样就可以将之前的回车符捕获&#xff0c;从而为读取下一行做好准备。这也就解释了之前为什么要写两个cin.get()&#xff1a;第一个用来处理之前保留在输入队列的回车符&#xff1b;第二个用来等待下一次输入&#xff0c;让窗口保持开启状态。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.3.4 简单读写文件</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">实际应用中&#xff0c;我们往往会遇到读写文件的需求&#xff0c;这也是一种IO操作&#xff0c;整体用法跟命令行的输入输出非常类似。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;的IO库中提供了专门用于文件输入的ifstream类和用于文件输出的ofstream类&#xff0c;要使用它们需要引入头文件fstream。ifstream用于读取文件内容&#xff0c;跟istream的用法类似&#xff1b;也可以通过输入操作符&gt;&gt;来读“单词”&#xff08;空格分隔&#xff09;&#xff0c;通过getline函数来读取一行&#xff0c;通过get函数来读取一个字符&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">ifstream input(&#34;input.txt&#34;);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 逐词读取</p>
<p style="margin-left:.0001pt;text-align:left;">string word;</p>
<p style="margin-left:.0001pt;text-align:left;">while (input &gt;&gt; word) </p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; word &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 逐行读取</p>
<p style="margin-left:.0001pt;text-align:left;">string line;</p>
<p style="margin-left:.0001pt;text-align:left;">while (getline(input, line))</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; line &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 逐字符读取</p>
<p style="margin-left:.0001pt;text-align:left;">char ch;</p>
<p style="margin-left:.0001pt;text-align:left;">while (input.get(ch))</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; ch &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">类似地&#xff0c;写入文件也可以通过使用输出运算符 &lt;&lt; 来实现&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">ofstream output(&#34;output.txt&#34;);</p>
<p style="margin-left:.0001pt;text-align:left;">output &lt;&lt; word &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h4 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>4</strong></strong><strong> </strong><strong><strong>结构体</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">实际应用中&#xff0c;我们往往希望把很多不同的信息组合起来&#xff0c;“打包”存储在一个单元中。比如一个学生的信息&#xff0c;可能包含了姓名、年龄、班级、成绩…这些信息的数据类型可能是不同的&#xff0c;所以数组和vector都无法完成这样的功能。</p>
<p style="margin-left:.0001pt;text-align:justify;">C/C&#43;&#43;中提供了另一种更加灵活的数据结构——结构体。结构体是用户自定义的复合数据结构&#xff0c;里面可以包含多个不同类型的数据对象。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.4.1 结构体的声明</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">声明一个结构体需要使用struct关键字&#xff0c;具体形式如下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">struct <em>结构体名</em></p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">    <em>类型1 数据对象1</em>;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>类型2 数据对象2</em>;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>类型3 数据对象3</em>;</p>
<p style="margin-left:.0001pt;text-align:justify;">    …</p>
<p style="margin-left:.0001pt;text-align:justify;">};</p>
<p style="margin-left:.0001pt;text-align:justify;">结构体中数据对象的类型和个数都可以自定义&#xff0c;这为数据表达提供了极大的灵活性。结构体可以说是迈向面向对象世界中“类”概念的第一步。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以尝试定义这样一个“学生信息”结构体&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">struct studentInfo </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string name;</p>
<p style="margin-left:.0001pt;text-align:left;">int age;</p>
<p style="margin-left:.0001pt;text-align:left;">double score;</p>
<p style="margin-left:.0001pt;text-align:justify;">};</p>
<p style="margin-left:.0001pt;text-align:justify;">这个结构体中包含了三个数据对象&#xff1a;string类型的名字name&#xff0c;int类型的年龄age&#xff0c;以及double类型的成绩score。一般会把结构体定义在主函数外面&#xff0c;称为“外部定义”&#xff0c;这样可以方便外部访问。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.4.2 结构体初始化</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">定义好结构之后&#xff0c;就产生了一个新的类型&#xff0c;叫做“studentInfo”。接下来就可以创建这种类型的对象&#xff0c;并做初始化了。</p>
<p style="margin-left:.0001pt;text-align:left;">// 创建对象并初始化</p>
<p style="margin-left:.0001pt;text-align:justify;">studentInfo stu &#61; {&#34;张三&#34;, 20, 60.0};</p>
<p style="margin-left:.0001pt;text-align:justify;">结构体对象的初始化非常简单&#xff0c;跟数组完全一样&#xff1a;只要按照对应顺序一次赋值&#xff0c;逗号分隔&#xff0c;最后用花括号括起来就可以了。</p>
<p style="margin-left:.0001pt;text-align:justify;">结构体还支持其它一些初始化方式&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">struct studentInfo </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string name;</p>
<p style="margin-left:.0001pt;text-align:left;">int age;</p>
<p style="margin-left:.0001pt;text-align:left;">double score;</p>
<p style="margin-left:.0001pt;text-align:justify;">}stu1, stu2 &#61; {&#34;小明&#34;, 18, 75.0};    // 定义结构体之后立即创建对象</p>
<p style="margin-left:.0001pt;text-align:left;">// 使用列表初始化</p>
<p style="margin-left:.0001pt;text-align:justify;">studentInfo stu3{&#34;李四&#34;, 22, 87};</p>
<p style="margin-left:.0001pt;text-align:left;">// 使用另一结构体对象进行赋值</p>
<p style="margin-left:.0001pt;text-align:justify;">studentInfo stu4 &#61; stu2;</p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意&#xff1a;</p>
<ol><li>创建结构体变量对象时&#xff0c;可以直接用定义好的结构体名作为类型&#xff1b;相比C语言中的定义&#xff0c;这里省略了关键字struct</li><li>不同的初始化方式效果相同&#xff0c;在不同位置定义的对象作用域不同&#xff1b;</li><li>如果没有赋初始值&#xff0c;那么所有数据将被初始化为默认值&#xff1b;算术类型的默认值就是0&#xff1b;</li><li>一般在代码中&#xff0c;会将结构体的定义和对象的创建分开&#xff0c;便于理解和管理</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>6.4.3 访问结构体中数据</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">访问结构体变量中的数据成员&#xff0c;可以使用成员运算符&#xff08;点号.&#xff09;&#xff0c;后面跟上数据成员的名称。例如stu.name就可以访问stu对象的name成员。</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;学生姓名&#xff1a;&#34; &lt;&lt; stu.name &lt;&lt; &#34;\t年龄&#xff1a;&#34; &lt;&lt; stu.age &lt;&lt; &#34;\t成绩&#xff1a;&#34; &lt;&lt; stu.score &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">这种访问内部成员的方式非常经典&#xff0c;后面要讲到的类的操作中&#xff0c;也会用这种方式访问自己的成员函数。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.4.4 结构体数组</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">可以把结构体和数组结合起来&#xff0c;创建结构体的数组。顾名思义&#xff0c;结构体数组就是元素为结构体的数组&#xff0c;它的定义和访问跟普通的数组完全一样。</p>
<p style="margin-left:.0001pt;text-align:left;">// 结构体数组</p>
<p style="margin-left:.0001pt;text-align:left;">studentInfo s[2] &#61; {</p>
<p style="margin-left:.0001pt;text-align:left;">{&#34;小红&#34;, 18, 92},</p>
<p style="margin-left:.0001pt;text-align:left;">{&#34;小白&#34;, 20, 82}</p>
<p style="margin-left:.0001pt;text-align:left;">};</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;学生姓名&#xff1a;&#34; &lt;&lt; s[0].name &lt;&lt; &#34;\t年龄&#xff1a;&#34; &lt;&lt; s[0].age &lt;&lt; &#34;\t成绩&#xff1a;&#34; &lt;&lt; s[0].score &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;学生姓名&#xff1a;&#34; &lt;&lt; s[1].name &lt;&lt; &#34;\t年龄&#xff1a;&#34; &lt;&lt; s[1].age &lt;&lt; &#34;\t成绩&#xff1a;&#34; &lt;&lt; s[1].score &lt;&lt; endl;</p>
<h4 style="text-align:justify;"><strong><strong><strong>6.5 枚举</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">实际应用中&#xff0c;经常会遇到某个数据对象只能取有限个常量值的情况&#xff0c;比如一周有7天&#xff0c;一副扑克牌有4种花色等等。对于这种情况&#xff0c;C&#43;&#43;提供了另一种批量创建符号常量的方式&#xff0c;可以替代const。这就是“枚举”类型enum。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.5.1 枚举类型定义</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">枚举类型的定义和结构体非常像&#xff0c;需要使用enum关键字。</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义枚举类型</p>
<p style="margin-left:.0001pt;text-align:left;">enum week</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">Mon, Tue, Wed, Thu, Fri, Sat, Sun</p>
<p style="margin-left:.0001pt;text-align:justify;">};</p>
<p style="margin-left:.0001pt;text-align:justify;">与结构体不同的是&#xff0c;枚举类型内只有有限个名字&#xff0c;它们都各自代表一个常量&#xff0c;被称为“枚举量”。</p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意的是&#xff1a;</p>
<ol><li>默认情况下&#xff0c;会将整数值赋给枚举量&#xff1b;</li><li>枚举量默认从0开始&#xff0c;每个枚举量依次加1&#xff1b;所以上面week枚举类型中&#xff0c;一周七天枚举量分别对应着0~6的常量值&#xff1b;</li><li>可以通过对枚举量赋值&#xff0c;显式地设置每个枚举量的值</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>6.5.2 使用枚举类型</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">使用枚举类型也很简单&#xff0c;创建枚举类型的对象后&#xff0c;只能将对应类型的枚举量赋值给它&#xff1b;如果打印它的值&#xff0c;将会得到对应的整数。</p>
<p style="margin-left:.0001pt;text-align:left;">week w1 &#61; Mon;</p>
<p style="margin-left:.0001pt;text-align:left;">week w2 &#61; Tue;</p>
<p style="margin-left:.0001pt;text-align:left;">//week w3 &#61; 2;    // 错误&#xff0c;类型不匹配</p>
<p style="margin-left:.0001pt;text-align:left;">week w3 &#61; week(3);    // int类型强转为week类型后赋值</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;w1 &#61; &#34; &lt;&lt; w1 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;w2 &#61; &#34; &lt;&lt; w2 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;w3 &#61; &#34; &lt;&lt; w3 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意&#xff1a;</p>
<ol><li>如果直接用一个整型值对枚举类型赋值&#xff0c;将会报错&#xff0c;因为类型不匹配&#xff1b;</li><li>可以通过强制类型转换&#xff0c;将一个整型值赋值给枚举对象&#xff1b;</li><li>最初的枚举类型只有列出的值是有效的&#xff1b;而现在C&#43;&#43;通过强制类型转换&#xff0c;允许扩大枚举类型合法值的范围。不过一般使用枚举类型要避免直接强转赋值。</li></ol>
<h4 style="text-align:justify;"><strong><strong><strong>6.6 指针</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">计算机中的数据都存放在内存中&#xff0c;访问内存的最小单元是“字节”&#xff08;byte&#xff09;。所有的数据&#xff0c;就保存在内存中具有连续编号的一串字节里。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/26e728b3fb04bda2c08c0b727f5d0500.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">指针顾名思义&#xff0c;是“指向”另外一种数据类型的复合类型。指针是C/C&#43;&#43;中一种特殊的数据类型&#xff0c;它所保存的信息&#xff0c;其实是另外一个数据对象在内存中的“地址”。通过指针可以访问到指向的那个数据对象&#xff0c;所以这是一种间接访问对象的方法。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/2056408560dc85f43cf44eedc21dd74b.png"  />
</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.6.1 指针的定义</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">指针的定义语法形式为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;"><em>类型</em> * <em>指针变量</em>;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里的类型就是指针所指向的数据类型&#xff0c;后面加上星号“*”&#xff0c;然后跟指针变量的名称。指针在定义的时候可以不做初始化。相比一般的变量声明&#xff0c;看起来指针只是多了一个星号“*”而已。例如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">int* p1;      // p1是指向int类型数据的指针</p>
<p style="margin-left:.0001pt;text-align:left;">long* p2;     // p2是指向long类型数据的指针</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;p1在内存中长度为&#xff1a;&#34; &lt;&lt; sizeof(p1) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;p2在内存中长度为&#xff1a;&#34; &lt;&lt; sizeof(p2) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">p1、p2就是两个指针&#xff0c;分别指向int类型和long类型的数据对象。</p>
<p style="margin-left:.0001pt;text-align:justify;">指针的本质&#xff0c;其实就是一个整数表示的内存地址&#xff0c;它本身在内存中所占大小跟系统环境有关&#xff0c;而跟指向的数据类型无关。64位编译环境中&#xff0c;指针统一占8个字节&#xff1b;若是32位系统则占4字节。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.6.2 指针的用法</strong></strong></strong></h5>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/60ada796cf7264ca2507280593eb2467.png"  />
</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/376a6ea3c4cce379e79fd5ae9b14ff33.png"  />
</p>
<p style="margin-left:.0001pt;text-align:center;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;获取对象地址给指针赋值</p>
<p style="margin-left:.0001pt;text-align:justify;">指针保存的是数据对象的内存地址&#xff0c;所以可以用地址给指针赋值&#xff1b;获取对象地址的方式是使用“取地址操作符”&#xff08;&amp;&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 12;</p>
<p style="margin-left:.0001pt;text-align:left;">int b &#61; 100;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的地址为&#xff1a;&#34; &lt;&lt; &amp;a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;b的地址为&#xff1a;&#34; &lt;&lt; &amp;b &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int* p &#61; &amp;b;           // p是指向b的指针</p>
<p style="margin-left:.0001pt;text-align:justify;">p &#61; &amp;a;    // p指向了a</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;p &#61; &#34; &lt;&lt; p &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">把指针当做一个变量&#xff0c;可以先指向一个对象&#xff0c;再指向另一个不同的对象。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;通过指针访问对象</p>
<p style="margin-left:.0001pt;text-align:justify;">指针指向数据对象后&#xff0c;可以通过指针来访问对象。访问方式是使用“解引用操作符”&#xff08;*&#xff09;&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">p &#61; &amp;a;    // p是指向a的指针</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;p指向的内存中&#xff0c;存放的值为&#xff1a;&#34; &lt;&lt; *p &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">*p &#61; 25;    // 将p所指向的对象&#xff08;a&#xff09;&#xff0c;修改为25</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">在这里由于p指向了a&#xff0c;所以*p可以等同于a。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.6.3</strong></strong><strong> </strong><strong><strong>无效指针、空指针和void*指针</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;无效指针</p>
<p style="margin-left:.0001pt;text-align:justify;">定义一个指针之后&#xff0c;如果不进行初始化&#xff0c;那么它的内容是不确定的&#xff08;比如0xcccc&#xff09;。如果这时把它的内容当成一个地址去访问&#xff0c;就可能访问的是不存在的对象&#xff1b;更可怕的是&#xff0c;如果访问到的是系统核心内存区域&#xff0c;修改其中内容会导致系统崩溃。这样的指针就是“无效指针”&#xff0c;也被叫做“野指针”。</p>
<p style="margin-left:.0001pt;text-align:left;">int* p1;</p>
<p style="margin-left:.0001pt;text-align:justify;">//*p1 &#61; 100;    // 危险&#xff01;指针没有初始化&#xff0c;是无效指针</p>
<p style="margin-left:.0001pt;text-align:justify;">指针非常灵活非常强大&#xff0c;但野指针非常危险。所以建议使用指针的时候&#xff0c;一定要先初始化&#xff0c;让它指向真实的对象。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;空指针</p>
<p style="margin-left:.0001pt;text-align:justify;">如果先定义了一个指针&#xff0c;但确实还不知道它要指向哪个对象&#xff0c;这时可以把它初始化为“空指针”。空指针不指向任何对象。</p>
<p style="margin-left:.0001pt;text-align:left;">int* np &#61; nullptr;    // 空指针字面值</p>
<p style="margin-left:.0001pt;text-align:left;">np &#61; NULL;      // 预处理变量</p>
<p style="margin-left:.0001pt;text-align:left;">np &#61; 0;         // 0值</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int zero &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">//np &#61; zero;      // 错误&#xff0c;int变量不能赋值给指针</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;np &#61; &#34; &lt;&lt; np &lt;&lt; endl;           // 输出0地址</p>
<p style="margin-left:.0001pt;text-align:justify;">//cout &lt;&lt; &#34;*np &#61; &#34; &lt;&lt; *np &lt;&lt; endl;       // 错误&#xff0c;不能访问0地址的内容</p>
<p style="margin-left:.0001pt;text-align:justify;">空指针有几种定义方式&#xff1a;</p>
<ol><li>使用字面值nullptr&#xff0c;这是C&#43;&#43; 11 引入的方式&#xff0c;推荐使用&#xff1b;</li><li>使用预处理变量NULL&#xff0c;这是老版本的方式&#xff1b;</li><li>直接使用0值&#xff1b;</li><li>另外注意&#xff0c;不能直接用整型变量给指针赋值&#xff0c;即使值为0也不行</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">所以可以看出&#xff0c;空指针所保存的其实就是0值&#xff0c;一般把它叫做“0地址”&#xff1b;这个地址也是内存中真实存在的&#xff0c;所以也不允许访问。</p>
<p style="margin-left:.0001pt;text-align:justify;">空指针一般在程序中用来做判断&#xff0c;看一个指针是否指向了数据对象。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;void * 指针</p>
<p style="margin-left:.0001pt;text-align:justify;">一般来说&#xff0c;指针的类型必须和指向的对象类型匹配&#xff0c;否则就会报错。不过有一种指针比较特殊&#xff0c;可以用来存放任意对象的地址&#xff0c;这种指针的类型是void*。</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:left;">string s &#61; &#34;hello&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">void* vp &#61; &amp;i;</p>
<p style="margin-left:.0001pt;text-align:left;">vp &#61; &amp;s;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;vp &#61; &#34; &lt;&lt; vp &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;vp的长度为&#xff1a; &#34; &lt;&lt; sizeof(vp) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">//cout &lt;&lt; &#34;*vp &#61; &#34; &lt;&lt; *vp &lt;&lt; endl;    // 错误&#xff0c;不能通过void *指针访问对象</p>
<p style="margin-left:.0001pt;text-align:justify;">void* 指针表示只知道“保存了一个地址”&#xff0c;至于这个地址对应的数据对象是什么类型并不清楚。所以不能通过 void* 指针访问对象&#xff1b;一般 void* 指针只用来比较地址、或者作为函数的输入输出。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.6.4 指向指针的指针</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">指针本身也是一个数据对象&#xff0c;也有自己的内存地址。所以可以让一个指针保存另一个指针的地址&#xff0c;这就是“指向指针的指针”&#xff0c;有时也叫“二级指针”&#xff1b;形式上可以用连续两个的星号**来表示。类似地&#xff0c;如果是三级指针就是***&#xff0c;表示“指向二级指针的指针”。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/1df488a47d68621514150a43541cbc27.png"  />
</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 1024;</p>
<p style="margin-left:.0001pt;text-align:left;">int* pi &#61; &amp;i;        // pi是一个指针&#xff0c;指向int类型的数据</p>
<p style="margin-left:.0001pt;text-align:left;">int** ppi &#61; π     // ppi是一个二级指针&#xff0c;指向一个int* 类型的指针</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;pi &#61; &#34; &lt;&lt; pi &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;* pi &#61; &#34; &lt;&lt; * pi &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ppi &#61; &#34; &lt;&lt; ppi &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;* ppi &#61; &#34; &lt;&lt; * ppi &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;** ppi &#61; &#34; &lt;&lt; ** ppi &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">如果需要访问二级指针所指向的最原始的那个数据&#xff0c;应该做两次解引用操作。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.6.5 指针和const</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">指针可以和const修饰符结合&#xff0c;这可以有两种形式&#xff1a;一种是指针指向的是一个常量&#xff1b;另一种是指针本身是一个常量。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;指向常量的指针</p>
<p style="margin-left:.0001pt;text-align:justify;">指针指向的是一个常量&#xff0c;所以只能访问数据&#xff0c;不能通过指针对数据进行修改。不过指针本身是变量&#xff0c;可以指向另外的数据对象。这时应该把const加在类型前。</p>
<p style="margin-left:.0001pt;text-align:left;">const int c &#61; 10, c2 &#61; 56;</p>
<p style="margin-left:.0001pt;text-align:left;">//int* pc &#61; &amp;c;        // 错误&#xff0c;类型不匹配</p>
<p style="margin-left:.0001pt;text-align:left;">const int* pc &#61; &amp;c;    // 正确&#xff0c;pc是指向常量的指针&#xff0c;类型为const int *</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">pc &#61; &amp;c2;            // pc可以指向另一个常量</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 1024;</p>
<p style="margin-left:.0001pt;text-align:left;">pc &#61; &amp;i;             // pc也可以指向变量</p>
<p style="margin-left:.0001pt;text-align:justify;">*pc &#61; 1000;          // 错误&#xff0c;不能通过pc更改数据对象</p>
<p style="margin-left:.0001pt;text-align:justify;">这里发现&#xff0c;pc是一个指向常量的指针&#xff0c;但其实把一个变量i的地址赋给它也是可以的&#xff1b;编译器只是不允许通过指针pc去间接更改数据对象。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;指针常量&#xff08;const指针&#xff09;</p>
<p style="margin-left:.0001pt;text-align:justify;">指针本身是一个数据对象&#xff0c;所以也可以区分变量和常量。如果指针本身是一个常量&#xff0c;就意味它保存的地址不能更改&#xff0c;也就是它永远指向同一个对象&#xff1b;而数据对象的内容是可以通过指针改变的。这种指针一般叫做“指针常量”。</p>
<p style="margin-left:.0001pt;text-align:justify;">指针常量在定义的时候&#xff0c;需要在星号*后、标识符前加上const。</p>
<p style="margin-left:.0001pt;text-align:left;">int* const cp &#61; &amp;i;</p>
<p style="margin-left:.0001pt;text-align:left;">*cp &#61; 2048;             // 通过指针修改对象的值</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;i &#61; &#34; &lt;&lt; i &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">//cp &#61; &amp;c;              // 错误&#xff0c;不可以更改cp的指向</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:justify;">const int* const ccp &#61; &amp;c;    // ccp是一个指向常量的常量指针</p>
<p style="margin-left:.0001pt;text-align:justify;">这里也可以使用两个const&#xff0c;定义的是“指向常量的常量指针”。也就是说&#xff0c;ccp指向的是常量&#xff0c;值不能改变&#xff1b;而且它本身也是一个常量&#xff0c;指向的对象也不能改变。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>6</strong></strong><strong><strong>.</strong></strong><strong><strong>6</strong></strong><strong> </strong><strong><strong>指针和数组</strong></strong></strong></h5>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/0e871d6bb2232f044108c9c0443fd67f.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;数组名</p>
<p style="margin-left:.0001pt;text-align:justify;">用到数组名时&#xff0c;编译器一般都会把它转换成指针&#xff0c;这个指针就指向数组的第一个元素。所以我们也可以用数组名来给指针赋值。</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[] &#61; {1,2,3,4,5};</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;arr &#61; &#34; &lt;&lt; arr &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;&amp;arr[0] &#61; &#34; &lt;&lt; &amp;arr[0] &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int* pia &#61; arr;      // 可以直接用数组名给指针赋值</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;* pia &#61; &#34; &lt;&lt; *pia &lt;&lt; endl;    // 指针指向的数据&#xff0c;就是arr[0]</p>
<p style="margin-left:.0001pt;text-align:justify;">也正是因为数组名被认为是指针&#xff0c;所以不能直接使用数组名对另一个数组赋值&#xff0c;数组也不允许这样的直接拷贝&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[] &#61; {1,2,3,4,5};</p>
<p style="margin-left:.0001pt;text-align:justify;">//int arr2[5] &#61; arr;    // 错误&#xff0c;数组不能直接拷贝</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;指针运算</p>
<p style="margin-left:.0001pt;text-align:justify;">如果对指针pia做加1操作&#xff0c;我们会发现它保存的地址直接加了4&#xff0c;这其实是指向了下一个int类型数据对象&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">pia &#43; 1;        // pia &#43; 1 指向的是arr[1]</p>
<p style="margin-left:.0001pt;text-align:justify;">*(pia &#43; 1);        // 访问 arr[1]</p>
<p style="margin-left:.0001pt;text-align:justify;">所谓的“指针运算”&#xff0c;就是直接对一个指针加/减一个整数值&#xff0c;得到的结果仍然是指针。新指针指向的数据元素&#xff0c;跟原指针指向的相比移动了对应个数据单位。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;指针和数组下标</p>
<p style="margin-left:.0001pt;text-align:justify;">我们知道&#xff0c;数组名arr其实就是指针。这就带来了非常有趣的访问方式&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">* arr;     // arr[0]</p>
<p style="margin-left:.0001pt;text-align:justify;">*(arr &#43; 1);     // arr[1]</p>
<p style="margin-left:.0001pt;text-align:justify;">这是通过指针来访问数组元素&#xff0c;效果跟使用下标运算符arr[0]、arr[1]是一样的。进而我们也可以发现&#xff0c;遍历元素所谓的“范围for循环”&#xff0c;其实就是让指针不停地向后移动依次访问元素。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;4&#xff09;指针数组和数组指针</p>
<p style="margin-left:.0001pt;text-align:justify;">指针和数组这两种类型可以结合在一起&#xff0c;这就是“指针数组”和“数组指针”。</p>
<ol><li>指针数组&#xff1a;一个数组&#xff0c;它的所有元素都是相同类型的指针&#xff1b;</li><li>数组指针&#xff1a;一个指针&#xff0c;指向一个数组的指针&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">int arr[] &#61; {1,2,3,4,5};</p>
<p style="margin-left:.0001pt;text-align:left;">int* pa[5];        // 指针数组&#xff0c;里面有5个元素&#xff0c;每个元素都是一个int指针</p>
<p style="margin-left:.0001pt;text-align:left;">int(* ap)[5];      // 数组指针&#xff0c;指向一个int数组&#xff0c;数组包含5个元素</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;指针数组pr的大小为&#xff1a;&#34; &lt;&lt; sizeof(pa) &lt;&lt; endl;    // 40</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;数组指针ap的大小为&#xff1a;&#34; &lt;&lt; sizeof(ap) &lt;&lt; endl;    // 8</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">pa[0] &#61; arr;           // pa中第一个元素&#xff0c;指向arr的第一个元素</p>
<p style="margin-left:.0001pt;text-align:left;">pa[1] &#61; arr &#43; 1;       // pa中第二个元素&#xff0c;指向arr的第二个元素</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">ap &#61; &amp;arr;             // ap指向了arr整个数组</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;arr &#61;&#34; &lt;&lt; arr &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;* arr &#61;&#34; &lt;&lt; *arr &lt;&lt; endl;                 // arr解引用&#xff0c;得到arr[0]</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;arr &#43; 1 &#61;&#34; &lt;&lt; arr &#43; 1 &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ap &#61;&#34; &lt;&lt; ap &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;* ap &#61;&#34; &lt;&lt; *ap &lt;&lt; endl;                   // ap解引用&#xff0c;得到的是arr数组</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ap &#43; 1 &#61;&#34; &lt;&lt; ap &#43; 1 &lt;&lt; endl;            </p>
<p style="margin-left:.0001pt;text-align:justify;">这里可以看到&#xff0c;指向数组arr的指针ap&#xff0c;其实保存的也是arr第一个元素的地址。arr类型是int *&#xff0c;指向的就是arr[0]&#xff1b;而ap类型是int (*) [5]&#xff0c;指向的是整个arr数组。所以arr &#43; 1&#xff0c;得到的是arr[1]的地址&#xff1b;而ap &#43; 1&#xff0c;就会跨过整个arr数组。</p>
<h4 style="text-align:justify;"><strong><strong><strong>6.</strong></strong><strong><strong>7</strong></strong><strong> </strong><strong><strong>引用</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">我们可以在C&#43;&#43;中为数据对象另外起一个名字&#xff0c;这叫做“引用”&#xff08;reference&#xff09;。</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.7.1 引用的用法</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在做声明时&#xff0c;我们可以在变量名前加上“&amp;”符号&#xff0c;表示它是另一个变量的引用。引用必须被初始化。</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:left;">int&amp; ref &#61; a;          // ref是a的引用</p>
<p style="margin-left:.0001pt;text-align:left;">//int&amp; ref2;             // 错误&#xff0c;引用必须初始化</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ref &#61; &#34; &lt;&lt; ref &lt;&lt; endl;            // ref等于a的值</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的地址为&#xff1a;&#34; &lt;&lt; &amp;a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;ref的地址为&#xff1a;&#34; &lt;&lt; &amp;ref &lt;&lt; endl;    // ref和a的地址完全一样</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">引用本质上就是一个“别名”&#xff0c;它本身不是数据对象&#xff0c;所以本身不会存储数据&#xff0c;而是和初始值“绑定”&#xff08;bind&#xff09;在一起&#xff0c;绑定之后就不能再绑定别的对象了。</p>
<p style="margin-left:.0001pt;text-align:justify;">定义了应用之后&#xff0c;对引用做的所有操作&#xff0c;就像直接操作绑定的原始变量一样。所以&#xff0c;引用也是一种间接访问数据对象的方式。</p>
<p style="margin-left:.0001pt;text-align:left;">ref &#61; 20;                  // 更改ref相当于更改a</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:left;">int b &#61; 26;</p>
<p style="margin-left:.0001pt;text-align:left;">ref &#61; b;                  // ref没有绑定b&#xff0c;而是把b的值赋给了ref绑定的a</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的地址为&#xff1a;&#34; &lt;&lt; &amp;a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;b的地址为&#xff1a;&#34; &lt;&lt; &amp;b &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ref的地址为&#xff1a;&#34; &lt;&lt; &amp;ref &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;既然是别名&#xff0c;那么根据这个别名再另起一个别名也是可以的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 引用的引用</p>
<p style="margin-left:.0001pt;text-align:left;">int&amp; rref &#61; ref;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;rref &#61; &#34; &lt;&lt; rref &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的地址为&#xff1a;&#34; &lt;&lt; &amp;a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;ref的地址为&#xff1a;&#34; &lt;&lt; &amp;ref &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;rref的地址为&#xff1a;&#34; &lt;&lt; &amp;rref &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">“引用的引用”&#xff0c;是把引用作为另一个引用的初始值&#xff0c;其实就是给原来绑定的对象又绑定了一个别名&#xff0c;这两个引用绑定的是同一个对象。</p>
<p style="margin-left:.0001pt;text-align:justify;">要注意&#xff0c;引用只能绑定到对象上&#xff0c;而不能跟字面值常量绑定&#xff1b;也就是说&#xff0c;不能把一个字面值直接作为初始值赋给一个引用。而且&#xff0c;引用本身的类型必须跟绑定的对象类型一致。</p>
<p style="margin-left:.0001pt;text-align:left;">//int&amp; ref2 &#61; 10;          // 错误&#xff0c;不能创建字面值的引用</p>
<p style="margin-left:.0001pt;text-align:left;">double d &#61; 3.14;</p>
<p style="margin-left:.0001pt;text-align:justify;">//int&amp; ref3 &#61; d;           // 错误&#xff0c;引用类型和原数据对象类型必须一致</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.7.2 对常量的引用</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">可以把引用绑定到一个常量上&#xff0c;这就是“对常量的引用”。很显然&#xff0c;对常量的引用是常量的别名&#xff0c;绑定的对象不能修改&#xff0c;所以也不能做赋值操作&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">const int zero &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">//int&amp; cref &#61; zero;        // 错误&#xff0c;不能用普通引用去绑定常量</p>
<p style="margin-left:.0001pt;text-align:left;">const int&amp; cref &#61; zero;    // 常量的引用</p>
<p style="margin-left:.0001pt;text-align:justify;">//cref &#61; 10;                 // 错误&#xff0c;不能对常量赋值</p>
<p style="margin-left:.0001pt;text-align:justify;">对常量的引用有时也会直接简称“常量引用”。因为引用只是别名&#xff0c;本身不是数据对象&#xff1b;所以这只能代表“对一个常量的引用”&#xff0c;而不会像“常量指针”那样引起混淆。</p>
<p style="margin-left:.0001pt;text-align:justify;">常量引用和普通变量的引用不同&#xff0c;它的初始化要求宽松很多&#xff0c;只要是可以转换成它指定类型的所有表达式&#xff0c;都可以用来做初始化。</p>
<p style="margin-left:.0001pt;text-align:left;">const int&amp; cref2 &#61; 10;     // 正确&#xff0c;可以用字面值常量做初始化</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 35;</p>
<p style="margin-left:.0001pt;text-align:left;">const int&amp; cref3 &#61; i;      // 正确&#xff0c;可以用一个变量做初始化</p>
<p style="margin-left:.0001pt;text-align:left;">double d &#61; 3.14;</p>
<p style="margin-left:.0001pt;text-align:justify;">const int&amp; cref4 &#61; d;      // 正确&#xff0c;d会先转成int类型&#xff0c;引用绑定的是一个“临时量”</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这样一来&#xff0c;常量引用和对变量的引用&#xff0c;都可以作为一个变量的“别名”&#xff0c;区别在于不能用常量引用去修改对象的值。</p>
<p style="margin-left:.0001pt;text-align:left;">int var &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:left;">int&amp; r1 &#61; var;</p>
<p style="margin-left:.0001pt;text-align:left;">const int&amp; r2 &#61; var;</p>
<p style="margin-left:.0001pt;text-align:left;">r1 &#61; 25;</p>
<p style="margin-left:.0001pt;text-align:justify;">//r2 &#61; 35;                  // 错误&#xff0c;不能通过const引用修改对象值</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.7.3 指针和引用</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">从上一节中可以看到&#xff0c;常量引用和指向常量的指针&#xff0c;有很类似的地方&#xff1a;它们都可以绑定/指向一个常量&#xff0c;也可以绑定/指向一个变量&#xff1b;但不可以去修改对应的变量对象。所以很明显&#xff0c;指针和引用有很多联系。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;引用和指针常量</p>
<p style="margin-left:.0001pt;text-align:justify;">事实上&#xff0c;引用的行为&#xff0c;非常类似于“指针常量”&#xff0c;也就是只能指向唯一的对象、不能更改的指针。</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 引用的行为&#xff0c;和指针常量非常类似</p>
<p style="margin-left:.0001pt;text-align:left;">int&amp; r &#61; a;</p>
<p style="margin-left:.0001pt;text-align:left;">int* const p &#61; &amp;a;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">r &#61; 20;</p>
<p style="margin-left:.0001pt;text-align:left;">*p &#61; 30;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a &#61; &#34; &lt;&lt; a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;a的地址为&#xff1a;&#34; &lt;&lt; &amp;a &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;r &#61; &#34; &lt;&lt; r &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;r的地址为&#xff1a;&#34; &lt;&lt; &amp;r &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;*p &#61; &#34; &lt;&lt; *p &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; &#34;p &#61; &#34; &lt;&lt; p &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">可以看到&#xff0c;所有用到引用r的地方&#xff0c;都可以用*p替换&#xff1b;所有需要获取地址&amp;r的地方&#xff0c;也都可以用p替换。这也就是为什么把操作符*&#xff0c;叫做“解引用”操作符。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;指针的引用</p>
<p style="margin-left:.0001pt;text-align:justify;">指针本身也是一个数据对象&#xff0c;所以当然也可以给它起别名&#xff0c;用一个引用来绑定它。</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 56, j &#61; 28;;</p>
<p style="margin-left:.0001pt;text-align:left;">int* ptr &#61; &amp;i;    // ptr是一个指针&#xff0c;指向int类型对象</p>
<p style="margin-left:.0001pt;text-align:left;">int*&amp; pref &#61; ptr;    // pref是一个引用&#xff0c;绑定指针ptr</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">pref &#61; &amp;j;           // 将指针ptr指向j</p>
<p style="margin-left:.0001pt;text-align:justify;">*pref &#61; 20;           // 将j的值变为20</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">pref是指针ptr的引用&#xff0c;所以下面所有的操作&#xff0c;pref就等同于ptr。</p>
<p style="margin-left:.0001pt;text-align:justify;">可以有指针的引用、引用的引用&#xff0c;也可以有指向指针的指针&#xff1b;但由于引用只是一个“别名”&#xff0c;不是实体对象&#xff0c;所以不存在指向引用的指针。</p>
<p style="margin-left:.0001pt;text-align:left;">int&amp; ref &#61; i;</p>
<p style="margin-left:.0001pt;text-align:left;">//int&amp;* rptr &#61; &amp;ref;     // 错误&#xff0c;不允许使用指向引用的指针</p>
<p style="margin-left:.0001pt;text-align:justify;">int* rptr &#61; &amp;ref;        // 事实上就是指向了i</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;引用的本质</p>
<p style="margin-left:.0001pt;text-align:justify;">引用类似于指针常量&#xff0c;但不等同于指针常量。</p>
<p style="margin-left:.0001pt;text-align:justify;">指针常量本身还是一个数据对象&#xff0c;它保存着另一个对象的地址&#xff0c;而且不能更改&#xff1b;而引用就是“别名”&#xff0c;它会被编译器直接翻译成所绑定的原始变量&#xff1b;所以我们会看到&#xff0c;引用和原始对象的地址是一样&#xff0c;引用并没有额外占用内存空间。这也是为什么不会有“指向引用的指针”。</p>
<p style="margin-left:.0001pt;text-align:justify;">引用的本质&#xff0c;只是C&#43;&#43;引入的一种语法糖&#xff0c;它是对指针的一种伪装。</p>
<p style="margin-left:.0001pt;text-align:justify;">指针是C语言中最灵活、最强大的特性&#xff1b;引用所能做的&#xff0c;其实指针全都可以做。但是指针同时又令人费解、充满危险性&#xff0c;所以C&#43;&#43;中通过引用来代替一些指针的用法。后面在函数部分&#xff0c;我们会对此有更深刻的理解。</p>
<h4 style="text-align:justify;"><strong><strong><strong>6.8 应用案例</strong></strong></strong></h4>
<h5 style="text-align:justify;"><strong><strong><strong>6.8.1 翻转数组</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">翻转数组&#xff0c;就是要把数组中元素的顺序全部反过来。比如一个数组{1,2,3,4,5,6,7,8}&#xff0c;翻转之后就是{8,7,6,5,4,3,2,1}。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;另外创建数组&#xff0c;反向填入元素</p>
<p style="margin-left:.0001pt;text-align:justify;">数组是将元素按照顺序依次存放的&#xff0c;长度固定。所以如果想要让数组“翻转”&#xff0c;一种简单的思路是&#xff1a;直接创建一个相同长度的新数组&#xff0c;然后遍历所有元素&#xff0c;从末尾开始依次反向填入就可以了。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">const int n &#61; 8;</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[n] &#61; { 1,2,3,4,5,6,7,8 };</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 1. 直接创建一个新数组&#xff0c;遍历元素反向填入</p>
<p style="margin-left:.0001pt;text-align:left;">int newArr[n];</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">newArr[n-i-1] &#61; arr;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 打印数组</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; newArr &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">需要注意原数组下标为i的元素&#xff0c;对应翻转后的新数组下标为n-i-1&#xff08;n为数组长度&#xff09;。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;基于原数组翻转</p>
<p style="margin-left:.0001pt;text-align:justify;">另建数组的方式很容易实现&#xff0c;但有明显的缺点&#xff1a;需要额外创建一个数组&#xff0c;占用更多的内存。最好的方式是&#xff0c;不要另开空间&#xff0c;就在原数组上调整位置。</p>
<p style="margin-left:.0001pt;text-align:justify;">这种思路的核心在于&#xff1a;我们应该有两个类似“指针”的东西&#xff0c;每次找到头尾两个元素&#xff0c;将它们调换位置&#xff1b;而后指针分别向中间逼近&#xff0c;再找两个元素对调。由于数组中下标是确定的&#xff0c;因此可以直接用下标代替“指针”。</p>
<p style="margin-left:.0001pt;text-align:left;">// 2. 双指针分别指向数组头尾&#xff0c;元素对调</p>
<p style="margin-left:.0001pt;text-align:left;">int head &#61; 0, tail &#61; n - 1;</p>
<p style="margin-left:.0001pt;text-align:left;">while(head &lt; tail)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; arr[head]; </p>
<p style="margin-left:.0001pt;text-align:left;">arr[head] &#61; arr[tail];</p>
<p style="margin-left:.0001pt;text-align:left;">arr[tail] &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 指针向中间移动</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;head;</p>
<p style="margin-left:.0001pt;text-align:left;">--tail;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 打印数组</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; arr &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; endl;</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.8.2 检验幻方</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">“幻方”是数学上一个有趣的问题&#xff0c;它让一组不同的数字构成一个方阵&#xff0c;并且每行、每列、每个对角线的所有数之和相等。比如最简单的三阶幻方&#xff0c;就是把1~9的数字填到九宫格里&#xff0c;要求横看、竖看、斜着看和都是15。</p>
<p style="margin-left:.0001pt;text-align:justify;">口诀&#xff1a;二四为肩&#xff0c;六八为足&#xff0c;左三右七&#xff0c;戴九履一&#xff0c;五居中央。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/cfc7776cfb5b044407387f25ae582293.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以给定一个n×n的矩阵&#xff0c;也就是二维数组&#xff0c;然后判断它是否是一个幻方&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">const int n &#61; 3;</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[n][n] &#61; {</p>
<p style="margin-left:.0001pt;text-align:left;">{4, 9, 2},</p>
<p style="margin-left:.0001pt;text-align:left;">{3, 5, 7},</p>
<p style="margin-left:.0001pt;text-align:left;">{8, 1, 6}</p>
<p style="margin-left:.0001pt;text-align:left;">};</p>
<p style="margin-left:.0001pt;text-align:left;">// 目标和</p>
<p style="margin-left:.0001pt;text-align:left;">int target &#61; (1 &#43; n * n) * n / 2;</p>
<p style="margin-left:.0001pt;text-align:left;">bool isMagic &#61; true;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 检验每一行</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int sum &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; n; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">sum &#43;&#61; arr[j];</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 如果和不是target&#xff0c;说明不是幻方</p>
<p style="margin-left:.0001pt;text-align:left;">if (sum !&#61; target)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">isMagic &#61; false;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 检验每一列</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; n; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int sum &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">sum &#43;&#61; arr[j];</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">if (sum !&#61; target)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">isMagic &#61; false;</p>
<p style="margin-left:.0001pt;text-align:left;">break;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 检验两个对角线</p>
<p style="margin-left:.0001pt;text-align:left;">int sumDiag1 &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">int sumDiag2 &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">sumDiag1 &#43;&#61; arr;</p>
<p style="margin-left:.0001pt;text-align:left;">sumDiag2 &#43;&#61; arr[n-i-1];</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">if (sumDiag1 !&#61; target || sumDiag2 !&#61; target)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">isMagic &#61; false;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 判断结果</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;给定的矩阵arr&#34; &lt;&lt; (isMagic ? &#34;是&#34; : &#34;不是&#34;) &lt;&lt; n &lt;&lt; &#34;阶幻方&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.8.3 大整数相加</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">实际应用中&#xff0c;有时会遇到非常大的整数&#xff0c;可能会超过long、甚至long long的范围。这时就需要用不限长度的字符串保存数据&#xff0c;然后进行计算。</p>
<p style="margin-left:.0001pt;text-align:justify;">最简单的需求就是“大整数相加”&#xff0c;即给定两个字符串形式的非负大整数 num1 和num2 &#xff0c;计算它们的和。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以把字符串按每个字符一一拆开&#xff0c;相当于遍历整数上的每一个数位&#xff0c;然后通过“乘10叠加”的方式&#xff0c;就可以整合起来了。这相当于算术中的“竖式加法”。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string num1 &#61; &#34;32535943020935527435432875&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">string num2 &#61; &#34;9323298429842985843509&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 用一个空字符串保存结果</p>
<p style="margin-left:.0001pt;text-align:left;">string result;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 获取两数个位的索引</p>
<p style="margin-left:.0001pt;text-align:left;">int p1 &#61; num1.size() - 1;</p>
<p style="margin-left:.0001pt;text-align:left;">int p2 &#61; num2.size() - 1;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 设置一个进位标志</p>
<p style="margin-left:.0001pt;text-align:left;">int carry &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">while (p1 &gt;&#61; 0 || p2 &gt;&#61; 0 || carry &gt; 0)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int x &#61; (p1 &gt;&#61; 0) ? (num1[p1] - &#39;0&#39;) : 0;</p>
<p style="margin-left:.0001pt;text-align:left;">int y &#61; (p2 &gt;&#61; 0) ? (num2[p2] - &#39;0&#39;) : 0;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int sum &#61; x &#43; y &#43; carry;  </p>
<p style="margin-left:.0001pt;text-align:left;">result &#43;&#61; (sum % 10 &#43; &#39;0&#39;);    // 和的个位写入结果</p>
<p style="margin-left:.0001pt;text-align:left;">carry &#61; sum / 10;              // 和的十位保存在进位上</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 继续遍历下一位</p>
<p style="margin-left:.0001pt;text-align:left;">--p1;</p>
<p style="margin-left:.0001pt;text-align:left;">--p2;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 结果需要做翻转</p>
<p style="margin-left:.0001pt;text-align:left;">int i &#61; 0, j &#61; result.size() - 1;</p>
<p style="margin-left:.0001pt;text-align:left;">while (i &lt; j)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">char temp &#61; result[j];</p>
<p style="margin-left:.0001pt;text-align:left;">result[j] &#61; result;</p>
<p style="margin-left:.0001pt;text-align:left;">result &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;i;</p>
<p style="margin-left:.0001pt;text-align:left;">--j;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num1 &lt;&lt; &#34; &#43; &#34; &lt;&lt; num2 &lt;&lt; endl &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34; &#61; &#34; &lt;&lt; result;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.8.4 旋转图像</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">旋转图像的需求&#xff0c;在图片处理的过程中非常常见。我们知道对于计算机而言&#xff0c;图像其实就是一组像素点的集合&#xff0c;所以图像旋转的问题&#xff0c;本质上就是一个二维数组的旋转问题。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以给定一个二维数组&#xff0c;用来表示一个图像&#xff0c;然后将它顺时针旋转90°。例如&#xff0c;对于4×4的矩阵&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 5, 1, 9, 11},</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 2, 4, 8, 10},</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 13, 3, 6, 7},</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 15, 14, 12, 16}</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">旋转之后变为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 15, 13, 2, 5},</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 14, 3, 4, 1},</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 12, 6, 8, 9},</p>
<p style="margin-left:.0001pt;text-align:justify;">  { 16, 7, 10, 11}</p>
<p style="margin-left:.0001pt;text-align:justify;">]</p>
<p style="margin-left:.0001pt;text-align:justify;">根据数学上矩阵的特性&#xff0c;可以把矩阵A先做转置得到AT&#xff0c;然后再翻转每一行就可以了。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">const int n &#61; 4;</p>
<p style="margin-left:.0001pt;text-align:left;">int image[n][n] &#61; {</p>
<p style="margin-left:.0001pt;text-align:left;">  { 5, 1, 9, 11},</p>
<p style="margin-left:.0001pt;text-align:left;">  { 2, 4, 8, 10},</p>
<p style="margin-left:.0001pt;text-align:left;">  { 13, 3, 6, 7},</p>
<p style="margin-left:.0001pt;text-align:left;">  { 15, 14, 12, 16}</p>
<p style="margin-left:.0001pt;text-align:left;">};</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 矩阵转置</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt;&#61; i; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 以对角线为对称轴&#xff0c;两边互换</p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; image[j];</p>
<p style="margin-left:.0001pt;text-align:left;">image[j] &#61; image[j];</p>
<p style="margin-left:.0001pt;text-align:left;">image[j] &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 每一行翻转</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; n / 2; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; image[j];</p>
<p style="margin-left:.0001pt;text-align:left;">image[j] &#61; image[n-j-1];</p>
<p style="margin-left:.0001pt;text-align:left;">image[n - j - 1] &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 打印输出</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int j &#61; 0; j &lt; n; j&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; image[j] &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>6.8.5 翻转链表</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">链表&#xff08;Linked List&#xff09;是一种常见的基础数据结构&#xff0c;它是一种线性表&#xff0c;但是并不会像数组那样按顺序存储数据&#xff0c;而是在每一个节点里存指向下一个节点的指针。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/a5337bc5b32a418b7282b9596bb2a012.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">对于数组&#xff0c;可以通过下标访问每个元素&#xff1b;如果想要翻转一个数组&#xff0c;只要不停地把头尾元素互换就可以了。</p>
<p style="margin-left:.0001pt;text-align:justify;">而链表并没有“下标”&#xff0c;所以想要访问元素只能依次遍历&#xff1b;如果要翻转一个链表&#xff0c;关键就在于“next”指针需要反向。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以先定义一个结构体类型ListNode&#xff0c;用来表示链表中的每个节点&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#pragma once</p>
<p style="margin-left:.0001pt;text-align:left;">struct ListNode</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int value;</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* next;</p>
<p style="margin-left:.0001pt;text-align:justify;">};</p>
<p style="margin-left:.0001pt;text-align:justify;">自定义一个头文件list_node.h&#xff0c;将结构体TreeNode的定义放在里面&#xff0c;这样之后如果需要使用它&#xff0c;就可以直接引入&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">#include &#34;list_node.h&#34;</p>
<p style="margin-left:.0001pt;text-align:justify;">这里的#prama once是一条预处理指令&#xff0c;表示头文件的内容只被解析一次&#xff0c;不会重复处理。</p>
<p style="margin-left:.0001pt;text-align:justify;">接下来就可以实现翻转链表的过程了。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">#include &#34;list_node.h&#34;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义一个链表 1-&gt;2-&gt;3-&gt;4-&gt;5-&gt;NULL</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode node5 &#61; { 5, nullptr };</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode node4 &#61; { 4, &amp;node5 };</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode node3 &#61; { 3, &amp;node4 };</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode node2 &#61; { 2, &amp;node3 };</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode node1 &#61; { 1, &amp;node2 };</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* list &#61; &amp;node1;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* curr &#61; list;</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* prev &#61; nullptr; </p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 翻转链表</p>
<p style="margin-left:.0001pt;text-align:left;">while (curr)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* temp &#61; curr-&gt;next;</p>
<p style="margin-left:.0001pt;text-align:left;">curr-&gt;next &#61; prev;</p>
<p style="margin-left:.0001pt;text-align:left;">prev &#61; curr;</p>
<p style="margin-left:.0001pt;text-align:left;">curr &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* newList &#61; prev;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 打印链表</p>
<p style="margin-left:.0001pt;text-align:left;">ListNode* np &#61; newList;</p>
<p style="margin-left:.0001pt;text-align:left;">while (np)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; np-&gt;value &lt;&lt; &#34;\t-&gt;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">np &#61; np-&gt;next;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;null&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里对指向结构体对象的curr指针&#xff0c;需要先解引用&#xff0c;然后取它所指向ListNode对象里的next指针。这个过程本应该写作&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">(*curr).next;</p>
<p style="margin-left:.0001pt;text-align:justify;">这个写法比较麻烦&#xff0c;所以一般会用另一种简化写法&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">curr-&gt;next;</p>
<p style="margin-left:.0001pt;text-align:justify;">这两个写法完全等价。这里的“-&gt;”叫做“箭头运算符”&#xff0c;它是解引用和访问成员两个操作的结合&#xff1b;这样就可以很方便地表示“取指针所指向内容的成员”。</p>
<h3 style="text-align:justify;"><strong><strong><strong>七、函数</strong></strong></strong></h3>
<p style="margin-left:.0001pt;text-align:justify;">函数其实就是封装好的代码块&#xff0c;并且指定一个名字&#xff0c;调用这个名字就可以执行代码并返回一个结果。</p>
<h4 style="text-align:justify;"><strong><strong><strong>7.1 </strong></strong><strong><strong>函数基本知识</strong></strong></strong></h4>
<h5 style="text-align:justify;"><strong><strong><strong>7.1.1 </strong></strong><strong><strong>函数定义</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">一个完整的函数定义主要包括以下部分&#xff1a;</p>
<ol><li>返回类型&#xff1a;调用函数之后&#xff0c;返回结果的数据类型&#xff1b;</li><li>函数名&#xff1a;用来命名代码块的标识符&#xff0c;在当前作用域内唯一&#xff1b;</li><li>参数列表&#xff1a;参数表示函数调用时需要传入的数据&#xff0c;一般叫做“形参”&#xff1b;放在函数名后的小括号里&#xff0c;可以有0个或多个&#xff0c;用逗号隔开&#xff1b;</li><li>函数体&#xff1a;函数要执行的语句块&#xff0c;用花括号括起来。</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">函数一般都是一个实现了固定功能的模块&#xff0c;把参数看成“输入”&#xff0c;返回结果看成“输出”&#xff0c;函数就是一个输入到输出的映射关系。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以定义一个非常简单的平方函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 平方函数 y &#61; f(x) &#61; x ^ 2</p>
<p style="margin-left:.0001pt;text-align:left;">int square(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int y &#61; x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">return y;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">使用流程控制语句return&#xff0c;就可以返回结果。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.1.2 </strong></strong><strong><strong>函数调用</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">调用函数时&#xff0c;使用的是“调用运算符”&#xff0c;就是跟在函数名后面的一对小括号&#xff1b;括号内是用逗号隔开的参数列表。</p>
<p style="margin-left:.0001pt;text-align:justify;">这里的参数不是定义时的形参&#xff0c;而是为了初始化形参传入的具体值&#xff1b;为了跟函数定义时的形参列表区分&#xff0c;把它叫作“实参”。</p>
<p style="margin-left:.0001pt;text-align:justify;">调用表达式的类型就是函数的返回类型&#xff0c;值就是函数执行返回的结果。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 平方函数 y &#61; f(x^2)</p>
<p style="margin-left:.0001pt;text-align:left;">int square(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; n &lt;&lt; &#34;的平方是&#xff1a;&#34; &lt;&lt; square(n) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意&#xff1a;</p>
<ol><li>实参是形参的初始值&#xff0c;所以函数调用时传入实参&#xff0c;相当于执行了int x &#61; 6的初始化操作&#xff1b;实参的类型必须跟形参类型匹配&#xff1b;</li><li>实参的个数必须跟形参一致&#xff1b;如果有多个形参&#xff0c;要按照位置顺序一一对应&#xff1b;</li><li>如果函数本身没有参数&#xff0c;参数列表可以为空&#xff0c;但空括号不能省&#xff1b;</li><li>形参列表中多个参数用逗号分隔&#xff0c;每个都要带上类型&#xff0c;类型相同也不能省略&#xff1b;</li><li>如果函数不需要返回值&#xff0c;可以定义返回类型为void&#xff1b;</li><li>函数返回类型不能是数组或者函数</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>7.1.3 案例练习</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">下面几个案例可以作为函数的基本练习。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;求两个数的立方和</p>
<p style="margin-left:.0001pt;text-align:justify;">定义一个函数&#xff0c;输入两个整型参数x、y&#xff0c;返回x3 &#43; y3。</p>
<p style="margin-left:.0001pt;text-align:left;">int cubeSum(int x, int y)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return pow(x, 3) &#43; pow(y, 3);</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;求阶乘</p>
<p style="margin-left:.0001pt;text-align:justify;">阶乘的计算公式n! &#61; 1 × 2 × 3 ×…× n&#xff0c;可以用一个循环来实现。定义一个求阶乘的函数&#xff0c;传入一个整数n&#xff0c;返回n!。</p>
<p style="margin-left:.0001pt;text-align:left;">// 求阶乘</p>
<p style="margin-left:.0001pt;text-align:left;">int factorial(int n)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int result &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 1; i &lt;&#61; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">result *&#61; i;</p>
<p style="margin-left:.0001pt;text-align:left;">return result;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;复制字符串</p>
<p style="margin-left:.0001pt;text-align:justify;">定义一个函数&#xff0c;传入一个字符串str和一个整数n&#xff0c;将字符串str复制n次后返回。</p>
<p style="margin-left:.0001pt;text-align:left;">// 复制字符串</p>
<p style="margin-left:.0001pt;text-align:left;">string copyStr(string str, int n)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string result;</p>
<p style="margin-left:.0001pt;text-align:left;">while (n &gt; 0)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">result &#43;&#61; str;</p>
<p style="margin-left:.0001pt;text-align:left;">--n;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">return result;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong>7.1.4 局部变量的生命周期</strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">之前介绍过变量的作用域&#xff0c;对于花括号内定义的变量&#xff0c;具有“块作用域”&#xff0c;在花括号外就不可见了。函数体都是语句块&#xff0c;而主函数main本身也是一个函数&#xff1b;所以在main中定义的所有变量、所有函数形参和在函数体内部定义的变量&#xff0c;都具有块作用域&#xff0c;统称为“局部变量”。局部变量仅在函数作用域内部可见。</p>
<p style="margin-left:.0001pt;text-align:left;">// 函数形参x是局部变量&#xff0c;作用域为函数内部</p>
<p style="margin-left:.0001pt;text-align:left;">void f(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 函数内部定义的变量a是局部变量&#xff0c;作用域为函数内部</p>
<p style="margin-left:.0001pt;text-align:left;">int a &#61; 10;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 主函数中定义的变量b也是局部变量&#xff0c;作用域为主函数内</p>
<p style="margin-left:.0001pt;text-align:left;">int b &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;作用域指的是变量名字的可见范围&#xff1b;变量不可见&#xff0c;并不代表变量所指代的数据对象就销毁了。这是两个不同的概念&#xff1a;</p>
<ol><li>作用域&#xff1a;针对名字而言&#xff0c;是程序文本中的一部分&#xff0c;名字在这部分可见&#xff1b;</li><li>生命周期&#xff1a;针对数据对象而言&#xff0c;是程序在执行过程中&#xff0c;对象从创建到销毁的时间段</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">基于作用域&#xff0c;变量可以分为“局部变量”和“全局变量”。对于全局变量而言&#xff0c;名字全局可见&#xff0c;对象也只有在程序结束时才销毁。</p>
<p style="margin-left:.0001pt;text-align:justify;">而对于局部变量代表的数据对象&#xff0c;基于生命周期&#xff0c;又可以分为“自动对象”和“静态对象”。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;自动对象</p>
<p style="margin-left:.0001pt;text-align:justify;">平常代码中定义的普通局部变量&#xff0c;生命周期为&#xff1a;在程序执行到变量定义语句时创建&#xff0c;在程序运行到当前块末尾时销毁。这样的对象称为“自动对象”。</p>
<p style="margin-left:.0001pt;text-align:justify;">形参也是一种自动对象。形参定义在函数体作用域内&#xff0c;一旦函数终止&#xff0c;形参也就被销毁了。</p>
<p style="margin-left:.0001pt;text-align:justify;">对于自动对象来说&#xff0c;它的生命周期和作用域是一致的。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;静态对象</p>
<p style="margin-left:.0001pt;text-align:justify;">如果希望延长一个局部变量的生命周期&#xff0c;让它在作用域外依然保留&#xff0c;可以在定义局部变量时加上static关键字&#xff1b;这样的对象叫做“局部静态对象”。</p>
<p style="margin-left:.0001pt;text-align:justify;">局部静态对象只有局部的作用域&#xff0c;在块外依然是不可见的&#xff1b;但是它的生命周期贯穿整个程序运行过程&#xff0c;只有在程序结束时才被销毁&#xff0c;这一点与全局变量类似。</p>
<p style="margin-left:.0001pt;text-align:left;">// 显示自身被调用多少次的函数</p>
<p style="margin-left:.0001pt;text-align:left;">int callCount()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">static int cnt &#61; 0;    // 静态对象只会创建一次</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;我被调用了&#34; &lt;&lt; &#43;&#43;cnt &lt;&lt; &#34;次&#xff01;&#34; &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">return cnt;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">//cout &lt;&lt; cnt &lt;&lt; endl;    // 错误&#xff0c;局部变量在作用域外不可见</p>
<p style="margin-left:.0001pt;text-align:justify;">callCount();</p>
<p style="margin-left:.0001pt;text-align:left;">callCount();</p>
<p style="margin-left:.0001pt;text-align:left;">callCount();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">可以发现&#xff0c;静态对象只在第一次执行到定义语句时创建出来&#xff0c;之后即使函数执行结束&#xff0c;它的值依然保持&#xff1b;下一次函数调用时&#xff0c;不会再次创建、也不会重新赋值&#xff0c;而是直接在之前的值基础上继续叠加。</p>
<p style="margin-left:.0001pt;text-align:justify;">静态对象和自动对象应用的场景不同&#xff0c;所以它们存放的内存区域也是不一样的。静态对象如果不在代码中做初始化&#xff0c;基本类型会被默认初始化为0值。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.1.5 函数声明</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">如果我们将一个函数放在主函数后面&#xff0c;就会出现运行错误&#xff1a;找不到标识符。这是因为函数和变量一样&#xff0c;使用之前必须要做声明。函数只有一个定义&#xff0c;可以定义在任何地方&#xff1b;如果需要调用函数&#xff0c;只需要在调用前做一个声明&#xff0c;告诉编译器“存在这个函数”就可以了。</p>
<p style="margin-left:.0001pt;text-align:justify;">函数声明的方式&#xff0c;和函数的定义非常相似&#xff1b;区别在于声明时不需要把函数体写出来&#xff0c;用一个分号替代就可以了。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 声明函数</p>
<p style="margin-left:.0001pt;text-align:left;">int square(int x);</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; n &lt;&lt; &#34;的平方是&#xff1a;&#34; &lt;&lt; square(n) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义函数</p>
<p style="margin-left:.0001pt;text-align:left;">int square(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int y &#61; x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">return y;</p>
<p style="margin-left:.0001pt;text-align:left;">return x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">事实上&#xff0c;由于没有函数体的执行过程&#xff0c;所以形参的名字也完全不需要&#xff0c;可以省略。可以直接这样声明一个函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">int square(int);</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">函数声明中包含了返回类型、函数名和形参类型&#xff0c;这就说明了调用这个函数所需要的所有信息。函数声明也被叫做“函数原型”。</p>
<p style="margin-left:.0001pt;text-align:justify;">一般情况下&#xff0c;把函数声明放在头文件中会更加方便。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.1.6 分离式编译和头文件</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;分离式编译</p>
<p style="margin-left:.0001pt;text-align:justify;">当程序越来越复杂&#xff0c;我们就会希望代码分散到不同的文件中来做管理。C&#43;&#43;支持分离式编译&#xff0c;这就可以把函数单独放在一个文件&#xff0c;独立编译之后链接运行。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如可以把复制字符串的函数单独保存成一个文件copy_string.cpp&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;string&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;">// 复制字符串</p>
<p style="margin-left:.0001pt;text-align:left;">string copyStr(string str, int n)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string result;</p>
<p style="margin-left:.0001pt;text-align:left;">while (n &gt; 0)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">result &#43;&#61; str;</p>
<p style="margin-left:.0001pt;text-align:left;">--n;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">return result;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">然后只要在主函数调用之前做声明就可以了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 声明函数</p>
<p style="margin-left:.0001pt;text-align:left;">string copyStr(string, int);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; copyStr(&#34;hello &#34;, n) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;编写头文件</p>
<p style="margin-left:.0001pt;text-align:justify;">对于一个项目而言&#xff0c;有些定义可能是所有文件共用的&#xff0c;比如一些常量、结构体/类&#xff0c;以及功能性的函数。于是每次需要引入时&#xff0c;都得做一堆声明——这显然太麻烦了。</p>
<p style="margin-left:.0001pt;text-align:justify;">一个好方法是&#xff0c;把它们定义在同一个文件中&#xff0c;需要时用一句#include统一引入就可以了&#xff0c;就像使用库一样。这样的文件以.h作为后缀&#xff0c;被称为“头文件”。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如我们可以把之前的一些功能性的函数&#xff08;比如求平方、阶乘、复制字符串等&#xff09;&#xff0c;放在一个叫做utils.h的头文件中&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#pragma once</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;string&gt;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 平方函数 y &#61; f(x^2)</p>
<p style="margin-left:.0001pt;text-align:left;">int square(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int y &#61; x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">return y;</p>
<p style="margin-left:.0001pt;text-align:left;">return x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 求立方和</p>
<p style="margin-left:.0001pt;text-align:left;">int cubeSum(int x, int y)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return pow(x, 3) &#43; pow(y, 3);</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 求阶乘</p>
<p style="margin-left:.0001pt;text-align:left;">int factorial(int n)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int result &#61; 1;</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 1; i &lt;&#61; n; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">result *&#61; i;</p>
<p style="margin-left:.0001pt;text-align:left;">return result;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 复制字符串</p>
<p style="margin-left:.0001pt;text-align:justify;">std::string copyStr(std::string str, int n);</p>
<p style="margin-left:.0001pt;text-align:justify;">这里有两点需要说明&#xff1a;</p>
<ol><li>#pragma once是一条预处理指令&#xff0c;表示这个头文件的内容只会被编译一次&#xff0c;这就避免了多次引入头文件时的重复定义&#xff1b;</li><li>复制字符串函数copyStr已经在别的文件单独做了定义&#xff0c;这里只要声明就可以&#xff1b;</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">如果想要使用这些函数&#xff0c;只要在文件中引入头文件即可&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">#include &#34;utils.h&#34;</p>
<p style="margin-left:.0001pt;text-align:justify;">这里文件名没有使用尖括号&lt;&gt;&#xff0c;而是使用了引号&#xff1b;这表示要在当前项目的根目录下寻找文件&#xff0c;而不是到编译器默认的库目录下去找。</p>
<h4 style="text-align:justify;"><strong><strong><strong>7.2 </strong></strong><strong><strong>参数传递</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">函数在每次调用时&#xff0c;都会重新创建形参&#xff0c;并且用传入的实参对它进行初始化。形参的类型&#xff0c;决定了形参和实参交互的方式&#xff1b;也决定了函数的不同功能。</p>
<p style="margin-left:.0001pt;text-align:justify;">可以先回忆一下对变量的初始化&#xff1a;对一个变量做初始化&#xff0c;如果用另一个变量给它赋初值&#xff0c;意味着值的拷贝&#xff1b;也就是说&#xff0c;此后这两个变量各自一份数据&#xff0c;各自管理&#xff0c;互不影响。而如果是定义一个引用&#xff0c;绑定另一个变量做初始化&#xff0c;并不会引发值的拷贝&#xff1b;引用和原变量管理的是同一个数据对象。</p>
<p style="margin-left:.0001pt;text-align:left;">int i1 &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">int i2 &#61; i1;</p>
<p style="margin-left:.0001pt;text-align:left;">i2 &#61; 1;      // i1的值仍然是0</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int&amp; i3 &#61; i1;</p>
<p style="margin-left:.0001pt;text-align:left;">i3 &#61; 10;      // i1的值也变为10</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">参数传递和变量的初始化类似&#xff0c;根据形参的类型可以分为两种方式&#xff1a;传值&#xff08;value&#xff09;和传引用&#xff08;reference&#xff09;。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.2.1 </strong></strong><strong><strong>传值参数</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">直接将一个实参的值&#xff0c;拷贝给形参做初始化的传参方式&#xff0c;就被称为“值传递”&#xff0c;这样的参数被称为“传值参数”。之前我们练习过的所有函数&#xff0c;都是采用这种传值调用的方式。</p>
<p style="margin-left:.0001pt;text-align:left;">int square(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return x * x;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; n &lt;&lt; &#34;的平方是&#xff1a;&#34; &lt;&lt; square(n) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">在上面平方函数的调用中&#xff0c;实参n的值&#xff08;6&#xff09;被拷贝给了形参x。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;传值的困扰</p>
<p style="margin-left:.0001pt;text-align:justify;">值传递这种方式非常简单&#xff0c;但是面对这样的需求会有些麻烦&#xff1a;传入一个数据对象&#xff0c;让它经过函数处理之后发生改变。例如&#xff0c;传入一个整数x&#xff0c;调用之后它自己的值要加1。这看起来很简单&#xff0c;但如果直接&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">void increase(int x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;x;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 6;</p>
<p style="margin-left:.0001pt;text-align:left;">increase(n);        // n的值不会增加</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这样做并不能实现需求。因为实参n的值是拷贝给形参x的&#xff0c;之后x的任何操作&#xff0c;都不会改变n。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;指针形参</p>
<p style="margin-left:.0001pt;text-align:justify;">使用指针形参可以解决这个问题。如果我们把指向数据对象的指针作为形参&#xff0c;那么初始化时拷贝的就是指针的值&#xff1b;复制之后的指针&#xff0c;依然指向原始数据对象&#xff0c;这样就可以保留它的更改了。</p>
<p style="margin-left:.0001pt;text-align:left;">// 指针形参</p>
<p style="margin-left:.0001pt;text-align:left;">void increase(int* p)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;(*p);</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">increase( &amp;n );        // 传入n的地址&#xff0c;调用函数后n的值会加1</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>7.2.2 </strong></strong><strong><strong>传引用参数</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">使用指针形参可以解决值传递的问题&#xff0c;不过这种方式函数定义显得有些繁琐&#xff0c;每次调用还需要记住传入变量的地址&#xff0c;使用起来不够方便。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;传引用方便函数调用</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;新增了引用的概念&#xff0c;可以替换必须使用指针的场景。采用引用作为函数形参&#xff0c;可以使函数调用更加方便。这种传参方式叫做“传引用参数”。之前的例子就可以改写成&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 传引用</p>
<p style="margin-left:.0001pt;text-align:left;">void increase(int&amp; x)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;x;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int n &#61; 0;</p>
<p style="margin-left:.0001pt;text-align:left;">increase( n );        // 调用函数后n的值会加1</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">由于使用了引用作为形参&#xff0c;函数调用时就可以直接传入n的值&#xff0c;而不用传地址了&#xff1b;x只是n的一个别名&#xff0c;修改x就修改了n。对比可以发现&#xff0c;这段代码相比最初尝试写出的传值实现&#xff0c;只是多了一个引用声明&amp;而已。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;传引用避免拷贝</p>
<p style="margin-left:.0001pt;text-align:justify;">使用引用还有一个非常重要的场景&#xff0c;就是不希望进行值拷贝的时候。实际应用中&#xff0c;很多时候函数要操作的对象可能非常庞大&#xff0c;如果做值拷贝会使得效率大大降低&#xff1b;这时使用引用就是一个好方法。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如&#xff0c;想要定义一个函数比较两个字符串的长度&#xff0c;需要将两个字符串作为参数传入。因为字符串有可能非常长&#xff0c;直接做值拷贝并不是一个好选择&#xff0c;最好的方式就是传递引用&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 比较两个字符串的长度</p>
<p style="margin-left:.0001pt;text-align:left;">bool isLonger(const string &amp; str1, const string &amp; str2)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return str1.size() &gt; str2.size();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;使用常量引用做形参</p>
<p style="margin-left:.0001pt;text-align:justify;">在上面的例子中&#xff0c;比较两个字符串长度&#xff0c;并不会更改字符串本身的内容&#xff0c;所以可以把形参定义为常量引用。</p>
<p style="margin-left:.0001pt;text-align:justify;">这样的好处是&#xff0c;既避免了对数据对象可能的更改&#xff0c;也扩大了调用时能传的实参的范围。因为之前讨论过常量引用的特点&#xff0c;可以用字面值常量对它做初始化&#xff0c;也可以用变量做初始化。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以在代码中&#xff0c;一般要尽量使用常量引用作为形参。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>7.2.</strong></strong><strong><strong>3</strong></strong><strong> </strong><strong><strong>数组形参</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">之前已经介绍过&#xff0c;数组是不允许做直接拷贝的&#xff0c;所以如果想要把数组作为函数的形参&#xff0c;使用值传递的方式是不可行的。与此同时&#xff0c;数组名可以解析成一个指针&#xff0c;所以可以用传递指针的方式来处理数组。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如一个简单的函数&#xff0c;需要遍历int类型数组所有元素并输出&#xff0c;就可以这样声明&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">void printArray(const int*);     // 指向int类型常量的指针</p>
<p style="margin-left:.0001pt;text-align:left;">void printArray(const int[]);   </p>
<p style="margin-left:.0001pt;text-align:justify;">void printArray(const int[5]);</p>
<p style="margin-left:.0001pt;text-align:justify;">由于只是遍历输出&#xff0c;不需要修改数组内容&#xff0c;所以这里使用了const。</p>
<p style="margin-left:.0001pt;text-align:justify;">以上三种声明方式&#xff0c;本质上是一样的&#xff0c;形参的类型都是const int *&#xff1b;虽然第三种方式指定了数组长度&#xff0c;但由于编译器会把传入的数组名解析成指针&#xff0c;事实上的数组长度还是无法确定的。</p>
<p style="margin-left:.0001pt;text-align:justify;">这就带来另一个问题&#xff1a;在函数中&#xff0c;遍历元素时怎样确定数组的结束&#xff1f;</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;规定结束标记</p>
<p style="margin-left:.0001pt;text-align:justify;">一种简单思路是&#xff0c;规定一个特殊的“结束标记”&#xff0c;遇到这个标记就代表当前数组已经遍历完了。典型代表就是C语言风格的字符串&#xff0c;是以空字符’\0’为结束标志的char数组。</p>
<p style="margin-left:.0001pt;text-align:justify;">这种方式比较麻烦&#xff0c;而且太多特殊规定也不适合像int这样的数据类型。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;把数组长度作为形参</p>
<p style="margin-left:.0001pt;text-align:justify;">除指向数组的指针外&#xff0c;可以再增加一个形参&#xff0c;专门表示数组的长度&#xff0c;这样就可以方便地遍历数组了。</p>
<p style="margin-left:.0001pt;text-align:left;">void printArray(const int* arr, int size)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; size; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; arr &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">int arr[6] &#61; { 1,2,3,4,5,6 };</p>
<p style="margin-left:.0001pt;text-align:left;">printArray(arr, 6);</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">在C语言和老式的C&#43;&#43;程序中&#xff0c;经常使用这种方法来处理数组。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;使用数组引用作为形参</p>
<p style="margin-left:.0001pt;text-align:justify;">之前的方法依赖指针&#xff0c;所以都显得比较麻烦。更加方便的做法&#xff0c;还是用引用来替代指针的功能。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;允许使用数组的引用作为函数形参&#xff0c;这样一来&#xff0c;引用作为别名绑定在数组上&#xff0c;使用引用就可以直接遍历数组了。</p>
<p style="margin-left:.0001pt;text-align:left;">// 使用数组引用作为形参</p>
<p style="margin-left:.0001pt;text-align:left;">void printArray(const int(&amp;arr)[6])</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num : arr)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">int arr[6] &#61; { 1,2,3,4,5,6 };</p>
<p style="margin-left:.0001pt;text-align:left;">printArray(arr);</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意的是&#xff0c;定义一个数组引用时需要用括号将&amp;和引用名括起来&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">int(&amp;arr)[6]        // 正确&#xff0c;arr是一个引用&#xff0c;绑定的是长度为6的int数组</p>
<p style="margin-left:.0001pt;text-align:justify;">// int &amp; arr[6]     // 错误&#xff0c;这是引用的数组&#xff0c;不允许使用</p>
<p style="margin-left:.0001pt;text-align:justify;">使用数组引用之后&#xff0c;调用函数直接传入数组名就可以了。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>7.2.4 可变形参</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">有时候我们并不确定函数中应该有几个形参&#xff0c;这时就需要使用“可变形参”来表达。</p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43;中表示可变形参的方式主要有三种&#xff1a;</p>
<ol><li>省略符&#xff08;…&#xff09;&#xff1a;兼容C语言的用法&#xff0c;只能出现在形参列表的最后一个位置&#xff1b;</li><li>初始化列表initializer_list&#xff1a;跟vector类似&#xff0c;也是一种标准库模板类型&#xff1b;initializer_list对象中的元素只能是常量值&#xff0c;不能更改&#xff1b;</li><li>可变参数模板&#xff1a;这是一种特殊的函数&#xff0c;后面会详细介绍。</li></ol>
<h4 style="text-align:justify;"><strong><strong><strong>7.3 </strong></strong><strong><strong>返回类型</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">函数可以通过return语句&#xff0c;终止函数的执行并“返回”函数调用的地方&#xff1b;并且可以给定返回值。返回值的类型由函数声明时的“返回类型”决定。</p>
<p style="margin-left:.0001pt;text-align:justify;">return语句可以有两种形式&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">return;<em> </em>           // 直接返回&#xff0c;无返回值</p>
<p style="margin-left:.0001pt;text-align:justify;">return  <em>表达式</em>;     // 返回表达式的值</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>7.3.1 </strong></strong><strong><strong>无返回值</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">当函数返回类型为void时&#xff0c;表示函数没有返回值。可以在函数中需要返回时直接执行 return语句&#xff0c;也可以不写。因为返回类型为void的函数执行完最后一句&#xff0c;会自动加上return返回。</p>
<p style="margin-left:.0001pt;text-align:justify;">例如&#xff0c;可以将之前“两元素值互换”的代码&#xff0c;包装成一个函数。可以先做一个判断&#xff0c;如果两者相等就直接返回&#xff0c;这样可以提高运行效率。</p>
<p style="margin-left:.0001pt;text-align:left;">// 元素互换</p>
<p style="margin-left:.0001pt;text-align:left;">void swap(int&amp; x, int&amp; y)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (x &#61;&#61; y)</p>
<p style="margin-left:.0001pt;text-align:left;">return;    // 不需要交换&#xff0c;直接返回</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; x;</p>
<p style="margin-left:.0001pt;text-align:left;">x &#61; y;</p>
<p style="margin-left:.0001pt;text-align:left;">y &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这里判断如果元素相等就直接返回&#xff0c;有些类似于流程控制中的break。最后一句代码后面省略了return。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.3.2 </strong></strong><strong><strong>有返回值</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">如果函数返回类型不为void&#xff0c;那么函数必须执行return&#xff0c;并且每条return必须返回一个值。返回值的类型应该跟函数返回类型一致&#xff0c;或者可以隐式转换为一致。</p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;函数返回值的原理</p>
<p style="margin-left:.0001pt;text-align:justify;">函数在调用点会创建一个“临时量”&#xff0c;用来保存函数调用的结果。当使用return语句返回时&#xff0c;就会用返回值去初始化这个临时量。所以返回值的相关规则&#xff0c;跟变量或者形参的初始化是一致的。</p>
<p style="margin-left:.0001pt;text-align:justify;">之前写过一个“比较字符串长度”的isLonger函数&#xff0c;我们可以稍作修改&#xff0c;让它可以返回较长的那个字符串&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 字符串比较长度&#xff0c;返回较长的</p>
<p style="margin-left:.0001pt;text-align:left;">string longerStr(const string&amp; str1, const string&amp; str2)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return str1.size() &gt; str2.size() ? str1 : str2;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string str1 &#61; &#34;hello world!&#34;, str2 &#61; &#34;c&#43;&#43; is interesting!&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; longerStr(str1, str2) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">调用这个函数&#xff0c;经过判断发现str2较长&#xff0c;这时执行return将返回str2。由于返回类型是string&#xff0c;所以将用str2对一个string临时量做初始化&#xff0c;执行的是值拷贝。最终返回的值&#xff0c;是str2的一个副本。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;返回引用类型</p>
<p style="margin-left:.0001pt;text-align:justify;">对于string对象&#xff0c;显然做值拷贝并不高效。所以我们依然可以借鉴之前的经验&#xff0c;使用引用类型来做返回值的传递&#xff0c;这样就可以避免值拷贝。</p>
<p style="margin-left:.0001pt;text-align:left;">// 返回一个string常量对象的引用&#xff0c;不做值拷贝</p>
<p style="margin-left:.0001pt;text-align:left;">const string &amp; longerStr(const string&amp; str1, const string&amp; str2)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return str1.size() &gt; str2.size() ? str1 : str2;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里我们同样把返回值定义成了常量引用&#xff0c;方式和作用跟形参完全一样。</p>
<p style="margin-left:.0001pt;text-align:justify;">上面函数返回的是形参str1或者str2的引用&#xff1b;而函数中的形参本身又是引用类型&#xff0c;所以最终是实参对象的引用。</p>
<p style="margin-left:.0001pt;text-align:justify;">而如果返回的是一个函数内局部变量的引用&#xff0c;比如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">const string &amp; f()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string str &#61; &#34;test&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">return str;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这样做是不安全的&#xff1a;因为str是函数内部的局部对象&#xff0c;函数执行完成后就销毁了&#xff1b;而返回值是它的引用&#xff0c;相当于引用了一个不存在的对象&#xff0c;这可能会导致无法预料的问题。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以&#xff0c;函数返回引用类型时&#xff0c;不能返回局部对象的引用&#xff1b;同样道理&#xff0c;也不应该返回指向局部对象的指针。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;返回类对象后连续调用</p>
<p style="margin-left:.0001pt;text-align:justify;">如果函数返回一个类的对象&#xff0c;那么我们可以继续调用这个对象的成员函数&#xff0c;这样就形成了“链式调用”。例如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">longerStr(str1, str2).size();</p>
<p style="margin-left:.0001pt;text-align:justify;">调用运算符&#xff0c;和访问对象成员的点运算符优先级相同&#xff0c;并且满足左结合律。所以链式调用就是从左向右依次调用&#xff0c;代码可读性会更高。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.3.3 主函数的返回值</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">主函数main是一个特殊函数&#xff0c;它是我们执行程序的入口。所以C&#43;&#43;中对主函数的返回值也有特殊的规定&#xff1a;即使返回类型不是void&#xff0c;主函数也可以省略return语句。如果主函数执行到结尾都没有return语句&#xff0c;编译器就会自动插入一条&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">return 0;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">主函数的返回值可以看做程序运行的状态指示器&#xff1a;返回0表示运行成功&#xff1b;返回非0值则表示失败。非0值具体的含义依赖机器决定。</p>
<p style="margin-left:.0001pt;text-align:justify;">这也是为什么之前我们在主函数中都可以不写return。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.3.3 </strong></strong><strong><strong>返回数组指针</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">与形参的讨论类似&#xff0c;由于数组“不能拷贝”的特点&#xff0c;函数也无法直接返回一个数组。同样的&#xff0c;我们可以使用指针或者引用来实现返回数组的目标&#xff1b;通常会返回一个数组指针。</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[5] &#61; { 1,2,3,4,5 };</p>
<p style="margin-left:.0001pt;text-align:left;">int* pa[5];        // 指针数组&#xff0c;pa是包含5个int指针的数组</p>
<p style="margin-left:.0001pt;text-align:left;">int(*ap)[5] &#61; &amp;arr;    // 数组指针&#xff0c;ap是一个指针&#xff0c;指向长度为5的int数组</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:justify;">int(*fun(int x))[5];    // 函数声明&#xff0c;fun返回值类型为数组指针</p>
<p style="margin-left:.0001pt;text-align:justify;">这里对于函数fun的声明&#xff0c;我们可以进行层层解析&#xff1a;</p>
<ol><li>fun(int x) &#xff1a;函数名为fun&#xff0c;形参为int类型的x&#xff1b;</li><li>( * fun(int x) )&#xff1a;函数返回的结果&#xff0c;可以执行解引用操作&#xff0c;说明是一个指针&#xff1b;</li><li>( * fun(int x) )[5]&#xff1a;函数返回结果解引用之后是一个长度为5的数组&#xff0c;说明返回类型是数组指针&#xff1b;</li><li>int ( * fun(int x) )[5]&#xff1a;数组中元素类型为int</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">数组指针的定义比较繁琐&#xff0c;为了简化这个定义&#xff0c;我们可以使用关键字typedef来定义一个类型的别名&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">typedef int arrayT[5];    // 类型别名&#xff0c;arrayT代表长度为5的int数组</p>
<p style="margin-left:.0001pt;text-align:justify;">arrayT* fun2(int x);      // fun2的返回类型是指向arrayT的指针</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">C&#43;&#43; 11新标准还提供了另一种简化方式&#xff0c;用一个-&gt;符号跟在形参列表后面&#xff0c;再把类型单独提出来放到最后。这种方式叫做“尾置返回类型”。</p>
<p style="margin-left:.0001pt;text-align:justify;">auto fun3(int x) -&gt; int(*)[5];    // 尾置返回类型</p>
<p style="margin-left:.0001pt;text-align:justify;">因为返回类型放到了末尾&#xff0c;所以前面的类型用了自动推断的auto。</p>
<h4 style="text-align:justify;"><strong><strong><strong>7</strong></strong><strong><strong>.</strong></strong><strong><strong>4</strong></strong><strong> </strong><strong><strong>递归</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">如果一个函数调用了自身&#xff0c;这样的函数就叫做“递归函数”&#xff08;recursive function&#xff09;。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.4.1 递归的实现</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">递归是调用自身&#xff0c;如果不加限制&#xff0c;这个过程是不会结束的&#xff1b;函数永远调用自己下去&#xff0c;最终会导致程序栈空间耗尽。所以在递归函数中&#xff0c;一定会有某种“基准情况”&#xff0c;这个时候不会调用自身&#xff0c;而是直接返回结果。基准情况的处理保证了递归能够结束。</p>
<p style="margin-left:.0001pt;text-align:justify;">递归是不断地自我重复&#xff0c;这一点和循环有相似之处。事实上&#xff0c;递归和循环往往可以实现同样的功能。</p>
<p style="margin-left:.0001pt;text-align:justify;">比如之前求阶乘的函数&#xff0c;我们可以用递归的方式重新实现&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 递归方式求阶乘</p>
<p style="margin-left:.0001pt;text-align:left;">int factorial(int n)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (n &#61;&#61; 1)</p>
<p style="margin-left:.0001pt;text-align:left;">return 1;</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">return factorial(n - 1) * n;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;5! &#61; &#34; &lt;&lt; factorial(5) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里我们的基准情况是n &#61;&#61; 1&#xff0c;也就是当n不断减小&#xff0c;直到1时就结束递归直接返回。5的阶乘具体计算流程如下&#xff1a;</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/cf2a3d6598ab154e6a9b1b371bacda6d.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">因为递归至少需要额外的栈空间开销&#xff0c;所以递归的效率往往会比循环低一些。不过在很多数学问题上&#xff0c;递归可以让代码非常简洁。</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.4.2 经典递归——斐波那契数列</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;指的是这样一个数列&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">1, 1, 2, 3, 5, 8, 13, 21, 34, …</p>
<p style="margin-left:.0001pt;text-align:justify;">它的规律是&#xff1a;当前数字&#xff0c;是之前两个数字之和。在数学上&#xff0c;斐波那契数列被以递推的方法定义&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">F(0)&#61;1&#xff0c;F(1)&#61;1, F(n) &#61; F(n - 1) &#43; F(n - 2)&#xff08;n ≥ 2&#xff0c;n ∈ N*&#xff09;</p>
<p style="margin-left:.0001pt;text-align:justify;">这天然适合使用递归实现&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int fib(int n)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">if (n &#61;&#61; 1 || n &#61;&#61; 2)</p>
<p style="margin-left:.0001pt;text-align:left;">return 1;</p>
<p style="margin-left:.0001pt;text-align:left;">return fib(n - 2) &#43; fib(n - 1);</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;fib(9) &#61; &#34; &lt;&lt; fib(9) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h4 style="text-align:justify;"><strong><strong><strong>7.5 应用案例</strong></strong></strong></h4>
<h5 style="text-align:justify;"><strong><strong><strong>7.5.1 二分查找</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;它是一种效率较高的查找方法&#xff0c;前提是数据对象必须先排好序。二分查找事实上采用的是一种“分治”策略&#xff0c;它充分利用了元素间的次序关系。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 可以递归调用的二分查找</p>
<p style="margin-left:.0001pt;text-align:left;">int search(const int(&amp;a)[10], int start, int end, int target)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 基准情况&#xff1a;目标值超出范围&#xff0c;或者start &gt; end&#xff0c;说明没有找到</p>
<p style="margin-left:.0001pt;text-align:left;">if ( target &lt; a[start] || target &gt; a[end] || start &gt; end)</p>
<p style="margin-left:.0001pt;text-align:left;">return -1;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 取二分的中间坐标</p>
<p style="margin-left:.0001pt;text-align:left;">int mid &#61; (start &#43; end) / 2;</p>
<p style="margin-left:.0001pt;text-align:left;">// 比较中间值和目标值的大小</p>
<p style="margin-left:.0001pt;text-align:left;">if (a[mid] &#61;&#61; target)</p>
<p style="margin-left:.0001pt;text-align:left;">return mid;        // 找到了</p>
<p style="margin-left:.0001pt;text-align:left;">else if (a[mid] &gt; target)</p>
<p style="margin-left:.0001pt;text-align:left;">return search(a, start, mid - 1, target);    // 比目标值大&#xff0c;在更小的部分找</p>
<p style="margin-left:.0001pt;text-align:left;">else</p>
<p style="margin-left:.0001pt;text-align:left;">return search(a, mid &#43; 1, end, target);      // 比目标值小&#xff0c;在更大的部分找</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[10] &#61; { 1,2,3,4,5,6,9,12,25,38 };</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int key &#61; 25;</p>
<p style="margin-left:.0001pt;text-align:left;">int size &#61; sizeof(arr) / sizeof(arr[0]);</p>
<p style="margin-left:.0001pt;text-align:left;">int result &#61; search(arr, 0, size - 1, key);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">result &#61;&#61; -1 </p>
<p style="margin-left:.0001pt;text-align:left;">? cout &lt;&lt; &#34;在数组中没有找到&#34; &lt;&lt; key &lt;&lt; &#34;!&#34; &lt;&lt; endl</p>
<p style="margin-left:.0001pt;text-align:left;">: cout &lt;&lt; &#34;在数组中找到&#34; &lt;&lt; key &lt;&lt; &#34;&#xff0c;索引下标为&#xff1a;&#34; &lt;&lt; result &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.5.2 快速排序</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">之前介绍过两种对一组数据进行排序的算法&#xff1a;选择排序和冒泡排序&#xff0c;它们都需要使用两层for循环遍历数组&#xff0c;效率较低。一种巧妙的改进思路是&#xff1a;通过一次扫描&#xff0c;将待排记录分隔成独立的两部分&#xff0c;其中一部分的值全比另一部分的小&#xff1b;接下来分别对这两部分继续进行排序&#xff0c;最终全部排完。这种算法更加高效&#xff0c;被称为“快速排序”。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/22c9c5428f7b7a4ee9bc85e43262a1a1.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">可以看出&#xff0c;快排也应用了分治思想&#xff0c;一般会用递归来实现。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">void quickSort(int(&amp;)[10], int, int);</p>
<p style="margin-left:.0001pt;text-align:left;">int partition(int(&amp;)[10], int, int);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">void printArr(const int(&amp;)[10]);</p>
<p style="margin-left:.0001pt;text-align:left;">void swap(int(&amp;)[10], int, int);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int arr[10] &#61; { 23, 45, 18, 6, 11, 19, 22, 18, 12, 9 };</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printArr(arr);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int size &#61; sizeof(arr) / sizeof(arr[0]);</p>
<p style="margin-left:.0001pt;text-align:left;">quickSort(arr, 0, size - 1);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printArr(arr);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 快速排序</p>
<p style="margin-left:.0001pt;text-align:left;">void quickSort(int(&amp;a)[10], int start, int end)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 基准情况</p>
<p style="margin-left:.0001pt;text-align:left;">if (start &gt;&#61; end)</p>
<p style="margin-left:.0001pt;text-align:left;">return;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 分区&#xff0c;返回分区点下标</p>
<p style="margin-left:.0001pt;text-align:left;">int mid &#61; partition(a, start, end);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 递归调用&#xff0c;分别对两部分继续排序</p>
<p style="margin-left:.0001pt;text-align:left;">quickSort(a, start, mid - 1);</p>
<p style="margin-left:.0001pt;text-align:left;">quickSort(a, mid &#43; 1, end);</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 按照pivot分区的函数</p>
<p style="margin-left:.0001pt;text-align:left;">int partition(int(&amp;a)[10], int start, int end)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 选取一个分区的“支点”</p>
<p style="margin-left:.0001pt;text-align:left;">int pivot &#61; a[start];</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int left &#61; start, right &#61; end;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">while (left &lt; right)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 分别从左右两边遍历数组</p>
<p style="margin-left:.0001pt;text-align:left;">while (a[left] &lt;&#61; pivot &amp;&amp; left &lt; right)</p>
<p style="margin-left:.0001pt;text-align:left;">&#43;&#43;left;</p>
<p style="margin-left:.0001pt;text-align:left;">while (a[right] &gt;&#61; pivot &amp;&amp; left &lt; right)</p>
<p style="margin-left:.0001pt;text-align:left;">--right;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 左右互换</p>
<p style="margin-left:.0001pt;text-align:left;">swap(a, left, right);</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">if (a[left] &lt; pivot) {</p>
<p style="margin-left:.0001pt;text-align:left;">swap(a, start, left);</p>
<p style="margin-left:.0001pt;text-align:left;">return left; </p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">else if (a[left] &gt; pivot)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">swap(a, start, left - 1);</p>
<p style="margin-left:.0001pt;text-align:left;">return left - 1;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 数组中两元素互换的函数</p>
<p style="margin-left:.0001pt;text-align:left;">void swap(int(&amp;a)[10], int i, int j)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">int temp &#61; a;</p>
<p style="margin-left:.0001pt;text-align:left;">a &#61; a[j];</p>
<p style="margin-left:.0001pt;text-align:left;">a[j] &#61; temp;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 打印输出一个数组</p>
<p style="margin-left:.0001pt;text-align:left;">void printArr(const int(&amp;a)[10]) </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num : a)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h5 style="text-align:justify;"><strong><strong><strong>7.5.3 遍历二叉树</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">跟数组不同&#xff0c;树是一种非线性的数据结构&#xff0c;是由n&#xff08;n &gt;&#61;0&#xff09;个节点组成的有限集合。如果n&#61;&#61;0&#xff0c;树为空树。如果n&gt;0&#xff0c;树有一个特定的节点&#xff0c;叫做根节点&#xff08;root&#xff09;。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/a61535cb7f10d76fb17a76bf3798974c.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">对于树这种数据结构&#xff0c;使用最频繁的是二叉树。每个节点最多只有2个子节点的树&#xff0c;叫做二叉树。二叉树中&#xff0c;每个节点的子节点作为根的两个子树&#xff0c;一般叫做节点的左子树和右子树。</p>
<p class="img-center">
<img alt=""  src="https://i-blog.csdnimg.cn/blog_migrate/bea588aa786bc8f18c69e8e1ba7e4c84.png"  />
</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以为树的节点定义一种结构体类型&#xff0c;而且为了方便以后在不同的文件中使用&#xff0c;还可以自定义一个头文件tree_node.h&#xff0c;将结构体TreeNode的定义放在里面&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#pragma once</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;string&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">struct TreeNode</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string name;</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode* left;</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode* right;</p>
<p style="margin-left:.0001pt;text-align:justify;">};</p>
<p style="margin-left:.0001pt;text-align:justify;">在别的文件中&#xff0c;如果想要使用TreeNode这个结构体&#xff0c;我们只要引入就可以&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">#include &#34;TreeNode.h&#34;</p>
<p style="margin-left:.0001pt;text-align:justify;">对于树的遍历&#xff0c;主要有这样三种方式&#xff1a;</p>
<ol><li>先序遍历&#xff1a;先访问根节点&#xff0c;再访问左子树&#xff0c;最后访问右子树&#xff1b;</li><li>中序遍历&#xff1a;先访问左子树&#xff0c;再访问根节点&#xff0c;最后访问右子树&#xff1b;</li><li>后序遍历&#xff1a;先访问左子树&#xff0c;再访问右子树&#xff0c;最后访问根节点。</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">这种遍历方式就隐含了“递归”的思路&#xff1a;左右子树本身又是一棵树&#xff0c;同样需要按照对应的规则来遍历。</p>
<p style="margin-left:.0001pt;text-align:justify;">我们可以先单独创建一个文件print_tree.cpp&#xff0c;实现二叉树的遍历方法&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">#include &#34;tree_node.h&#34;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 先序遍历打印二叉树</p>
<p style="margin-left:.0001pt;text-align:left;">void printTreePreOrder(TreeNode* root)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 基准情况&#xff0c;如果是空树&#xff0c;直接返回</p>
<p style="margin-left:.0001pt;text-align:left;">if (root &#61;&#61; nullptr)    return;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">//cout &lt;&lt; (*root).name &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; root-&gt;name &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 递归打印左右子树</p>
<p style="margin-left:.0001pt;text-align:left;">printTreePreOrder(root-&gt;left);</p>
<p style="margin-left:.0001pt;text-align:left;">printTreePreOrder(root-&gt;right);</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 中序遍历打印二叉树</p>
<p style="margin-left:.0001pt;text-align:left;">void printTreeInOrder(TreeNode* root)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 基准情况&#xff0c;如果是空树&#xff0c;直接返回</p>
<p style="margin-left:.0001pt;text-align:left;">if (root &#61;&#61; nullptr)    return;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printTreeInOrder(root-&gt;left);</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; root-&gt;name &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;">printTreeInOrder(root-&gt;right);</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 后序遍历打印二叉树</p>
<p style="margin-left:.0001pt;text-align:left;">void printTreePostOrder(TreeNode* root)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 基准情况&#xff0c;如果是空树&#xff0c;直接返回</p>
<p style="margin-left:.0001pt;text-align:left;">if (root &#61;&#61; nullptr)    return;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printTreePostOrder(root-&gt;left);</p>
<p style="margin-left:.0001pt;text-align:left;">printTreePostOrder(root-&gt;right);</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; root-&gt;name &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">然后将这些函数的声明也放到头文件tree_node.h中&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">void printTreePreOrder(TreeNode* root);</p>
<p style="margin-left:.0001pt;text-align:left;">void printTreeInOrder(TreeNode* root);</p>
<p style="margin-left:.0001pt;text-align:justify;">void printTreePostOrder(TreeNode* root);</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">接下来就可以在代码中实现具体的功能了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">#include &#34;tree_node.h&#34;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 定义一棵二叉树</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeG &#61; {&#34;G&#34;, nullptr, nullptr};</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeF &#61; { &#34;F&#34;, nullptr, nullptr };</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeE &#61; { &#34;E&#34;, &amp;nodeG, nullptr };</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeD &#61; { &#34;D&#34;, nullptr, nullptr };</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeC &#61; { &#34;C&#34;, nullptr, &amp;nodeF};</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeB &#61; { &#34;B&#34;, &amp;nodeD, &amp;nodeE };</p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode nodeA &#61; { &#34;A&#34;, &amp;nodeB, &amp;nodeC };</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">TreeNode* tree &#61; &amp;nodeA;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printTreePreOrder(tree);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printTreeInOrder(tree);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">printTreePostOrder(tree);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<h3 style="text-align:justify;"><strong><strong><strong>八、函数高阶</strong></strong></strong></h3>
<p style="margin-left:.0001pt;text-align:justify;">函数是模块化编程思想的重要体现&#xff0c;相对于传统的C语言&#xff0c;C&#43;&#43;还提供了很多新的函数特性。这一章我们就来深入探讨一下函数的高级用法以及在C&#43;&#43;中的新特性。</p>
<h4 style="text-align:justify;"><strong><strong><strong>8.1 内联函数</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">内联函数是C&#43;&#43;为了提高运行速度做的一项优化。</p>
<p style="margin-left:.0001pt;text-align:justify;">函数让代码更加模块化&#xff0c;可重用性、可读性大大提高&#xff1b;不过函数也有一个缺点&#xff1a;函数调用需要执行一系列额外操作&#xff0c;会降低程序运行效率。</p>
<p style="margin-left:.0001pt;text-align:justify;">为了解决这个问题&#xff0c;C&#43;&#43;引入了“内联函数”的概念。使用内联函数时&#xff0c;编译器不再去做常规的函数调用&#xff0c;而是把它在调用点上“内联”展开&#xff0c;也就是直接用函数代码替换了函数调用。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.1.1 内联函数的定义</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">定义内联函数&#xff0c;只需要在函数声明或者函数定义前加上inline关键字。</p>
<p style="margin-left:.0001pt;text-align:justify;">例如之前写过的函数&#xff1a;比较两个字符串、并返回较长的那个&#xff0c;就可以重写为内联函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">inline const string&amp; longerStr(const string&amp; str1, const string&amp; str2)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return str1.size() &gt; str2.size() ? str1 : str2;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">当我们试图打印输出调用结果时&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; longerStr(str1, str2) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">编译器会自动把它展开为&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; (str1.size() &gt; str2.size() ? str1 : str2) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">这样就大大提高了运行效率。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.1.2 内联函数和宏</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">内联函数是C&#43;&#43;新增的特性。在C语言中&#xff0c;类似功能是通过预处理语句#define定义“宏”来实现的。</p>
<p style="margin-left:.0001pt;text-align:justify;">然而C中的宏本身并不是函数&#xff0c;无法进行值传递&#xff1b;它的本质是文本替换&#xff0c;我们一般只用宏来定义常量。用宏实现函数的功能会比较麻烦&#xff0c;而且可读性较差。所以在C&#43;&#43;中&#xff0c;一般都会用内联函数来取代C中的宏。</p>
<h4 style="text-align:justify;"><strong><strong><strong>8.2 默认实参</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">在有些场景中&#xff0c;当调用一个函数时它的某些形参一般都会被赋一个固定的值。为了简单起见&#xff0c;我们可以给它设置一个“默认值”&#xff0c;这样就不用每次都传同样的值了。</p>
<p style="margin-left:.0001pt;text-align:justify;">这种会反复出现的默认值&#xff0c;称为函数的“默认实参”。当调用一个有默认实参的函数时&#xff0c;这个实参可以省略。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.2.1 定义带默认实参的函数</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">我们用一个string对象表示学生基本信息&#xff0c;调用函数时应传入学生的姓名、年龄和平均成绩。对于这些参数&#xff0c;我们可以指定默认实参&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 默认实参</p>
<p style="margin-left:.0001pt;text-align:left;">string stuInfo(string name &#61; &#34;&#34;, int age &#61; 18, double score &#61; 60) </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string info &#61; &#34;学生姓名&#xff1a;&#34; &#43; name &#43; &#34;\t年龄&#xff1a;&#34; &#43; </p>
<p style="margin-left:.0001pt;text-align:left;">to_string(age) &#43; &#34;\t平均成绩&#xff1a;&#34; &#43; to_string(score);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">return info;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">定义默认实参&#xff0c;形式上就是给形参做初始化。这里在整合学生信息时&#xff0c;使用了运算符&#43;进行字符串拼接&#xff0c;并且调用to_string函数将age和score转换成了string。</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意&#xff0c;一旦某个形参被定义了默认实参&#xff0c;那它后面的所有形参都必须有默认实参。也就是说&#xff0c;所有默认实参的指定&#xff0c;应该在形参列表的末尾。</p>
<p style="margin-left:.0001pt;text-align:left;">// 错误&#xff0c;默认实参不在形参列表末尾</p>
<p style="margin-left:.0001pt;text-align:left;">//string stuInfo(string name &#61; &#34;&#34;, int age &#61; 18, double score);    </p>
<p style="margin-left:.0001pt;text-align:justify;">// 正确&#xff0c;可以前面的形参没有默认实参</p>
<p style="margin-left:.0001pt;text-align:justify;">string stuInfo(string name, int age &#61; 18, double score &#61; 60);      </p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>8.2.2 使用默认实参调用函数</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">函数调用时&#xff0c;如果对某个形参不传实参&#xff0c;那么它初始化时用的就是默认实参的值。由于之前所有形参都定义了默认实参&#xff0c;因此可以用不同的传参方式调用函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; stuInfo() &lt;&lt; endl;                      // &#34;&#34;&#xff0c;18, 60.0</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; stuInfo(&#34;张三&#34;) &lt;&lt; endl;                // &#34;张三&#34;&#xff0c;18, 60.0</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; stuInfo(&#34;李四&#34;, 20) &lt;&lt; endl;            // &#34;李四&#34;&#xff0c;20, 60.0</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; stuInfo(&#34;王五&#34;, 22, 85.5) &lt;&lt; endl;      // &#34;王五&#34;&#xff0c;22, 85.5</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">//cout &lt;&lt; stuInfo(19, 92.5) &lt;&lt; endl;         // 错误&#xff0c;不能跳过前面的形参给后面传值</p>
<p style="margin-left:.0001pt;text-align:justify;">//cout &lt;&lt; stuInfo(, , 59.5) &lt;&lt; endl;         // 错误&#xff0c;只能省略末尾的形参</p>
<p style="margin-left:.0001pt;text-align:justify;">可以看到&#xff0c;默认实参定义时要优先放到形参列表的尾部&#xff1b;而调用时&#xff0c;只能省略尾部的参数&#xff0c;不能跳过前面的形参给后面传值。</p>
<h4 style="text-align:justify;"><strong><strong><strong>8.3 函数重载</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">在C&#43;&#43;中&#xff0c;同一作用域下&#xff0c;同一个函数名是可以定义多次的&#xff0c;前提是形参列表不同。这种名字相同但形参列表不同的函数&#xff0c;叫做“重载函数”。这是C&#43;&#43;相对C语言的重大改进&#xff0c;也是面向对象的基础。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.3.1 定义重载函数</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">在上一章数组形参部分&#xff0c;我们曾经实现过几个不同的打印数组的函数&#xff0c;它们是可以同时存在的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">// 使用指针和长度作为形参</p>
<p style="margin-left:.0001pt;text-align:left;">void printArray(const int* arr, int size)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int i &#61; 0; i &lt; size; i&#43;&#43;)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; arr &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">// 使用数组引用作为形参</p>
<p style="margin-left:.0001pt;text-align:left;">void printArray(const int(&amp;arr)[6])</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">for (int num : arr)</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; num &lt;&lt; &#34;\t&#34;;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:justify;">{</p>
<p style="margin-left:.0001pt;text-align:justify;">int arr[6] &#61; { 1,2,3,4,5,6 };</p>
<p style="margin-left:.0001pt;text-align:left;">printArray(arr, 6);           // 传入两个参数&#xff0c;调用第一种实现</p>
<p style="margin-left:.0001pt;text-align:left;">printArray(arr);              // 传入一个参数&#xff0c;调用第二种实现</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意&#xff1a;</p>
<ol><li>重载的函数&#xff0c;应该在形参的数量或者类型上有所不同&#xff1b;</li><li>形参的名称在类型中可以省略&#xff0c;所以只有形参名不同的函数是一样的&#xff1b;</li><li>调用函数时&#xff0c;编译器会根据传递的实参个数和类型&#xff0c;自动推断使用哪个函数&#xff1b;</li><li>主函数不能重载</li></ol>
<h5 style="text-align:justify;"><strong><strong><strong>8.3.2 有const形参时的重载</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">当形参有const修饰时&#xff0c;要区分它对于实参的要求到底是什么&#xff0c;是否要进行值的拷贝。如果是传值参数&#xff0c;传入实参时会发生值的拷贝&#xff0c;那么实参是变量还是常量其实是没有区别的&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">void fun(int x);</p>
<p style="margin-left:.0001pt;text-align:justify;">void fun(const int x);     // int常量做形参&#xff0c;跟不加const等价</p>
<p style="margin-left:.0001pt;text-align:left;">void fun2(int* p);</p>
<p style="margin-left:.0001pt;text-align:left;">void fun2(int* const p);    // 指针常量做形参&#xff0c;也跟不加const等价</p>
<p style="margin-left:.0001pt;text-align:justify;">这种情况下&#xff0c;const不会影响传入函数的实参类型&#xff0c;所以跟不加const的定义是一样的&#xff1b;这叫做“顶层const”。这时两个函数相同&#xff0c;无法进行函数重载。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">另一种情况则不同&#xff0c;那就是传引用参数。这时如果有const修饰&#xff0c;就成了“常量的引用”&#xff1b;对于一个常量&#xff0c;只能用常量引用来绑定&#xff0c;而不能使用普通引用。</p>
<p style="margin-left:.0001pt;text-align:justify;">类似地&#xff0c;对于一个常量的地址&#xff0c;只能由“指向常量的指针”来指向它&#xff0c;而不能用普通指针。</p>
<p style="margin-left:.0001pt;text-align:justify;">void fun3(int &amp;x);</p>
<p style="margin-left:.0001pt;text-align:justify;">void fun3(const int &amp; x);     // 形参类型是常量引用&#xff0c;这是一个新函数 </p>
<p style="margin-left:.0001pt;text-align:left;">void fun4(int* p);</p>
<p style="margin-left:.0001pt;text-align:left;">void fun4(const int* p);    // 形参类型是指向常量的指针&#xff0c;这是一个新函数</p>
<p style="margin-left:.0001pt;text-align:justify;">这种情况下&#xff0c;const限制了间接访问的数据对象是常量&#xff0c;这叫做“底层const”。当实参是常量时&#xff0c;不能对不带const的引用进行初始化&#xff0c;所以只能调用常量引用做形参的函数&#xff1b;而如果实参是变量&#xff0c;就会优先匹配不带const的普通引用&#xff1a;这就实现了函数重载。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.3.3 函数匹配</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">如果传入的实参跟形参类型不同&#xff0c;只要能通过隐式类型转换变成需要类型&#xff0c;函数也可以正确调用。那假如有几个不同的重载函数&#xff0c;它们的形参类型可以进行自动转换&#xff0c;这时传入实参应该调用哪个函数呢&#xff1f;例如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">void f();</p>
<p style="margin-left:.0001pt;text-align:justify;">void f(int x); </p>
<p style="margin-left:.0001pt;text-align:justify;">void f(int x, int y); </p>
<p style="margin-left:.0001pt;text-align:justify;">void f(double x, double y &#61; 1.5);</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">f(3.14);      // 应该调用哪个函数&#xff1f;</p>
<p style="margin-left:.0001pt;text-align:justify;">确定到底调用哪个函数的过程&#xff0c;叫做“函数匹配”。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;1&#xff09;候选函数</p>
<p style="margin-left:.0001pt;text-align:justify;">函数匹配的第一步&#xff0c;就是确定“候选函数”&#xff0c;也就是先找到对应的重载函数集。候选函数有两个要求&#xff1a;</p>
<ol><li>与调用的函数同名</li><li>函数的声明&#xff0c;在函数的调用点是可见的</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">所以上面的例子中&#xff0c;一共有4个叫做f的函数&#xff0c;它们都是候选函数。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;2&#xff09;可行函数</p>
<p style="margin-left:.0001pt;text-align:justify;">接下来需要从候选函数中&#xff0c;选出跟传入的实参匹配的函数。这些函数叫做“可行函数”。可行函数也有两个要求&#xff1a;</p>
<ol><li>形参个数与调用传入的实参数量相等</li><li>每个实参的类型与对应形参的类型相同&#xff0c;或者可以转换成形参的类型</li></ol>
<p style="margin-left:.0001pt;text-align:justify;">上面的例子中&#xff0c;传入的实参只有一个&#xff0c;是一个double类型的字面值常量&#xff0c;所以可以排除 f() 和 f(int, int) 。而剩下的 f(int) 和 f(double, double &#61; 1.5) 都是匹配的&#xff0c;所以有2个可行函数。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;3&#xff09;寻找最佳匹配</p>
<p style="margin-left:.0001pt;text-align:justify;">最后就是在可行函数中&#xff0c;选择最佳匹配。简单来说&#xff0c;实参类型与形参类型越接近&#xff0c;它们就匹配得越好。所以&#xff0c;能不进行转换就实际匹配的&#xff0c;要优于需要转换的。</p>
<p style="margin-left:.0001pt;text-align:justify;">上面的例子中&#xff0c;f(int) 必须要将double类型的实参转换成int&#xff0c;而f(double, double &#61; 1.5) 不需要&#xff0c;所以后者是最佳匹配&#xff0c;最终调用的就是它。第二个参数会由默认实参1.5来填补。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;4&#xff09;多参数的函数匹配</p>
<p style="margin-left:.0001pt;text-align:justify;">如果实参的数量不止一个&#xff0c;那么就需要逐个比较每个参数&#xff1b;同样&#xff0c;类型能够精确匹配的要优于需要转换的。这时寻找最佳匹配的原则如下&#xff1a;</p>
<ol><li>如果可行函数的所有形参都能精确匹配实参&#xff0c;那么它就是最佳匹配</li><li>如果没有全部精确匹配&#xff0c;那么当一个可行函数所有参数的匹配&#xff0c;都不比别的可行函数差、并且至少有一个参数要更优&#xff0c;那它就是最佳匹配</li></ol>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">&#xff08;5&#xff09;二义性调用</p>
<p style="margin-left:.0001pt;text-align:justify;">如果检查所有实参之后&#xff0c;有多个可行函数不分优劣、无法找到一个最佳匹配&#xff0c;那么编译器会报错&#xff0c;这被称为“二义性调用”。例如&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">f(10, 3.14);      // 二义性调用</p>
<p style="margin-left:.0001pt;text-align:justify;">这时的可行函数为 f(int, int) 和 f(double, double &#61; 1.5)。第一个实参为int类型&#xff0c;f(int, int)占优&#xff1b;而第二个实参为double类型&#xff0c;f(double, double &#61; 1.5)占优。这时两个可行函数分不出胜负&#xff0c;于是就会报二义性调用错误。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.3.4 重载与作用域</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">重载是否生效&#xff0c;跟作用域是有关系的。如果在内层、外层作用域分别声明了同名的函数&#xff0c;那么内层作用域中的函数会覆盖外层的同名实体&#xff0c;让它隐藏起来。</p>
<p style="margin-left:.0001pt;text-align:justify;">不同的作用域中&#xff0c;是无法重载函数名的。</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">void print(double d)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;d: &#34; &lt;&lt; d &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">void print(string s)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;s: &#34; &lt;&lt; s &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">// 调用之前做函数声明</p>
<p style="margin-left:.0001pt;text-align:left;">void print(int i);</p>
<p style="margin-left:.0001pt;text-align:left;">print(10);</p>
<p style="margin-left:.0001pt;text-align:left;">print(3.14);           // 将3.14转换为3&#xff0c;然后调用</p>
<p style="margin-left:.0001pt;text-align:left;">//print(&#34;hello&#34;);        // 错误&#xff0c;找不到对应参数的函数定义</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">void print(int i)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;i: &#34; &lt;&lt; i &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">如果想让函数正确地重载&#xff0c;应该把函数声明放到同一作用域下&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">#include&lt;iostream&gt;</p>
<p style="margin-left:.0001pt;text-align:left;">using namespace std;</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 作用域和重载测试</p>
<p style="margin-left:.0001pt;text-align:left;">void print(int i)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;i: &#34; &lt;&lt; i &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">void print(double d)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;d: &#34; &lt;&lt; d &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;">void print(string s)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; &#34;s: &#34; &lt;&lt; s &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:left;">}</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">int main()</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">print(10);</p>
<p style="margin-left:.0001pt;text-align:left;">print(3.14); </p>
<p style="margin-left:.0001pt;text-align:left;">print(&#34;hello&#34;); </p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">cin.get();</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<h4 style="text-align:justify;"><strong><strong><strong>8.5 函数指针</strong></strong></strong></h4>
<p style="margin-left:.0001pt;text-align:justify;">一类特殊的指针&#xff0c;指向的不是数据对象而是函数&#xff0c;这就是“函数指针”。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.5.1 声明函数指针</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">函数指针本质还是指针&#xff0c;它的类型和所指向的对象类型有关。现在指向的是函数&#xff0c;函数的类型是由它的返回类型和形参类型共同决定的&#xff0c;跟函数名、形参名都没有关系。</p>
<p style="margin-left:.0001pt;text-align:justify;">例如之前写过的函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">string stuInfo(string name &#61; &#34;&#34;, int age &#61; 18, double score &#61; 60) </p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">string info &#61; &#34;学生姓名&#xff1a;&#34; &#43; name &#43; &#34;\t年龄&#xff1a;&#34; &#43; </p>
<p style="margin-left:.0001pt;text-align:left;">to_string(age) &#43; &#34;\t平均成绩&#xff1a;&#34; &#43; to_string(score);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">return info;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">它的类型就是&#xff1a;string(string, int, double)。</p>
<p style="margin-left:.0001pt;text-align:justify;">如果要声明一个指向它的指针&#xff0c;只要把原先函数名的位置填上指针就可以了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">string (* fp) (string, int, double);    // 一个函数指针</p>
<p style="margin-left:.0001pt;text-align:justify;">这里要注意&#xff0c;指针两侧的括号必不可少。如果去掉括号&#xff0c;</p>
<p style="margin-left:.0001pt;text-align:justify;">string *fp(string, int, double);      // 这是一个函数&#xff0c;返回值为指向string的指针</p>
<p style="margin-left:.0001pt;text-align:justify;">这就变成了一个返回string *类型的函数。</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">更加复杂的例子也是一样&#xff0c;例如之前写过的比较字符串长度的函数&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">const string&amp; longerStr(const string&amp; str1, const string&amp; str2)</p>
<p style="margin-left:.0001pt;text-align:left;">{</p>
<p style="margin-left:.0001pt;text-align:left;">return str1.size() &gt; str2.size() ? str1 : str2;</p>
<p style="margin-left:.0001pt;text-align:justify;">}</p>
<p style="margin-left:.0001pt;text-align:justify;">对应类型的函数指针就是&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">const string &amp;(*fp) (const string &amp;, const string &amp;);</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.5.2 使用函数指针</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">当一个函数名后面跟调用操作符&#xff08;小括号&#xff09;&#xff0c;表示函数调用&#xff1b;而单独使用函数名作为一个值时&#xff0c;函数会自动转换成指针。这一点跟数组名类似。</p>
<p style="margin-left:.0001pt;text-align:justify;">所以我们可以直接使用函数名给函数指针赋值&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">fp &#61; longerStr;        // 直接将函数名作为指针赋给fp</p>
<p style="margin-left:.0001pt;text-align:left;">fp &#61; &amp;longerStr;      // 取地址符是可选的&#xff0c;和上面没有区别</p>
<p style="margin-left:.0001pt;text-align:justify;">也可以加上取地址符&amp;&#xff0c;这和不加&amp;是等价的。</p>
<p style="margin-left:.0001pt;text-align:justify;">赋值之后&#xff0c;就可以通过fp调用函数了。fp做解引用可以得到函数&#xff0c;而这里解引用符*也是可选的&#xff0c;不做解引用同样可以直接表示函数。</p>
<p style="margin-left:.0001pt;text-align:left;">cout &lt;&lt; fp(&#34;hello&#34;, &#34;world&#34;) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">cout &lt;&lt; (*fp)(&#34;C&#43;&#43;&#34;, &#34;is good&#34;) &lt;&lt; endl;</p>
<p style="margin-left:.0001pt;text-align:justify;">所以这里能够看出&#xff0c;函数指针完全可以当做函数来使用。</p>
<p style="margin-left:.0001pt;text-align:justify;">在对函数指针赋值时&#xff0c;函数的类型必须精确匹配。当然&#xff0c;函数指针也可以赋nullptr&#xff0c;表示空指针&#xff0c;没有指向任何一个函数。</p>
<h5 style="text-align:justify;"><strong><strong><strong>8.5.3 函数指针作为形参</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">有了指向函数的指针&#xff0c;就给函数带来了更加丰富灵活的用法。比如&#xff0c;可以将函数指针作为形参&#xff0c;定义在另一个函数中。也就是说&#xff0c;可以定义一个函数&#xff0c;它以另一个函数类型作为形参。当然&#xff0c;函数本身不能作为形参&#xff0c;不过函数指针完美地填补了这个空缺。这一点上&#xff0c;函数跟数组非常类似。</p>
<p style="margin-left:.0001pt;text-align:left;">void selectStr(const string&amp; s1, const string&amp; s2, const string &amp; fp(const string&amp;, const string&amp;));</p>
<p style="margin-left:.0001pt;text-align:justify;">void selectStr(const string&amp; s1, const string&amp; s2, const string &amp; (*fp) (const string&amp;, const string&amp;));</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">同样&#xff0c;上面两种形式是等价的&#xff0c;*是可选的。</p>
<p style="margin-left:.0001pt;text-align:justify;">很明显&#xff0c;对于函数类型和函数指针类型来说&#xff0c;这样的定义太过复杂&#xff0c;所以有必要使用typedef做一个类型别名的声明。</p>
<p style="margin-left:.0001pt;text-align:left;">// 类型别名</p>
<p style="margin-left:.0001pt;text-align:left;">typedef const string&amp; Func(const string&amp;, const string&amp;);    // 函数类型</p>
<p style="margin-left:.0001pt;text-align:left;">typedef const string&amp; (*FuncP)(const string&amp;, const string&amp;);    // 函数指针类型</p>
<p style="margin-left:.0001pt;text-align:justify;">当然&#xff0c;还可以用C&#43;&#43; 11提供的decltype函数直接获取类型&#xff0c;更加简洁&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:left;">typedef decltype(longerStr) Func2;</p>
<p style="margin-left:.0001pt;text-align:justify;">typedef decltype(longerStr) *FuncP2;</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<p style="margin-left:.0001pt;text-align:justify;">这样一来&#xff0c;声明函数指针做形参的新函数&#xff0c;就非常方便了&#xff1a;</p>
<p style="margin-left:.0001pt;text-align:justify;">void selectStr(const string&amp;, const string&amp;, Func);</p>
<p style="margin-left:.0001pt;text-align:justify;"></p>
<h5 style="text-align:justify;"><strong><strong><strong>8.5.4 函数指针作为返回值</strong></strong></strong></h5>
<p style="margin-left:.0001pt;text-align:justify;">类似地&#xff0c;函数不能直接返回另一个函数&#xff0c;但是可以返回函数指针。所以可以将函数指针作为另一个函数的返回值。</p>
<p style="margin-left:.0001pt;text-align:justify;">这里需要注意的是&#xff0c;这种场景下&#xff0c;函数的返回类型必须是函数指针&#xff0c;而不能是函数类型。</p>
<p style="margin-left:.0001pt;text-align:left;">// 函数指针作为返回值</p>
<p style="margin-left:.0001pt;text-align:left;">FuncP fun(int);</p>
<p style="margin-left:.0001pt;text-align:left;">//Func fun2(int);      // 错误&#xff0c;不能直接返回函数</p>
<p style="margin-left:.0001pt;text-align:left;">Func* fun2(int);</p>
<p style="margin-left:.0001pt;text-align:left;"></p>
<p style="margin-left:.0001pt;text-align:left;">// 尾置返回类型</p>
<p style="margin-left:.0001pt;text-align:justify;">auto fun3(int) -&gt; FuncP; </p>
<p style="margin-left:.0001pt;text-align:justify;">另外也可以使用尾置返回类型的方式&#xff0c;指定返回函数指针类型。</p>
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4008
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行