C++ Primer 条件语句

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 5.3 条件语句
    • if语句
    • 使用if else语句
    • 嵌套if语句
    • 注意使用花括号
    • 悬垂else
    • 使用花括号控制执行路径
    • switch语句
    • switch内部的控制流
    • 漏写break容易引发缺陷
    • switch内部的变量定义

5.3 条件语句

C++语言提供了两种按条件执行的语句。一种是if语句,它根据条件决定控制流;另外一种是switch语句,它计算一个整型表达式的值,然后根据这个值从几条执行路径中选择一条。

if语句

if语句(if statement〉的作用是:判断一个指定的条件是否为真,根据判断结果决定是否执行另外一条语句。if语句包括两种形式:一种含有else分支,另外一种没有。简单if 语句的语法形式是

if(condiion)statement
// if else 语句的形式是
if(condition)statement
elsestalement2

在这两个版本的if语句中,condition都必须用圆括号包围起来。condition可以是一个表达式,也可以是一个初始化了的变量声明。不管是表达式还是变量,其类型都必须能转换成布尔类型。通常情况下,statemet 和 statemet2 是块语句。

如果condition为真,跳过statement。对于简单if语句来说,程序继续执行if语句后面的其他语句;对于if else语句来说,执行statement。

使用if else语句

我们举个例子来说明if语句的功能,程序的目的是把数字形式表示的成绩转换成宇母形式。假设数字成绩的范围是从0到100(包括100在内),其中100分对应的字母形式是“A++“,低于60分的成绩对应的字母形式是“F“。其他成绩每10个划分成一组;60到69(包括69在内)对应字母“D“70到79对应字母“C“,以此类推。使用vector对象存放字母成绩所有可能的取值:

const vector<string> scores = ("F","D","C","B","A++")我们使用if else语句解决该问题,根据成绩是否合格执行不同的操作:
//如果grade小于60,对应的字母是F;否则计算其下标```cpp
string lettergrade;
if(grade < 60)lettergrade=scores[0];
elselettergrade = scores[(grade-50)/10];

判断grade的值是否小于60,根据结果选择执行if分支还是else分支。在else分支中,由成绩计算得到一个下标,具体过程是:首先从grade中减去50,然后执行整数除法,去掉余数后所得的商就是数组scores对应的下标。

嵌套if语句

接下来让我们的程序更有趣点儿,试着给那些合格的成绩后面添加一个加号或减号。如果成绩的末位是8或者9,添加一个加号;如果末位是0、1或2,添加一个减号:

if(grade % 10>7)lettergrade+='+';//结尾是8或者9的成绩添加一个加号
else if(grade%10<3)lettergrade+='-';//末尾是0、1或者2的成绩添加一个减号

我们使用取模运算符计算余数,根据余数决定添加哪种符号。接着把这段添加符号的代码整合到转换成绩形式的代码中去:

//如果成绩不合格,不需要考虑添加加号减号的问题
if(grade<60)lettergrade=scores[0];
elselettergrade=scores[(grade-50)/10];//获得字母形式的成绩if(grade!=100)//只要不是A++,就考虑添加加号或减号if(grade%10>7)lettergrade +='+';//未尾是8或者9的成绩添加一个加号
else if(grade%10<3)lettergrade += '-';//末尾是0、1或者2的成绩添加一个减号

注意,我们使用花括号把第一个else后面的两条语句组合成了一个块。如果grade不小于60要做两件事:从数组scores中获取对应的字母成绩,然后根据条件设置加号或减号。

注意使用花括号

有一种常见的错误:本来程序中有几条语句应该作为一个块来执行,但是我们忘了用花括号把这些语句包围。在下面的例子中,添加加号减号的代码将被无条件地执行,这显然违背了我们的初衷:

if(grade<60)lettergrade=scores[0];
else//错误:缺少花括号lettergrade=scores[(grade-50)/10];//虽然下面的语句从形式上看有缩进,但是图为没有花括号,//所以无论什么情况都会执行接下来的代码//不及格的成绩也会添加上加号或减号,这显然是错误的
if(grade!=100)if(grade%10>7)lettergrade+='+';//末尾是8或者9的成绩添加一个加号else if(grade%10<3)lettergrade+='-';//未尾是0、1或者2的成绩添加一个减号

要想发现这个错误可能非常困难,毕竟这段代码"看起来"是正确的。为了避免此类问题,有些编码风格要求在if或else之后必须写上花括号(对while和for语句的循环体两端也有同样的要求)。这么做的好处是可以避免代码混乱不清,以后修改代码时如果想添加别的语句,也可以很容易地找到正确位置。许多编辑器和开发环境都提供一种辅助工具,它可以自动地缩进代码以匹配其语法结构。

悬垂else

当一个if语句嵌套在另一个if语句内部时,很可能if分支会多于else分支。事实上,之前那个成绩转换的程序就有4个if分支,而只有2个else分支。这时候问题出现了:我们怎么知道某个给定的else是和哪个if匹配呢?

这个问题通常称作悬垂else(dangling else),在那些既有if语句又有if else语句的编程语言中是个普道存在的问题。不同语言解决该问题的思路也不同,就C++而言,它规定else与离它最近的尚未匹配的if匹配,从而消除了程序的二义性。

当代码中if分支多于else分支时,程序员有时会感觉比较麻烦。举个例子来说明,对于添加加号减号的那个最内层的if else语句,我们用另外一组条件改写它:

//错误:实际的执行过程并非像缩进格式显示的那样;else分支匹配的是内层if语句
if(grade%10>=3)if(grade%10>7)lettergrade+='+';//未尾是8或者9的成绩添加一个加号
elselettergrade+='-';//末尾是3、4、5、6或者7的成绩添加一个减号!

从代码的缩进格式来看,程序的初衷应该是希望else和外层的if匹配,也就是说,我们希望当grade的未位小于3时执行else分支。然而,不管我们是什么意图,也不管程序如何缩进,这里的else分支其实是内层if语句的一部分。最终,上面的代码将在末位大于3小于等于7的成绩后面添加减号!它的执行过程实际上等价于如下形式:

//缩进格式与执行过程相符,但不是程序员的意图
if(grade%10>=3)
i(grade%10>7)lettergrade+="+";//末尾是8或者9的成绩添加一个加号
elselettergrade+="-";//末尾是3、4、5、6或者7的成绩添加一个减号!

使用花括号控制执行路径

要想使else分支和外层的if语句匹配起来,可以在内层if语句的两端加上花括号,其成为一个块:

//末尾是8或者9的成绩添加一个加号,末尾是0、1或者2的成绩添加一个减号
if(grade%10>=3){
if(grade%10>7)lettergrade+="+";//末尾是8或者9的成绩添加一个加号
}else//花括号强迫else与外层if匹配lettergrade+="-";//末尾是0、1或者2的成绩添加一个减号

语句属于块,意味着语句一定在块的边界之内,因此内层if语句在关键字else前面的那个花括号处已经结束了。else不会再作为内层if的一部分。此时,最近的尚未匹配的if是外层if,也就是我们希望else匹配的那个。

switch语句

switch语句(switch statement)提供了一条便利的途径使得我们能够在若干固定选项中做出选择。举个例子,假如我们想统计五个元音字母在文本中出现的次数,程序逻辑应该如下所示:

  • 从输入的内容中读取所有字符。
  • 令每一个字符都与元音字母的集合比较。
  • 如果字符与某个元音字母匹配,将该字母的数量加1。
  • 显示结果。

例如,本章的文本作为输入内容,程序的输出结果将是:

Number of vowel a:3195
Number of vowel e:6230
Number of vowel i:3102
Number of vowel o:3289
Number of vowel u:1033

要想实现这项功能,直接使用switch语句即可:

//为每个元音字母初始化其计数值
unsigned aCnt=0,eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
while(cin>>ch){
//如果ch是元音守母,将其对应的计数值加1switch(ch){case 'a':++aCnt;break;case 'e':++eCnt;break;case 'i':++iCnt;break;case 'o':++oCnt;break;case 'u':++uCnt;break;
//输出结果cout<< "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << '\n'
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel t: \t" << oCnt << '\n'
<< "Number of vowel t: \t" << uCnt << '\n'

switch语句首先对括号里的表达式求值,该表达式紧跟在关键字switch的后面,可以是一个初始化的变量声明。表达式的值转换成整数类型,然后与每个case标签的值比较。

如果表达式和某个case标签的值匹配成功,程序从该标签之后的第一条语句开始执行,直到到达了switch的结尾或者是遇到一条break语句为止。简言之,break语句的作用是中断当前的控制流。此例中,break语句将控制权转移到switch语句外面.因为switch是while循环体内唯一的语句,所以从switch语句中断出来以后,程序的控制权将移到while语句的右花括号处。此时while语句内部没有其他语句要执行,所以while会返回去再一次判断条件是否满足。

如果switch语句的表达式和所有case都没有匹配上,将直接跳转到switch结构之后的第一条语句。刚刚说过,在上面的例子中,退出switch后控制权回到while语句的条件部分。

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

char ch=getVal();
int ival=42;
switch(ch){case 3.14://错误:case标签不是一个整数case ival://错误:case标签不是一个常量
}

任何两个case标签的值不能相同,否则就会引发错误。另外,default也是一种特殊的case标签。

switch内部的控制流

理解程序在case标签之间的执行流程非常重要。如果某个case标签匹配成功,将从该标签开始往后顺序执行所有case分支,除非程序显式地中断了这一过程,否则直到switch的结尾处才会停下来。要想避免执行后续case分支的代码,我们必须显式地告诉编译器终止执行过程。大多数情况下,在下一个case标签之前应该有一条break语句。

然而,也有一些时候默认的switch行为才是程序真正需要的。每个case标签只能对应一个值,但是有时候我们希望两个或更多个值共享同一组操作。此时,我们就故意省略掉break语句,使得程序能够连续执行若干个case标签。

例如,也许我们想统计的是所有元音字母出现的总次数:

unsigned vowelCnt=0;
switch(ch)
{//出现了a、e、i、o或u 中的任意一个都会将vowelCnt的值加1case 'a':case 'e':case 'i':case 'o':case 'u':++vowelCnt;break;
}

在上面的代码中,几个case标签连写在一起,中间没有break语句。因此只要ch是元音字母,不管到底是五个中的哪一个都执行相同的代码。C++程序的形式比较自由,所以case标签之后不一定非得换行。把几个case标签写在一行里,强调这些case代表的是某个范围内的值:

switch(ch){//另一种合法的书写形式case 'a': case 'e': case 'i':case 'o': case 'u':++vowelCnt;break;
}

一般不要省略case分支最后的break语句,如果没有写break语句,最好加一段注释说清楚程序的逻辑。

漏写break容易引发缺陷

有一种常见的错觉是程序只执行匹配成功的那个case分支的语句。例如,下面程序的统计结果是错误的:

//警告:不正确的程序逻辑!
switch(ch){
casSe 'a':++aCnt;//此处应该有一条break语句
case 'e':++eCnt;//此处应该有一条break语句
case 'i':++iCnt;//此处应该有一条break语句
case 'o':++oCnt;//此处应该有一条break语句
case 'u':++uCnt;
}

要想理解这段程序的执行过程,不妨假设ch的值是’e’。此时,程序直接执行case 'e’标签后面的代码,该代码把eCnt的值加1。接下来,程序将跨越case标签的边界,接着递增iCnt、oCnt和uCnt。

default标签

如果没有任何一个case标签能匹配上switch表达式的值,程序将执行紧跟在default标签(default label)后面的语句。例如,可以增加一个计数值来统计非元音字母的数量,只要在default分支内不断递增名为otherCnt的变量就可以了:

//如果ch是一个元音字母,将相应的计数值加1
switch(ch){
case 'a':case 'e':case 'i':case 'u':++vawelCnt;break;
default:++otherCnt;break;
}

在这个版本的程序中,如果ch不是元音字母,就从default标签开始执行并把 otherCnt加1。

即使不准备在default标签下做任何工作,定义一个default标签也是有用的。其目的是告诉读者,我们已经考虑到默认情况,只是目前什么也没有做。

标签不应该孤零零地出现,它后面必须跟上一条语句或者另外一个case标签。如果switch结构以一个空的default标签作为结束,则该default标签后面必须跟上一条语句或一个空块。

switch内部的变量定义

如前所述,switch的执行流程有可能会跨过某些case标签。如果程序跳转到了某个特定的case,则switch结构中该case标签之前的部分会被忽略掉。这种忽略掉一部分代码的行为引出了一个有趣的问题:如果被略过的代码中含有变量的定义该怎么办?

答案是:如果在某处一个带有初值的变量位于作用域之外,在另一处该变量位于作用域之内,则从前一处跳转到后一处的行为是非法行为。

case true:
//因为程序的执行流程可能绕开下面的初始化语句,所以该switch语句不合法
string file_name;//错误:控制流绕过一个隐式初始化的变量
int val=0;//错误:控制流绕过一个显式初始化的变量
int jval;//正确:因为jval没有初始化
break;
case false:
//正确:jval虽然在作用域内,但是它没有被初始化
jval=next_num()}//正确:给jgal赋一个值
if(filename.empty())//file_name在作用域内,但是没有被初始化
//...

假设上述代码合法,则一旦控制流直接跳到false分支,也就同时略过了变量fil1e_name和ival的初始化过程。此时这两个变量位于作用域之内,跟在false之后的代码试图在尚未初始化的情况下使用它们,这显然是行不通的。因此C++语言规定,不允许跨过变量的初始化语句直接跳转到该变量作用域内的另一个位置。

如果需要为某个case分支定义并初始化一个变量,我们应该把变量定义在块内,从而确保后面的所有case标签都在变量的作用域之外。

case true: {//正确:声明语句位于语句块内部string file_name=get_file_name();}break;
case false:if(file_name.empty())// 错误:fine_name 不在作用域之内

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务SpringCloud Alibaba组件nacos教程【详解naocs基础使用、服务中心配置、集群配置,附有案例+示例代码】

一.Nacos教程 文章目录 一.Nacos教程1.1 Nacos简介1.2 nacos基本使用直接下载打包服务源码方式启动 1.3 创建nacos客服端1.4 nacos集群配置1.5 nacos配置中心 1.1 Nacos简介 nacos是spring cloud alibaba生态中非常重要的一个组件&#xff0c;它有两个作用&#xff1a; 1:注册…

在 Navicat 17 中扩展 PostgreSQL 数据类型 - 范围类型

范围类型 PostgreSQL 是市场上最灵活的数据库之一&#xff0c;这已不是什么秘密。事实上&#xff0c;PostgreSQL 的可扩展性和丰富的功能集使 PostgreSQL 近期已超越 MySQL&#xff0c;成为最受开发人员推崇和最受欢迎的数据库系统。在这个使用 Navicat Premium 17 在 Postgre…

内容测试2

备注&#xff1a; 在7月10日记录之前遇到的问题及解决方法: 一&#xff1a;常见的访问问题&#xff1a; 403 Forbidden&#xff1a;&#xff08;未有请求权限&#xff09; 表示服务器理解请求但是拒绝执行它。这通常是由于服务器上的文件或资源没有正确的读、写或执行权限&…

安川伺服控制器MP系列优势特点及行业应用

在工业自动化领域&#xff0c;运动控制器的性能直接决定了设备的精度、效率和可靠性。作为全球领先的运动控制品牌&#xff0c;安川电机伺服控制器凭借其卓越的技术优势和广泛的应用场景&#xff0c;正在为智能制造注入强劲动力&#xff01; MP3100&#xff1a;主板型运动控制…

kafka生产端之架构及工作原理

文章目录 整体架构元数据更新 整体架构 消息在真正发往Kafka之前&#xff0c;有可能需要经历拦截器&#xff08;Interceptor&#xff09;、序列化器&#xff08;Serializer&#xff09;和分区器&#xff08;Partitioner&#xff09;等一系列的作用&#xff0c;那么在此之后又会…

二、交换机的vlan子设备接入

一、交换机的vlan设置-CSDN博客 二、交换机的vlan子设备接入-CSDN博客 接上篇的文章&#xff0c;本文接入了子设备 网络结构如下&#xff1a; 用路由器A和POE交换机B代替第一篇中的笔记本电脑&#xff0c;路由器A和交换机B都关闭DHCP服务&#xff0c;并分别接入一个IPC&#…

DedeBIZ系统审计小结

之前简单审计过DedeBIZ系统&#xff0c;网上还没有对这个系统的漏洞有过详尽的分析&#xff0c;于是重新审计并总结文章&#xff0c;记录下自己审计的过程。 https://github.com/DedeBIZ/DedeV6/archive/refs/tags/6.2.10.zip &#x1f4cc;DedeBIZ 系统并非基于 MVC 框架&…

C语言基本概念————讨论sqrt()和pow()函数与整数的关系

本文来源&#xff1a;C语言基本概念——讨论sqrt()和pow()函数与整数的关系. C语言基本概念——sqrt和pow函数与整数的关系 1. 使用sqrt()是否可以得到完全平方数的精确的整数平方根1.1 完全平方数的计算结果是否精确&#xff1f;1.2 为什么不会出现误差&#xff08;如 1.99999…

浏览器自动化与AI Agent结合项目browser-use初探

browser-use介绍 browser-use是将您的 AI 代理连接到浏览器的最简单方式。它通过提供一个强大且简单的接口来实现 AI 代理访问网站的自动化。 GitHub地址&#xff1a;https://github.com/browser-use/browser-use。目前已经获得了27.3k颗stars&#xff0c;2.7kforks&#xff…

阿里云 DeepSeek 模型部署与使用技术评测

引言 随着人工智能技术的不断发展&#xff0c;越来越多的企业和个人开始探索如何利用深度学习模型来提升业务效率和用户体验。阿里云推出的【零门槛、轻松部署您的专属 DeepSeek 模型】解决方案为用户提供了多种便捷的部署方式&#xff0c;包括基于百炼 API 调用满血版、基于人…

第二天:工具的使用

每天上午9点左右更新一到两篇文章到专栏《Python爬虫训练营》中&#xff0c;对于爬虫有兴趣的伙伴可以订阅专栏一起学习&#xff0c;完全免费。 键盘为桨&#xff0c;代码作帆。这趟为期30天左右的Python爬虫特训即将启航&#xff0c;每日解锁新海域&#xff1a;从Requests库的…

vue项目 Axios创建拦截器

Axios 1. Axios 和 Ajax 简介2. Axios 和 Ajax 的区别3. 从 按钮 到 Axios请求后端接口的 大致顺序 1. Axios 和 Ajax 简介 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09; 不是一种技术&#xff0c;而是一个编程技术概念&#xff0c;核心是通过 XMLHttpReques…

windows + visual studio 2019 使用cmake 编译构建静、动态库并调用详解

环境 windows visual studio 2019 visual studio 2019创建cmake工程 1. 静态库.lib 1.1 静态库编译生成 以下是我创建的cmake工程文件结构&#xff0c;只关注高亮文件夹部分 libout 存放编译生成的.lib文件libsrc 存放编译用的源代码和头文件CMakeLists.txt 此次编译CMak…

通过 VBA 在 Excel 中自动提取拼音首字母

在excel里面把表格里的中文提取拼音大写缩写怎么弄 在Excel中&#xff0c;如果你想提取表格中的中文字符并转换为拼音大写缩写&#xff08;即每个汉字的拼音首字母的大写形式&#xff09;&#xff0c;可以通过以下步骤来实现。这项工作可以分为两个主要部分&#xff1a; 提取拼…

通过环境变量实现多个 python 版本的自由切换以及 Conda 虚拟环境的使用教程

目录 Python 安装包的下载和安装通过环境变量的方式来切换不同的 Python 版本Pycharm 创建项目使用虚拟环境 使用虚拟环境管理工具 condaConda 教程1. **环境管理**创建虚拟环境激活虚拟环境退出虚拟环境列出所有虚拟环境删除虚拟环境导出虚拟环境配置从文件创建虚拟环境 2. **…

排序算法详解、应用对比与C语言实现

四种经典排序算法详解&#xff08;原理动图代码&#xff09; 一、排序算法的重要性 排序算法是计算机科学领域最基础的算法之一&#xff0c;在数据库索引、搜索引擎优化、大数据分析等领域有广泛应用。根据Stack Overflow 2022开发者调查&#xff0c;超过83%的面试会考察算法…

Python基于Django的微博热搜、微博舆论可视化系统(V3.0)【附源码】

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

网络安全ids是什么意思

1、 简述IPS和IDS的异同点&#xff1b; 入侵检测系统&#xff08;IDS&#xff09; IDS&#xff08;Intrusion Detection Systems&#xff0c;入侵检测系统&#xff09;&#xff0c;专业上讲就是依照一定的安全策略&#xff0c;对网络、系统、运行状况进行监视&#xff0c;尽可能…

JVM春招快速学习指南

1.说在前面 在Java相关岗位的春/秋招面试过程中&#xff0c;JVM的学习是必不可少的。本文主要是通过《深入理解Java虚拟机》第三版来介绍JVM的学习路线和方法&#xff0c;并对没有过JVM基础的给出阅读和学习建议&#xff0c;尽可能更加快速高效的进行JVM的学习与秋招面试的备战…

json格式,curl命令,及轻量化处理工具

一. JSON格式 JSON&#xff08;JavaScript Object Notation&#xff09; 是一种轻量级的数据交换格式。它基于一个子集的JavaScript编程语言&#xff0c;使用人类易于阅读的文本格式来存储和表示数据。尽管名字中有“JavaScript”&#xff0c;但JSON是语言无关的&#xff0c;几…