当前位置: 首页 > news >正文

3.1 C语言之控制流语句

3.1 C语言之控制流语句

  • 一、控制流概述
  • 二、if-else语句
  • 三、else-if语句
  • 四、switch语句
  • 五、while循环与for循环
  • 六、do-while循环
  • 七、break语句和continue语句
  • 八、goto

一、控制流概述

所谓控制流语句就是用来控制程序指令执行的次序,比如,如果条件满足执行语句A,否则,执行语句B。
在讲述具体的控制流语句之前,先了解一下语句和程序块是有必要的

  • 语句:在x=0、x++、printf(…)这样的表达式之后加上一个分号( ; ),它们就变成了语句,例如:
	x = 0;x++;printf("Hello");
     在C语言中,分号是语句结束符。
  • 程序块:使用一对花括号{}把一组声明和语句括起来就是程序块,例如:
if(a > 0) {   // 程序块开始int x, y;x = 1;y = x + 2;
}  // 程序块结束,} 后面不需要加分号

二、if-else语句

if-else语句用于条件判定,表示如果…,否则…,其语法如下:

if(表达式)语句1;
else语句2;

其中,else部分是可选的。该程序执行时,先计算表达式的值,如果其值为真(即表达式的值非0),则执行语句1,如果其值为假(即表达式的值为0),则执行语句2。
看一个例子:

#include <stdio.h>int main()
{int n = 5;char s[] = "hello";if (n > 0)for(int i =0; i<n;i++)if(s[i] > 0)printf("%c", s[i]);elseprintf("n为负数");return 0;
}

如上程序,从缩进结构可以明确的表明设计意图,但编译器不一定能get到,它可能会将else与内层的if配对,这种错误是很难发现的,因此我们建议在有if语句嵌套的情况下使用花括号。例如:

#include <stdio.h>int main()
{int n = 5;char s[] = "hello";if (n > 0) {for (int i = 0; i < n;i++) {if (s[i] > 0)printf("%c", s[i]);}} elseprintf("n为负数");return 0;
}

三、else-if语句

else-if用于多路判定

if(表达式)语句;
else if(表达式)语句;
else if(表达式)语句;
else if(表达式)语句;
else语句;    

上述程序中的各表达式将被依次求值,一旦某个表达式的结果为真,执行与之对应的语句,并终止整个语句序列。例如:

#include <stdio.h>int main()
{int score = 70;if (score >= 90) {printf("优秀");} else if (score >= 80) {printf("良好");} else if (score >= 60) {printf("及格");} else {printf("不及格");}return 0;
}

四、switch语句

switch语句用于多路判定,它测试表达式是否与给定的一些常量整数值中的某一个值匹配,并执行相应的分支动作。

switch(表达式)case 常量表达式: 语句序列case 常量表达式: 语句序列default: 语句序列
  • default分支是可选的
  • 如果某个分支与表达式的值匹配,则从该分支开始执行
  • 各表达式必须互不相同
  • 如果没有case分支能匹配表达式的值,则默认执行default分支,如果没有default,则switch语句不执行任何动作
  • 各分支及default的分支排列次序是任意的(即使如此,请将default放到最后)
    看一个例子:编写一个程序,统计输入中数字、空白符、其他字符出现的次数。
#include <stdio.h>// 编写一个程序,统计输入中各个数字(0-9)、空白符(空格,水平制表符,换行符)、其他字符出现的次数。
int main()
{int ndigit[10];  // 存放各个数字出现的次数int nwhite;  // 空白符出现的次数int nother;  // 其他字符出现的次数for (int i = 0; i < 10;i++)ndigit[i] = 0;nwhite = nother = 0;char c;  // 输入的字符while ((c = getchar()) != EOF) {switch (c) {case '0': case '1':case '2': case '3':case '4': case '5':case '6': case '7':case '8': case '9':ndigit[c - '0']++;break;case ' ':case '\t':case '\n':nwhite++;break;default:nother++;break;}}for (int i = 0; i < 10;i++) {printf("%d出现的次数:%d\n", i, ndigit[i]);}printf("空白符出现的次数:%d\n", nwhite);printf("其他字符出现的次数:%d\n", nother);return 0;
}
  • case只是一个标号,所以,执行完一个case,程序会接着执行下一个case,除非在程序中显式跳转,这与else if有所不同
  • 跳出switch语句最常用的方法是使用break语句与return语句
  • break语句的作用是立即退出switch,不再执行switch中其他分支
  • 依次执行各分支有优点也有缺点。好的一面是它可以把若干个分支组合在一起完成一个任务,如上面的统计各数字的次数,不好的地方在于这样的程序修改时容易出错。所以除了组合分支外,其他分支都应该加上break以明确表示switch的退出
  • 作为一种良好的程序设计风格,在switch语句最后一个分支(即default)的后面也加上break语句。这样做在逻辑上没有必要,但当我们需要向该switch添加其他分支时,这种防范措施会降低出错误的可能性。

五、while循环与for循环

while(表达式)语句
  • while循环,先测试表达式,如果表达式的值为真,进入循环体,执行语句,接着继续测试表达式,直至表达式的值为0,退出循环,执行循环后面的其他程序
for(表达式1;表达式2;表达式3)语句  
它等价于
表达式1
while(表达式2)语句 表达式3  
  • for循环,先执行表达式1,再测试表达式2,如果表达式2的值为真,执行语句,然后再执行表达式3,接着继续测试表达式2,直到表达式2的值为0,退出循环,执行循环后面的其他程序
  • 最常见的情况是,表达式1与表达式3是赋值表达式或函数调用,表达式2是关系表达式
    这三个表达式中的任何一个都可以省略,但分号必须保留,如果在for语句中省略表达式1与表达式3,它就退化成for循环语句。如果省略表达式2,则认为其值永远为真,因此如下代码
    for( ; ; ) {

    }
    是一个无限循环语句,这种语句需要借助break或return语句才能终止执行。
  • 在设计程序时到底选用for循环还是while循环,主要取决于程序设计人员的个人偏好。例如,
    while((c = getchar()) == ’ ’ || c==‘\n’ || c==‘\t’)
    因为没有初始化或重新初始化的操作,所以使用while更自然些
    如果语句中需要执行简单的初始化和变量递增,使用for更合适一些,它将循环控制语句集中放到循环的开头,结构更紧凑、更清晰,例如,
    for(int i = 0; i<n; i++)

六、do-while循环

while和for这两种循环在循环体执行前对终止条件进行测试,与此相反,do-while循环在循环体执行之后测试终止条件,这样,循环体至少被执行一次。

do {语句;
} while (表达式);

先执行循环体,再测试表达式,如果表达式为真,继续执行循环体,直到表达式结果为假,退出循环

七、break语句和continue语句

在需要的时候提前退出循环,是很有用的操作。break语句可以用于从for、while、do-while循环中提前退出,就如同在switch中提前退出一样。

  • break能使程序从switch语句或最内层循环中立即跳出
  • continue语句用于使for、while、do-while语句开始下一次循环的执行;在while与do-while循环中,continue意味着立即执行测试条件,而在for循环中,则意味着控制转移到递增循环变量部分。
  • continue只能用于循环,不用于switch,某个循环包含的switch语句中的continue,将导致进入下一次循环

八、goto

C语言提供了可随意滥用的goto语句以及标记跳转位置的标号。从理论上讲,goto语句是没有必要的,实践中不使用goto也可以很容易地写出代码。但是,在某些场合下goto还是用的着的。最常见的用法是终止程序在某些深度嵌套的结构中的处理过程。例如,一次跳出两层或多层循环。这种情况下使用break是不能达到目的的,它只能从最内层循环退出到上一级的循环。
下面是使用goto的一个例子:

#include <stdio.h>int main()
{int r = 0;for (;;)for (int i = 0; i < 10; i++)if (i > 5) {r = i;goto error;}
error:printf("%d", r);return 0;
}
  • 如果错误处理代码很重要,并且错误可能出现在多个地方,使用goto语句会比较方便。
  • error为标号,命名同变量的命名形式,标号的后面要紧跟一个冒号
  • 标号的作用域是整个函数

再看一个例子,考虑判定两个数组a和b中是否有相同元素的问题。一种可能的解决办法是

#include <stdio.h>int main()
{int a[10];int b[6];for (int i = 0;i < 10; i++) {a[i] = 1;}for (int i = 0;i < 6; i++) {if (i = 5) {b[i] = 1;}else {b[i] = 2;}}int ia = 0;int ib = 0;int res = 0;for (int i = 0; i < 10; i++)for (int j = 0; j < 6; j++)if (a[i] == b[j]) {ia = i;ib = j;res = a[i];goto found;}found:printf("%d\t%d\t%d", ia, ib, res);return 0;
}

所有使用了goto的语句都可以改写成不带goto,但可能会增加一些额外的测试或变量,例如,上面的程序可以改写成:

#include <stdio.h>int main()
{int a[10];int b[6];for (int i = 0;i < 10; i++) {a[i] = 1;}for (int i = 0;i < 6; i++) {if (i = 5) {b[i] = 1;}else {b[i] = 2;}}int ia = 0;int ib = 0;int res = 0;int found = 0;for (int i = 0; i < 10; i++){if (found) {break;}for (int j = 0; j < 6; j++)if (a[i] == b[j]) {ia = i;ib = j;res = a[i];found = 1;break;}}if(found)printf("%d\t%d\t%d", ia, ib, res);return 0;
}

大多数情况下,使用goto的程序比没有使用goto的程序要难以理解和维护,少数情况除外,比如我们前面的几个例子。尽管问题并不太严重,但我们还是建议尽可能少的使用goto语句。

相关文章:

  • 【四】记一次关于架构设计从0到1的讨论
  • prometheus二进制安装
  • 【Flink-Kafka-To-ClickHouse】使用 Flink 实现 Kafka 数据写入 ClickHouse
  • SSTI模板注入基础(Flask+Jinja2)
  • 前端验收测试驱动开发
  • 高并发情况下,数据库与缓存数据不一致问题
  • WorkPlus超级APP助力企业节省IT人力成本,实现快速移动化
  • ARM学习(24)Can的高阶认识和错误处理
  • WPScan安全建议和防护
  • ArrayList源码学习笔记(3)
  • Vue 的两种实现:VSCode 中配置 vue 模板快捷方式的过程
  • 合并两个有序链表算法(leetcode第21题)
  • 台阶问题额
  • linux如何在不重新登录用户的情况下使用户加入的组生效
  • S7-1200/1500(T) 通过功能块FB38051实现 SINAMICS S200 的 EPOS 基本定位控制
  • isset在php5.6-和php7.0+的一些差异
  • Less 日常用法
  • Linux中的硬链接与软链接
  • SQLServer之创建显式事务
  • vagrant 添加本地 box 安装 laravel homestead
  • vue 配置sass、scss全局变量
  • vuex 笔记整理
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • windows下使用nginx调试简介
  • 百度小程序遇到的问题
  • 坑!为什么View.startAnimation不起作用?
  • 模型微调
  • 盘点那些不知名却常用的 Git 操作
  • 前端性能优化--懒加载和预加载
  • 深度学习入门:10门免费线上课程推荐
  • 优秀架构师必须掌握的架构思维
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • #QT(串口助手-界面)
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (02)vite环境变量配置
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (笔试题)合法字符串
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转) Android中ViewStub组件使用
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • .dwp和.webpart的区别
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .NET 反射的使用
  • .NET高级面试指南专题十一【 设计模式介绍,为什么要用设计模式】
  • .net中生成excel后调整宽度
  • @cacheable 是否缓存成功_Spring Cache缓存注解
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • [bzoj1324]Exca王者之剑_最小割
  • [CF494C]Helping People
  • [CISCN2021 Quals]upload(PNG-IDAT块嵌入马)
  • [codeforces] 25E Test || hash
  • [CVPR 2023:3D Gaussian Splatting:实时的神经场渲染]