C语言系列之——(6)C语言中的控制语句
c语言中的控制语句
- 1.条件判断语句
- 1.1 if
- a if……else……
- b 嵌套if
- b.1悬空else问题
- 1.2 switch
- 2.循环执行语句
- 2.1 do while
- 2.2 while
- 2.3 for
- a. for的多表达式问题
- b. for的无表达式问题
- 3.转向语句
- 3.1 break 和 continue
- 3.2 goto
- 3.3 return
- a.return的使用规范
控制语句用于控制程序的执行流程,以实现程序的各种结构方式
C语言有三种结构方式:顺序结构、选择结构和循环结构,他们由特定语句的定义符组成
而控制语句则有九种:
这九种可分为以下三类:
条件判断语句/分支语句:if , switch
循环执行语句:do while , while , for
转向语句:break , goto , continue , return
1.条件判断语句
1.1 if
if语句的语法形式如下:
if(表达式)语句表达式为真(成立),语句执行;反之则不执行
if默认只包含紧跟的第一条语句,若有多条待执行语句,需要用大括号{}将语句包含在内
if(表达式){语句1语句2……}上面的条件判断只能实现一种情况下的行为执行,但实际中,我们常常想实现多种情况的对应行为执行,如:
不下雨就不带伞,下雨就带伞
如果bob爱吃辣,就给他辣条,如果爱吃酸,就给他柠檬,如果……
这时只用if一个一个情况识别显然不太可取,实际上,if可以“升级”为下面的几种形式:
a if……else……
对于 “不下雨就不带伞,下雨就带伞” 这种仅有两种对立,即只有a成立和a不成立的情况,可以使用if else语句,形式如下:
if(表达式1)语句1else语句2表达式1为真,就执行语句1,为假就执行语句2,相当于:如果(表达式1为真)就(执行语句1)否则 就(执行语句2)if else区分的两种情况是对立关系,即这两种情况互斥,且这两种情况组合起来就是全集(全部情况)
b 嵌套if
而在将一个全集分成多个部分,有大于等于三种互斥情况时,可以用嵌套if,核心是如果……否则……的嵌套条件判断,如下:
if(表达式1)语句1else{if(表达式2)语句2else{……}}上面的else里又有if可以简化成下面这种形式:if(表达式1)语句1elseif(表达式2)语句2elseif(表达式3)语句3elseif(……)……else语句n上面代码的嵌套逻辑可用下图表示
当然if里也可以嵌套if(表达式1){if(表达式2)语句1else//表示满足表达式1情况下不满足表达式2{……}}else{……}嵌套if重在如果……否则……的层层逻辑判断,在逻辑满足下if的嵌套是灵活多变的
b.1悬空else问题
请看下面代码:
#include<stdio.h>intmain(){inta=0;intb=2;if(a==1)if(b==2)printf("hehe\n");elseprintf("haha\n");return0;}上面代码中的else并不是和if(a == 1)匹配,而是和if(b == 2)匹配,相当于:
#include<stdio.h>intmain(){inta=0;intb=2;if(a==1){if(b==2)printf("hehe\n");elseprintf("haha\n");}return0;}C语言并不是和python一样靠缩进来划分代码块,而是只会按语法符号{}划分代码块作用域
else总是跟自己所在作用域内离它最近、尚未配对的 if匹配
要让else和if(a == 1)匹配,代码应改为:
#include<stdio.h>intmain(){inta=0;intb=2;if(a==1){if(b==2)printf("hehe\n");}elseprintf("haha\n");return0;}1.2 switch
switch是一种特殊形式的 if else 结构,用于判断表达式(条件)有多个结果的情况
语法形式如下:
switch(表达式){case整型常量表达式1://case后必须是一个整型的常量,不能是1.0这种小数,也不能是k这种变量语句1case整型常量表达式2:语句2……}switch的逻辑是:
先计算表达式的结果,然后从和结果匹配的case处开始依次执行后续语句
请看下面代码:
#include<stdio.h>intmain(){inta=1;intb;switch(a){case1:b=1;case2:b=2;case3:b=3;}printf("%d",b);return0;}上面的代码展现出了使用switch语句的一个大坑,就是switch语句只会根据表达式不同的值从不同的case处开始,并不会局限于只实现一个case区域里的代码,而是从开始位置一直执行到尾,所以上面代码最后打印结果为3,而不是1
要想只执行一个分支的语句,需要在语句后加上break
switch(表达式){case整型常量表达式1:语句1break;case整型常量表达式2:语句2break;……}换句话说,case决定了分支的入口,break决定了分支的出口
在使用 switch 语句的时候,我们经常可能遇到一种情况,比如 switch 后的表达式中的值无法匹配代码中的 case 语句的时候,这时候要不就不做处理,要不就得在 switch 语句中加入default子句
switch(表达式){case整型常量表达式1:语句1break;case整型常量表达式2:语句2break;……default:语句nbreak;}default其实就相当于另一个有自己特殊对应值的case入口,这个入口的条件是:表达式的值和其他case的值都不匹配
所以,default和case的顺序没有一定的规定,default可以不放在末尾,甚至可以放在开头,因为它其实也就是一个条件特殊的入口
不过通常还是放在末尾
2.循环执行语句
2.1 do while
循环语句中do while的使用频率较低,其语法形式如下:
do循环语句while(表达式);它的执行逻辑为:
开始先执行循环语句,再判断表达式真假
若表达式为真,则重复上诉操作;若为假,则停止执行循环语句
也就是说,do while的循环语句,至少会被执行一次
注意:do while默认只包含do紧跟的第一条语句,若有多条待执行语句,需要用大括号{}将语句包含在内
下面用打印1~10的数字来演示do while的使用:
#include<stdio.h>intmain(){inti=1;do{printf("%d ",i);i++;}while(i<11);return0;}2.2 while
while语句的语法结构和if很相似:
while(表达式)循环语句它的执行逻辑为:
开始先判断表达式真假
若表达式为真,执行循环语句,每执行完都会重新判断表达式真假,为真则重复上述步骤;
若为假,则停止执行循环语句
同样的,while也默认只包含紧跟的第一条语句,若有多条待执行语句,需要用大括号{}将语句包含在内
下面用打印1~10的数字来演示while的使用:
#include<stdio.h>intmain(){inti=1;while(i<11){printf("%d ",i);i++;}return0;}2.3 for
for循环的使用频率较高,其复杂程度也比前两种更高,其语法形式如下:
for(表达式1;表达式2;表达式3)循环语句其中:
表达式1用于循环变量的初始化
表达式2用于循环结束条件的判断
表达式3用于循环变量的调整
它的执行可用下图表示:
for 循环最先执行一次表达式 1 完成变量初始化,随后进入循环判断逻辑:
先运行表达式 2 做条件判定,若条件为假,循环直接结束;若条件为真,则执行循环语句。
执行循环语句时如果碰到 break,会直接终止整个循环;如果碰到 continue,会立刻停止当前循环剩下的代码,跳到表达式 3 执行变量调整。
正常走完循环语句后同样会执行表达式 3 更新变量,之后再次回到表达式 2 重新判断条件,不断重复该流程,直到表达式 2 判定为假退出循环。
【表达式1只在开始时执行这一次,后续就不会再执行表达式1】
同样的,while也默认只包含紧跟的第一条语句,若有多条待执行语句,需要用大括号{}将语句包含在内
下面用打印1~10的数字来演示for的使用:
#include<stdio.h>intmain(){for(inti=1;i<11;i++)printf("%d ",i);return0;}a. for的多表达式问题
for在表达式1中可同时初始化多个同类型变量,多个变量使用逗号分隔,如:
for(inti=1,g=3,……;表达式2;表达式3)循环语句for在表达式2和表达式3中同样也可以利用逗号运算符书写多段语句,但需要注意的是:
将多个表达式用逗号分隔组成多段语句,实际上构成了逗号表达式,执行时会依次执行每个表达式,最终将最后一个表达式的值作为整个逗号表达式的值,而这对靠判断真假来决定循环的终止或执行的表达式2来说是个大坑,请看下面代码:
#include<stdio.h>intmain(){for(inti=1,e=1;i>11,e<5;i++,e++)printf("%d+%d ",i,e);return0;}上面的代码,表达式2的位置判断的是整个逗号表达式的真假,而该逗号表达式的值就是e < 5的值,与i > 11无关,所以即使i < 11,循环依旧进行
如果想实现多表达式作用的真假判断,可以借助逻辑运算符
下面代码用&&将两个表达式用并且关系关联起来,使循环开始即结束
#include<stdio.h>intmain(){for(inti=1,e=1;(i>11)&&(e<5);i++,e++)printf("%d+%d ",i,e);return0;}b. for的无表达式问题
for 循环允许省略任意一个、两个甚至全部三个表达式,但底层执行逻辑不会改变。程序运行到被省略的表达式位置时,等价于执行一条空语句。
特别注意:
如果省略表达式 2(条件判断部分),该位置默认判定为恒真,循环会持续运行形成死循环;这种情况下想要退出循环,只能依靠循环体内的break语句,否则循环永远不会终止。
3.转向语句
在程序运行时,我们时常需要改变代码默认的顺序执行流程,循环里提前退出、跳过本轮代码、直接跳转到指定位置、结束函数等场景都十分常见。
C 语言提供了四类转向语句:break、continue、goto、return,用来灵活调整程序执行流向。
下面我们逐一讲解它们的作用与使用规范。
3.1 break 和 continue
break:直接永久终止当前循环,代码执行到 break 时,会立刻跳出当前循环,运行循环外部后续代码
continue:放弃本次循环剩下尚未执行的代码,直接开启下一轮循环
下面我们直接通过实际代码感受这两个转向语句:
#include<stdio.h>intmain(){printf("===== break 演示 =====\n");for(inti=1;i<=10;i++){if(i==5){break;// 直接终止整个循环}printf("%d ",i);}printf("\nbreak演示结束\n");printf("===== continue 演示 =====\n");for(inti=1;i<=10;i++){if(i==5){continue;// 跳过本次循环剩余代码,直接进入下一轮}printf("%d ",i);}printf("\ncontinue演示结束\n");return0;}运行结果:
===== break 演示 =====
1 2 3 4
break演示结束
===== continue 演示 =====
1 2 3 4 6 7 8 9 10
continue演示结束
3.2 goto
goto语句实际包括goto和跳转标号 ,是一种比较特殊的语法,其形式如下:
goto跳转标号;//和取变量名一样给跳转标号取名跳转标号:程序执行到goto语句时,会直接跳转到 跳转标号: 的这个位置执行后续代码
在想要跳出多层循环时,如果用break,每层循环都要有break语句,而用goto就会方便很多
#include<stdio.h>intmain(){// 双层循环for(inti=1;i<=3;i++){for(intj=1;j<=3;j++){printf("i=%d,j=%d\n",i,j);if(i==2&&j==2){gotoend_loop;}}}end_loop:// 跳转标号,名称自定义,末尾带冒号printf("已经跳出双层循环\n");return0;}3.3 return
return 用于终止当前所在函数,将程序执行流程返回函数调用处;如果函数有返回值,还可以附带返回数据。
在main函数中执行return,代表直接结束整个程序;
一旦执行return,函数内return之后的所有代码都不会运行;
return 不受循环约束,在循环体内执行 return,会直接退出整个函数
注意区分:break只跳出循环,不能退出函数;return直接退出整个函数
请看下面代码:
#include<stdio.h>voidtest(){printf("函数开始运行\n");return;// 直接终止函数,下方printf不会执行printf("这段代码永远无法执行\n");}intmain(){printf("main函数开始\n");test();printf("main函数继续执行\n");return0;// 结束main函数,程序退出}运行结果:
main 函数开始
函数开始运行
main 函数继续执行
a.return的使用规范
1.返回类型为void的函数,可不写return;当然也可以写return,但不能附带返回数值,只能按return ;这样书写,返回空语句
2.带有返回值的函数,正常应当使用return 数据;的形式返回匹配类型的值。
因为普通有返回值函数,如果代码执行结束仍没有执行return 值;,程序会触发未定义行为;
然而有一个特例,main函数允许省略末尾的 return,编译器会自动补充 return 0;
其他函数不享受该规则
