关于流程控制,一般来说,计算机使用有着三种流程:一就是正常走,按照预先设定的语句序列执行,二就是重复做那么几个动作,直到满足条件,三就是有几条分支走向,根据目前条件去选择分支序列执行。

一、C中的循环

在C语言中,针对循环这一特性的就有常见于循环次数不明确的while关键字,循环次数明确的for关键字。

1.1 while循环

while,英文意思是当......时候,所以它的通用形式很明显,如下:

while (expression)
statement

上面伪代码解读起来就是,while关键字出现,判断括号内表达式是否正确,如果正确就继续执行statement中语句,statement语句可以是以分号结尾的单条语句也可以是大括号括起来的语句块,如果不正确就跳过statement部分,执行后面的语句。

关于expression,这里需要知道的就是它是否为真。很多数据类型都有着自己的零值,而非零值的就是真值,当然,布尔值就是最直显的真假值。常见的表达式如比较常见的"1 == 2"、"1 != 7"、"7 > 6"这种数值判断,同类型间比对大小,不同类型的比较往往有一个隐性的类型转换,如整型和浮点型的比较,往往是整型转换成浮点型,特殊的比如字符型和整型的比较:'a'和6的比较,由于浮点型也被称为单字节型整数,所以转换起来不会丢失精度,两者的比较相对整型和浮点型的比较要更加准确可靠,而后者往往因为精度丢失无法得到理想输出。

另外,在C语言中可用的用于比较的运算符有:<、>、<=、>=、==、!=,常见的用于比较数值的可以用运算符进行,但如果是有特殊规则的比较--比如比较字符往往就要用一些比较特殊的方法,常用的就是ctype.h、string.h中的函数,也有一些是属于自定义的函数方法。掌握循环,非常重要的就是控制expression这一个循环条件,因为循环中往往会对这一条件进行操作,如果操作不当就会出现无限循环,一直没有停止循环的条件。

简单例子:

//正确例子
int i = 10;
while(i < 20)
i++;
printf("i: %d\n", i); //"i: 20" //错误例子1
int i = 10;
while(i > 10)
i++;
printf("i: %d\n", i); //"i: 10" //错误例子2
int i = 10;
while(i > 9)
i++;

如上, 可以设定终止条件为i自加将达到的值,比如20,当它自加达到20时,不符合while条件,判定表达式为非真,while循环中断;而当它设定为大于10时,根本没达到循环条件,所以根本没进入循环;另外设定为一开始就满足的条件,大于9,那它一直自加都是满足条件的,根本不会退出循环,而是会一直运行下去,有时候病毒就是干的这种活,就这么做无用的事但是又占着你的资源。

和scanf联动的while

char ch;
while(scanf("%c", ch)&&(ch != 'n'))
printf("是的,我还想进行下去");
printf("不想玩了,拜拜");

作为expression,随便一个语句都能成立,比如上面的scanf语句,在成功接收数据后再判断赋值初始化后的ch变量接收的是否不是'n'字符,这个走完才确定循环是否进行。简单地说,就是输入的是否是n,不是就继续循环,是就退出循环。需要注意的是,上面的&&,是逻辑运算符的一种,表示两个都为真表达式方为真。上面的例子可以进一步简单化:

char ch;
scanf("%c", ch);
while(ch != 'n'){
scanf("%c", ch);
printf("是的,我还想进行下去");
}
printf("不想玩了,拜拜");

上面只是一个范例,对于很多程序来说,持续运行就是进入了一个类似while循环的无穷循环,退出条件是可控的。

do-while

这也是while循环的一种,不过正常的while循环是先进行判断,然后再来确定是否执行循环语句,但do-while不同,它是先跑了一次,然后再来看是否符合循环条件,这样无论是否符合条件它都先跑了一次。为何?因为它的通用形式是这样的:

do
statement
while(expression);

上面statement和expression的情况和寻常的while循环一样,没有变化,但由于代码是顺序式进行的,在这种情况下就先执行do部分的statement,然后再来进行while的expression是否为真的判断。

int i = 10;
do {
printf("%d\n", i);
i++;
} while(i <10);
printf("outside: %d\n", i);
/*输出:
10
outside: 11
*/

上面的例子就可以看出来,循环条件并不成立,但是它依然是执行了do后面的语句块,然后因为循环条件不成立所以退出继而执行后面的语句了。

1.2 for循环

和while循环不同,for循环属于计数循环,天然地适合上面最早出现的例子所在场合。这种循环,我们在开始之前,就能确定它大概会经历多少次循环然后停止了的,而while循环则是不明确的,你得一直等的那种,就比如上面的第二种输入型例子。

for循环的通用形式如下:

for ( init; condition; increment )
statement(s);

上面需要关注的主要就是condition部分,和while里面的expression一样,这里是循环条件的判断,这里的语句为真,循环继续,语句为假,循环结束;另外,init部分一般都用来进行初始化,而increment则是做递增或者递减的运算。具体可参考下面的例子。

int i;
for(i = 0; i < 10; i++)
printf("%d, ", i);
printf("%d, ", i);
//输出"0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10"

上面就是基础例子,用来对i变量进行初始化,然后每次加一运算,最后在i为10时跳出循环,所以上面的例子才会输出在一行,也可以这么修改一下:

int i;
for(i = 0; i < 10; i++)
printf("%d, ", i);
printf("\n%d, ", i);
//输出"0, 1, 2, 3, 4, 5, 6, 7, 8, 9,"
//"10"

这样就分层比较明显了,单纯看输出的话。


其实init部分和increment部分都可以不做任何操作,也可以做不止一个操作,比如:

int i = 0;
for(;i < 10;){
printf("%d, ", i);
i++
}
printf("\n%d", i);
//输出"0, 1, 2, 3, 4, 5, 6, 7, 8, 9,"
//"10" //甚至可以这样
char ch;
for(ch = 'a';ch != 'f';ch++)
printf("%c, ", ch);
printf("\nI' m out.\n");
/*输出
a, b, c, d, e,
I' m out.
*/

如上进行,同样可以实现上面的效果,然后来多搞几个

int i, j;
for(i=10, j=0;i > j;i--, j++)
printf("i:%d, j: %d\n");
printf("outside: i:%d, j: %d\n");
/*输出
i:10, j: 0
i:9, j: 1
i:8, j: 2
i:7, j: 3
i:6, j: 4
outside: i:5, j: 5
*/

如上,同时进行两种计数,然后让他们交叠在一起做判断条件,只要你设计得好,花样少不了(反正面试题是这样玩花样的)。其实,上面的condition部分也可以为空置的,啥语句都没有,啥操作都没有,然后就是一个另类的while循环了,不过,这样没啥意思,因为要搞这样还不如用while。

1.3 退出循环

上面就是基本的循环使用,除此以外还有一些配合循环使用的关键字,比如:break和continue。这两个关键字都可用于循环体的代码之中,前者用来终止循环,跳到循环后面的语句;后者用来跳出本次循环,略过循环体中continue语句后面的代码,直接进行下一次循环。不过这两者往往是和if语句进行配合使用,所以这里进行一次简单强调和介绍。

二、分支和跳转

和循环一样的,就是分支跳转同样是需要条件的,比如,十八岁才能开车这一条件就形成分流,把十八岁以下和十八岁乃至以上人群分开。正如用于这两种场景的关键字if和switch的意思,前者是当.....时候,后者是切换,结合英文意思可以很好理解它们的通用形式和应用场景。

2.1 if-else跳转

其通用模式如下:

//单if
if (expression)
statement //if-else
if (expression1)
statement1
else
statement2 //if-else if-else
if (exp1)
statement1
else if (exp2)
statement2
...
else
statementn

如上,if代表的跳转控制有大概三种:

  • 单if,当expression为真,执行statement部分语句块(单条语句或大括号括起来的语句块);
  • if-else组合,当expression1为真,执行statement1部分语句,expression2为假,执行statement2部分语句;
  • else-if组合,(先说明,当中的...表示省略上面重复部分),和上面一致,exp1为真,执行statement1,exp2为真,执行statement2,exp3为真,执行statement3。。。。。。上面判断条件都不成立,执行statementn。

一般来说,if语句使用场景是和循环或者其他跳转语句进行配合用来设计更为复杂的情况,单个拿出来,更多是作为一些例子讲解或者应用于简单函数。配合上面的else-if组合,这种多重选择的应用场景就是switch的使用场合。

2.2 多重选择switch

switch通用形式如下:

switch (整型表达式) {
case 常量1:
语句1
case 常量2:
语句2
default:
语句3
}

对整型表达式求值,根据这个值来对号入座,如果这个值是下面case后面的任一常量,就执行下面对应语句。整型表达式必须是一个整型或者枚举类型,事实上,枚举类型更多。当上面case对应常量都不不符合整型表达式的值,就执行default对应语句。

int choice;
scanf("%d", &choice);
switch(choice){
case 0:printf("0");break;
case 1:printf("1");break;
case 2:printf("2");break;
default:printf("not 0, 1, 2\n");
}

可以看得到,break语句也用可用于switch当中,尤其是case后面的执行语句,每一部分case都会带一个break来跳出switch,不然就会一直顺序执行下去。比如都不带break,然后输入的是1,那它就会输出case1和case2以及defautlt对应语句。值得一提的是,continue也同样可以用于跳出switch,但它本义是跳出当前循环不执行循环后面的部分,所以它用在循环中的switch中也是合理的,简单的switch没有用它的场景。当然,浮点型被称为单字节整型,也是可以作为上面的常量表达式进入switch分支中使用的。比如:

char ch;
switch(ch) {
case 'a':printf("a");break;
case 'e':printf("e");break;
case 'i':printf("i");break;
case 'o':printf("o");break;
default:printf("balabala");
}

逻辑运算符和条件运算符

上面在说明while循环和scanf配置应用的时候,有提到&&这么一个逻辑运算符,除此以外,还有其他逻辑运算符,如下:

逻辑运算符 说明
&& a&&b,表达式a和表达式b都为真,结果为真,反之为假
|| a || b,表达式a和表达式b,两者有一个为真,结果为真,两个都假,结果为假
! !a,表达式a为真,!a为假;表达式a为假,!a为真

逻辑运算符的应用比较简单,真假判断就完了。有意思的是,在iso646.h头文件中,and可用于替代&&,or可替代||,not可替代!,这样使用更能明白其意义。

条件运算符

上面的if-else组合,当其应用的场景中,statement都是单语句情况下,用条件运算符替代if-else也可达到同等效果。是的,这是个替代品,用来写出更紧凑的语句。如下:

if (y < 0)
x = -y;
else
x = y //转换成条件运算符
(y < 0) ? (x = -y) : (x = y)

?:就是条件运算符,从上面例子就可以很直白得看出来它的通用形式如下:

(exp1) ? (exp2): (exp3)
//exp1为真,执行exp2语句;为假,执行exp3语句

2.3 难搞的goto

作为跳转控制,goto估计是最为简单的一个了,就是给某个可以跳转的地方加个label,然后在需要使用的时候,使用goto label跳转到该处执行对应语句即可。但对于初学者来说,往往难以搞清分支的对应,然后把定向goto用成乱向乱飞;另外加就算是一些老手,针对超大项目的管理,使用goto也是很犯迷糊的事,几十个文件,每个文件几百行,随便跳一下,脑子都要炸了。往常学习用得少的缘故,这里也不进行。

三、嵌套

作为流程控制,跳转和循环之间往往可以相互嵌套使用,甚至跳转内也可以继续套一层跳转,循环内再套好几层循环,只要你能设计得分明,那它就能给你需要的理想输出。所以这个其实是自定义性质的内容,要说起来就太泛了,只说那么几个应用场景和对应措施供以参考。

场景一:

玩一个游戏,像猜数猜单词游戏,可以玩五次,每次都持续询问,对就退出不对继续问,或者不想玩了就退出来,这种场景就可以如下嵌套:

for 5次
while(是否是某个值&&(!退出字符))


如上,可以用while循环形成和玩家的持续对话,而玩家答对或者输入退出字符的时候就退出while循环,然后这样一个流程就结束了。这样的流程可以进行5次,当然,当你不想进行的时候,for循环也可以针对某个退出条件添加一个if,达成条件就执行break即可。

场景二:

拆分不同难度的关卡,进行不同难度的游戏

switch(难度) {
case 1:while(){game1};break;
case 2:while(){game2};break;
...
default:printf("没有这个难度,意料外输入");
}

如上,可以用switch拆分难度,在输入难度选择后,进行不同难度的游戏(由while循环把控,内设退出条件break),当输入出问题就进入default分支退出;同样的,switch-case也可被else-if来进行替代。

c流程控制的更多相关文章

  1. 第10章 Shell编程(4)_流程控制

    5. 流程控制 5.1 if语句 (1)格式: 格式1 格式2 多分支if if [ 条件判断式 ];then #程序 else #程序 fi if [ 条件判断式 ] then #程序 else # ...

  2. Shell命令和流程控制

    Shell命令和流程控制 在shell脚本中可以使用三类命令: 1)Unix 命令: 虽然在shell脚本中可以使用任意的unix命令,但是还是由一些相对更常用的命令.这些命令通常是用来进行文件和文字 ...

  3. PHP基础知识之流程控制的替代语法

    PHP 提供了一些流程控制的替代语法,包括 if,while,for,foreach 和 switch. 替代语法的基本形式是把左花括号({)换成冒号(:),把右花括号(})分别换成 endif;,e ...

  4. Python黑帽编程2.4 流程控制

    Python黑帽编程2.4  流程控制 本节要介绍的是Python编程中和流程控制有关的关键字和相关内容. 2.4.1 if …..else 先上一段代码: #!/usr/bin/python # - ...

  5. 使用yield进行异步流程控制

    现状 目前我们对异步回调的解决方案有这么几种:回调,deferred/promise和事件触发.回调的方式自不必说,需要硬编码调用,而且有可能会出现复杂的嵌套关系,造成"回调黑洞" ...

  6. [Java入门笔记] Java语言基础(四):流程控制

    流程控制指的是在程序运行的过程中控制程序运行走向的方式.主要分为以下几种: 顺序结构 顺序结构,顾名思义,是指程序从上往下逐步顺序执行.中间没有任何的判断和跳转. 分支结构 Java提供两种分支结构: ...

  7. node基础13:异步流程控制

    1.流程控制 因为在node中大部分的api都是异步的,比如说读取文件,如果采用回调函数的形式,很容易造成地狱回调,代码非常不容易进行维护. 因此,为了解决这个问题,有大神写了async这个中间件.极 ...

  8. Shell入门教程:流程控制(1)命令的结束状态

    在Bash Shell中,流程控制命令有2大类:“条件”.“循环”.属于“条件”的有:if.case:属于“循环”的有:for.while.until:命令 select 既属于“条件”,也属于“循环 ...

  9. Oracle中PL/SQL的执行部分和各种流程控制

    Oracle中PL/SQL的执行部分和异常部分 一.PL/SQL的执行部分. 赋值语句. 赋值语句分两种,一种是定义一个变量,然后接收用户的IO赋值:另一种是通过SQL查询结果赋值. 用户赋值举例: ...

  10. swift_简单值 | 元祖 | 流程控制 | 字符串 | 集合

    //: Playground - noun: a place where people can play import Cocoa var str = "Hello, playground& ...

随机推荐

  1. Nginx-总结列表

    Nginx配置详解 Nginx实现前后端分离,反向代理.负载均衡 Nginx 专为性能优化而开发,性能是其最重要的考量,实现上非常注重效率 .它支持内核 Poll 模型,能经受高负载的考验,有报告表明 ...

  2. Golang-包8

    http://c.biancheng.net/golang/package/ Go语言包的基本概念 Go语言是使用包来组织源代码的,包(package)是多个 Go 源码的集合,是一种高级的代码复用方 ...

  3. HTB Builder walkthrough

    nmap nmap -sS -A -T4 10.10.11.10 Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-20 01:31 UTC Nma ...

  4. CentOS 7 MongoDB 重装启动失败

    诊断过程 1. 错误提示: > journalctl -xe 提示: .... ERROR: child process failed, exited with error number 14 ...

  5. 用 DeepSeek 分析,SaaS 多租户的3种隔离模式,结果。。。

    大家好,我是汤师爷~ 在SaaS模式下,多租户之间的资源隔离是基础且关键的一环. SaaS服务商需要在确保运营效率和控制成本的前提下,搭建一个让多个租户能够同时访问的共享环境. 虽然大家都在用同一套S ...

  6. go的math/rand随机数生成器

    伪随机数生成器,默认情况下随机数种子是固定的, 注意:固定的随机数种子每次生成的随机数都是相同的随机数序列 一.基础用法 math/rand 包提供了随机数生成的方法.常用的函数包括: rand.In ...

  7. 爬虫基础知识及scrapy框架使用和基本原理

    爬虫 一.异步IO 线程:线程是计算机中工作的最小单元 ​ IO请求(IO密集型)时多线程更好,计算密集型进程并发最好,IO请求不涉及CPU 自定义线程池 进程:进程默认有主线程,可以有多线程共存,并 ...

  8. JMeter中使用嵌套变量方法

    JMeter中使用嵌套变量方法 在使用JMeter进行接口测试时,经常遇到需要动态生成变量名并引用其值的情况.在JMeter中,嵌套变量的使用可以帮助我们实现这一目标.本文将详细介绍如何在JMeter ...

  9. Luogu P11233 CSP-S2024 染色 题解 [ 蓝 ] [ 线性 dp ] [ 前缀和优化 ]

    染色:傻逼题. 赛时没切染色的都是唐氏!都是唐氏!都是唐氏!都是唐氏!都是唐氏!都是唐氏!都是唐氏! 包括我. 真的太傻逼了这题. 我今晚心血来潮一打这题,随便优化一下,就 AC 了. 怎么做到这么蠢 ...

  10. 解决Webstorm Nodejs console.log("这是中文") 控制台乱码

    设置文件编码 自定义vm选项文件 添加 文件最后一行添加 -Dfile.encoding=UTF-8 3.修改注册表 Windows+R --> regedit --> 计算机\HKEY_ ...