C++ 控制流


控制流

为什么会有控制流这玩意呢?那是因为程序不总是按顺序执行。这个很好理解,生活中也随处可见。例如:如果今天不下雨,那我就去打篮球,否则,宅在宿舍打LOL。这个就可以对应if else这种控制流结构了。控制流在大多数的语言中都是有的,而且也基本上都是 while for if这三剑客或者变种。

程序设计语言一般都会提供多种不同的控制流语句,允许我们执行更为复杂的执行路径。


while语句

while 语句 反复执行一段代码,直至给定的条件为假(false)为止。while语句的形式为:

while (condition)statement

让我们用while语句来完成一个小任务:求整数1到100的和。 知道大数学家高斯的人,想必都知道这个经典问题的出处。直接上代码:

#include <iostream>using namespace std;int main()
{int sum = 0;          // sum用于存储和int x = 1;            // 当前要加的值while (x <= 100)    // 判断当前值是否小于等于100{sum = sum + x;    // 和加上当前要加的值x = x + 1;        // 当前值加1,往后递推。}cout << "整数1到100的和为" << sum << endl;return 0;
}

注释比较详尽,就不一一解释了。使用while语句特别要注意的是条件以及条件的改变,不然容易造成死循环,使程序崩溃。例如上面的程序,粗心的人忘了写 x = x + 1 这一句,那么程序就会陷入死循环。

最经典的Windows消息机制也是用的while语句,像这样:

// 消息循环过程
MSG msg = {0};
while (msg.message != WM_QUIT)        // 如果没有退出消息
{if(PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)){TranslateMessage(&msg);        // 将虚拟按键装化为字符DispatchMessage(&msg);        // 分发消息给程序窗口}
}

还有一些输入不确定数量的数,也可以使用while语句解决。

问题:输入若干个整数,求其和。

#include <iostream>using namespace std;int main()
{int sum = 0;        // sum用于存储和int x = 0;            // 当前要输入的值int num = 0;        // 记录一共输入了几个数while (cin >> x)    // 读取数,一直遇到结束符为止{num = num + 1;sum = sum + x;    // 和加上当前要加的值}cout << "你一个输入了" << num << "个数," << "这些数的和为:" << sum << endl;return 0;
}

在Windows系统下,结束输入的组合键是 Ctrl + Z,然后回车。 Linux系统下是 Ctrl + D。 Max OS系统下是 Ctrl + D

另外,还有个和while有关的是do while结构的语句。这种结构会至少执行一次循环体,然后检查while中的条件是否成立。看示例:

#include <iostream>using namespace std;int main()
{int count = 0;        // 记录一共执行了几次循环体do{count = count + 1;// 一段你想执行的代码} while (false);cout << "语句块执行的次数为:" << count << endl;return 0;
}

输出结果为1,也就是说,这种结构至少执行一次 do 语句块中的代码。


for 语句

像while语句这样在循环条件中检测变量,在循环体中改变变量的模式非常频繁。于是一个专门的语句出现了,它就是 for 语句。 for语句的形式为:

for(init-statement; condition; expression)statement

每个for语句都包含两部分:循环头和循环体。循环头控制循环体的执行次数,它由三个部分组成:一个初始化语句(init-statement),一个循环条件(condition)以及一个表达式(expression)

for循环和while循环是可以相互转换的。
看之前的一个小任务:求整数1到100的和。 使用for语句完成如下:

#include <iostream>using namespace std;int main()
{int sum = 0;        // sum用于存储和for (int x = 1; x <= 100; ++x){sum += x;        // 和加上当前要加的值}cout << "整数1到100的和为" << sum << endl;return 0;
}

简述一下for循环的总体执行流程

  1. 创建变量x,将其初始化为1
  2. 检测变量x是否小于等于100,若检测成功,执行for循环的循环体。若失败,退出循环。继续执行循环体之后的代码。
  3. 将x的值增加1
  4. 重复第2步中的检测条件,只要条件为真,就继续执行剩余步骤。

需要注意的是,for语句的初始化语句、循环条件、表达式都是可以省略的。例如for (; ;){},相当于while(true){}。 所以说,for语句使用不当,也会造成死循环。

范围for语句

C++ 11新标准引入的一种更简单的for语句。这种语句可以遍历容器或者其他序列的所有元素。

范围for语句(range for statement) 的语法形式如下:

for (declaration : expression)statement

declaration 定义了一个变量,序列中的每个元素都是能转换成该变量的类型。

expression 必须是一个序列。 例如可以是 数组、vector或者string等对象。

看代码:

#include <iostream>
#include <vector>using namespace std;int main()
{vector<int> v = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };// 范围for语句。如果想要改写范围变量,则需要把它写成引用形式。for (auto& val : v){val = val * val;}// 普通的for语句for (vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter){cout << *iter << " ";}return 0;
}

输出结果为:81 64 49 36 25 16 9 4 1 0

可见容器内的值已经被改变。


if 语句

if语句也就是条件语句。大部分语言都支持了if语句。if语句的形式为:

if (condition)statement

另一种if语句带else分支,形式为:

if (condition)statement
elsestatement2

如果condition为真,则执行statement语句,否则执行statement2语句。

另外还有就是嵌套的if语句,像这样:

if (condition)
{// other codeif (condition1){// other code}else{// other code}
}
else
{// other code
}

建议大家像我一样,使用方括号和缩进,来控制执行路径,解决垂悬else问题,也使得代码更加可读。

看一个《C++ Prime》上的一个例子:统计输入中的每个值连续出现的次数。

#include <iostream>using namespace std;int main()
{// currentVal 是我们正要统计的数,我们将新的值存入valint currentVal = 0, val = 0;// 读取第一个数,并确保有数据可以处理if (cin >> currentVal){int count = 1;                // 保存我们正在处理的当前值的个数while (cin >> val){if (val == currentVal)    // 如果值相同{++count;}else                    // 如果值不同,则打印出现的个数{cout << currentVal << "出现了" << count << "次" << endl;currentVal = val;    // 记住新出现的这个值count = 1;            // 重置计数器}}    // while循环到这里结束// 打印文件中最后一个值的个数cout << currentVal << "出现了" << count << "次" << endl;}        // 最外层if语句结束return 0;
}

最基本的流程控制实际就这三个,也是大多数语言所支持的。其他的一些控制流其实都可以用这三个来转换。下面介绍一下switch语句跳转语句,他们也是控制流程语句的一份子。


switch 语句

switch语句(switch statement) 提供了一条便利的途径使得我们在若干个固定选项中做出选择。switch语句形式如下:

switch (switch_on)
case x1:statement1break;
case x2:statement2break;
// other case
default:statementnbreak;

case关键字和它对应的值一起被称为 case标签(case label)。 case标签必须是整型常量表达式

下面用一段代码来说明此问题:

问题: 统计一段小写文本中元音字母出现次数,以及非元音字母出现的次数。

代码:

#include <iostream>using namespace std;int main()
{// 五个元音分别统计int aCount = 0, eCount = 0, iCount = 0, oCount = 0, uCount = 0;// 非元音统计int otherCount = 0;char ch;while (cin >> ch){switch (ch){case 'a':++aCount;break;case 'e':++eCount;break;case 'i':++iCount;break;case 'o': ++oCount;break;case 'u':++uCount;break;default:++otherCount;break;}}cout << "元音字母a出现的次数为:" << aCount << endl;cout << "元音字母e出现的次数为:" << eCount << endl;cout << "元音字母i出现的次数为:" << iCount << endl;cout << "元音字母o出现的次数为:" << oCount << endl;cout << "元音字母u出现的次数为:" << uCount << endl;cout << "非元音字母出现的次数为:" << otherCount << endl;return 0;
}

输入:acetanlovesnail

输出:

元音字母a出现的次数为:3

元音字母e出现的次数为:2

元音字母i出现的次数为:1

元音字母o出现的次数为:1

元音字母u出现的次数为:0

非元音字母出现的次数为:8

这里要注意每个分支下的break,它代表结束当前的控制流,break下文会讲到。新手常犯的错误就是漏写break,漏写break最容易导致逻辑错误,漏写break后,程序会继续执行下面的代码。例如:上面的程序

case 'i':++iCount;    // 漏写了break
case 'o': ++oCount;    // 漏写了break
case 'u':++uCount;

如果有一个元音字母i,那么iCount会加1,oCount也会加1,uCount也会加1。这一点千万要注意。

switch语句是可以转化为if语句的,这很容易看出来。


跳转语句

跳转语句是中断当前的执行过程。C++中提供了四种跳转语句:break, continue, goto, return.

break语句(break statement) 负责终止离它最近的while, do while, for 或者switch语句,并从这些语句之后的第一条语句开始执行。

break语句只能出现在迭代语句或者switch语句内部。例子可以参考上面switch的例子。

continue语句(continue statement) 终止离它最近的循环中的当前迭代并立即开始下一次迭代。

continue语句只能出现在for, while, do while循环的内部,或者嵌套在此类循环里的语句或者块的内部。

goto语句(goto statement) 的作用是无条件跳转到同一函数内的另一条语句。

不要在程序中使用goto语句,因为它使得程序既难理解又难修改。

goto语句是一个比较有意思的语句,虽然各种教材都说它很危险,不推荐使用。但它有时候也能起到奇效的作用。

goto语句的形式为:

goto label;

其中label是用于标示某条语句的标示符。 带标签语句(labeled statement) 是一种特殊语句,在它之前有一个标示符以及一个冒号。

end: return; // 带标签语句,可以作为goto的目标

goto语句一个优点就是它可以在很深的嵌套层次中直接跳到最外围,否则你只能break一层一层的跳。其实
Java也保留了这种用法,不过换了语法变成break label;

使用goto语法:

for (...)
{for (...) {for (...) {for (...) {for (...) {for (...) {if (...) goto:outside;}}}}}
}outside:

goto只能在函数体内跳转,不能跳到函数体外的函数。即goto有局部作用域,需要在同一个栈内。

return 语句(return statement) 终止当前正在执行的函数并将控制权返回到调用该函数的地方。
return 语句有两种形式:

return;return expression;

return 函数非常常见,每个函数都要使用它。其中,无返回值函数使用第一种形式。有返回值函数使用第二种形式。 需要注意的是:

在含有return语句的循环后也要有一条return语句,如果没有的话,那么该程序就是错误的。很多编译器都无法发现此类错误。

其实,比较新的编译器都会给出警告的。例如VS2015会给出这样的警告:

warning C4715: “”: 不是所有的控件路径都返回值

在Code::Blocks上会给出这样的警告:

warning: control reaches end of non-void function [-Wreturn-type]|

结束语

关于C++的控制流就介绍到这里,希望各位读者能熟练掌握和应用。

C++入门系列博客二 C++ 控制流相关推荐

  1. RecBole小白入门系列博客(二) ——General类模型运行流程

    RecBole小白入门系列博客(二) --General类模型运行流程 写在前面 选定模型 设置模型超参数 选定数据集 数据集基本格式 设置数据集参数 设置训练参数 设置评测参数 总结参数设置 运行 ...

  2. 点云深度学习系列博客(二): 点云配准网络PCRNet

    目录 一. 简介 二. 基础结构 三. 项目代码 四. 实验结果 总结 Reference 今天的点云深度学习系列博客为大家介绍一个用于点云配准的深度网络:PCRNet [1].凡是对点云相关应用有些 ...

  3. 2021-08-26 转载 Scala快速入门系列博客文章

    作者:Huidoo_Yang 出处:http://www.cnblogs.com/yangp/ 本文版权归作者Huidoo_Yang和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面 ...

  4. RPC框架原理及从零实现系列博客(二):11个类实现简单RPC框架

    项目1.0版本源码 https://github.com/wephone/Me... 在上一博文中 跟大家讲了RPC的实现思路 思路毕竟只是思路 那么这篇就带着源码给大家讲解下实现过程中的各个具体问题 ...

  5. 堪比培训机构的MySQL系列博客

    这个blog,我整理了我之前写的MySQL开发系列和MySQL运维系列,知识丰富程度堪比培训机构 个人能力有限,如有错误的地方,欢迎指正. 文章目录 一.MySQL开发系列 1.1 MySQL 行转列 ...

  6. Flutter 即学即用系列博客——09 MethodChannel 实现原生与 Flutter 通信(二)

    前言 上一篇我们讲解了如何通过 EventChannel 实现 Android -> Flutter 的通信. 并且也看到了 Flutter 内部 EventChannel 源码也是对 Meth ...

  7. Django 系列博客(二)

    Django 系列博客(二) 前言 今天博客的内容为使用 Django 完成第一个 Django 页面,并进行一些简单页面的搭建和转跳. 命令行搭建 Django 项目 创建纯净虚拟环境 在上一篇博客 ...

  8. 利用用户行为数据——基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二)

    系列文章目录 初识推荐系统--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(一) 利用用户行为数据--基于Spark平台的协同过滤实时电影推荐系统项目系列博客(二) 项目主要效果展示--基 ...

  9. SpringBoot和Vue集成Markdown和多级评论——基于SpringBoot和Vue的后台管理系统项目系列博客(二十三)

    系列文章目录 系统功能演示--基于SpringBoot和Vue的后台管理系统项目系列博客(一) Vue2安装并集成ElementUI--基于SpringBoot和Vue的后台管理系统项目系列博客(二) ...

  10. 网站框架搭建——基于Django框架的天天生鲜电商网站项目系列博客(二)

    系列文章目录 需求分析--基于Django框架的天天生鲜电商网站项目系列博客(一) 网站框架搭建--基于Django框架的天天生鲜电商网站项目系列博客(二) 用户注册模块--基于Django框架的天天 ...

最新文章

  1. editplus 3 注册码
  2. NTU 课程笔记: CV6422 regression
  3. mysql基础拓扑图
  4. 核心编程之十一章的11-9
  5. ext中的EXT.XTemplate()
  6. 第四周课程总结实验报告二
  7. svn 在windows下创建仓库子目录失败解决办法
  8. socket服务端处理多个客户端的请求学习理解
  9. 超慢速移动动画使用CSS3实现流畅效果
  10. FPGA开发设计流程
  11. sql 常见查询代码操作
  12. 磁盘加密软件(Secret Disk Pro 2020) v2020.03
  13. 滑动切换下一个视频,点击暂停视频,再次点击播放视频,很多案列pc预览正常,真机调试就不能用了;此案例我手机是可以用的,废话不多说直接上源码
  14. Python爬取百度文库的内容输出
  15. 网页视频怎么录制?这两个方法操作简单,录制高清!
  16. c# 从MySQL往sharpmap中加载矢量数据图层(一)
  17. Elasticsearch Kibana Filebeat开启SSL通信
  18. 操作系统——如何求磁盘的物理地址
  19. 关于DVDScr, Screener,TS, TC等常见术语
  20. 树莓派安装迅雷xware的失败经验

热门文章

  1. 用Sql Server 2000的数据库备份来还原Sql Server 2005中的数据库
  2. 借助CSS来管理js事件
  3. Go 标准库介绍五: io
  4. Android Studio配置文件修改
  5. 【云栖大会】创无止境 YunOS云栖大会诠释万物互联
  6. Monkey学习笔记三:Monkey脚本编写
  7. mabatis的工作原理
  8. 【Ubuntu^Java】Ubuntu下JDK环境变量的配置
  9. 如何在程序中安装指定apk文件
  10. Android 强升逻辑和实现