在 C 语言中,分支和循环语句是控制程序执行流程的重要工具,它们使得程序能够根据条件进行不同的操作或者重复执行某些代码片段。使用if、switch实现分支结构。使用for、while、do while实现循环结构。
1.if语句
1.1 if
if (表达式)
语句
if表达式两边的圆括号是必须的,不是表达式的内容。在执行if语句时,先计算圆括号内表达式的值,如果表达式的值非零,那么会接着执行圆括号后面的语句;如果表达式的值为零,则不执行。需要注意的是:不要混淆==(判等)运算符和=(赋值)运算符。if(i == 0)是判断 i是否等于零,而if(i = 0 )是把零赋值给i。
还要说明一点的是:在 if 的语句中,语句是一条语句,而不是多条语句,如果想用if 语句处理两条 或更多条语句,需要使用复合语句,即通过在一组语句周围放置花括号,强制编译器将其作为一条语句来处理。
1.2 else
if 语句可以有else 子句:如果圆括号内的表达式的值为0,那么就执行else 后边的语句。为了使程序更加容易阅读,把else和if 对齐排列在语句的起始位置,为语句增加花括号(即使有时并不是必需的)。同样需要注意的是:默认在 if 和 else 语句中都只控制一条语句,要使得 if 和 else 同时控制2条以上的语句,那就要使用{}将代码括起来。
1.3 嵌套if
在if else语句中,else语句可以与另一个if语句连用,构成多重判断。例如:要求输入一个整数,判断输入的整数是零,正数还是负数。
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if(num == 0)
printf("输⼊的数字是0\n");
else
if(num > 0)
printf("输⼊的数字是正数\n");
else
printf("输⼊的数字是负数\n");
return 0;
}
虽然第二个if 语句是嵌套在第一个if 语句内部的,但是通常不会对它进行缩进,而是把每个else 都与最初的if 对齐:
#include <stdio.h>
int main()
{
int num = 0;
scanf("%d", &num);
if(num == 0)
printf("输⼊的数字是0\n");
else if(num > 0) //这⾥的if 相当于嵌套在else语句中,形成了嵌套结构
printf("输⼊的数字是正数\n");
else
printf("输⼊的数字是负数\n");
return 0;
}
这样的安排带给if 语句独特的书写形式:
if (表达式)
语句
else if (表达式)
语句
...
else if (表达式)
语句
else
语句
这种缩进if 语句的方法避免了判定数量过多时过度缩进的问题,也证明了这组语句只是一连串的判定。
1.4 悬空else问题
如果又多个 if 和 else ,要始终记住:else 总是和最接近的 if 匹配 。首先看下面这段代码:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if(a == 1)
if(b == 2)
printf("hehe\n");
else
printf("haha\n");
return 0;
}
这段代码的运行结果是什么也不输出,最后的else实际上是和第二个 if 匹配的,这样的话 if...else 语句是嵌套在第一个 if 语句里面的,第一个 if 语句的判断条件不满足的话,后面的语句都不会执行,所以什么也不会输出。
为了使else 子句属于外层的if 语句,可以把内层的if 语句用大括号括起来:
#include <stdio.h>
int main()
{
int a = 0;
int b = 2;
if(a == 1)
{
if(b == 2)
printf("hehe\n");
}
else
printf("haha\n");
return 0;
}
只要带上适当大括号,代码的逻辑就会更加清晰,让代码的可读性更高。
2 switch语句
除了 if 语句外,从语言还提供了switch 语句来实现分支结构。在编程中,常常需要把表达式和一系列值进行比较,从中找出当前匹配的值,嵌套 if 语句可以达到这个目的,但可以使用 switch 语句将多重的else if 改成更易用、可读性更好的形式。此外,switch 语句往往比if 语句执行速度快,特别是在有许多情况要判定的时候。
2.1 switch语句的基本用法
switch 语句的最常用格式如下:
switch (表达式) {
case 常量表达式 : 语句
...
case 常量表达式 : 语句
default : 语句
}
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d", &day);
switch(day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("⼯作⽇\n");
break;
case 6:
case 7:
printf("休息⽇\n");
break;
}
return 0;
}
上述代码出现了break语句,并且出现了多个分支,下面介绍一下switch语句中break语句和case语句执行的顺序。
2.2 switch语句中的break
执行break语句会导致程序“跳”出switch 语句,继续执行switch 后面的语句,对控制表达式求值时,控制会跳转到与switch 表达式的值相匹配的case分支处。case语句分支只是一个说明switch 内部位置的标记。在执行完分支中的最后一条语句后,程序控制“向下跳转”到下一个分支的第一条语句上,而忽略下一个分支的case语句。如果没有break 语句(或者其他某种跳转语句),控制将会从一个分支继续到下一个分支。看下面这段代码:
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
switch(n%3)
{
case 0:
printf("整除,余数为0\n");
case 1:
printf("余数是1\n");
case 2:
printf("余数是2\n");
}
return 0;
}
各个分支语句里面去掉了break语句,输入数字7,输出结果不会是:余数是1,而是输出了两行:”余数是1“和”余数是2“,多出了”余数是2“,这是因为在找到case 1并执行其中的语句后,没有break语句,无法跳出switch语句,继续往下执行其他case语句。所以break语句在switch语句中是非常重要的,当然break语句也不是每个case语句都需要的,要根据实际情况调整。在2.1中的代码中,输入1-5,输出的都是”工作日“,输入6-7,则输出”休息日“,在这段代码中就利用了switch语句的特性,没有break语句就步跳出switch语句,而是一直往下面的分支语句的执行,直到遇到break语句或者所有的case语句全部执行完。
2.3 switch语句中的default
在2.1中,我们提到了default语句的位置不一定要放到最后,那么default语句是一个什么含义的语句呢?在使⽤ switch 语句的时候,switch 后的表达式中的值无法匹配代码中的 case 语句时,这时要不就不做处理,要不就在 switch 语句中加⼊default 子句。
switch (expression) {
case value1: statement
case value2: statement
default: statement
}
switch圆括号内的整型表达式的值如果不是value1,也不是value2的时候,就会执行default子句。
3. while循环
c 语言提供了三种循环语句,这节内容介绍while语句。
if 语句的形式和while语句的形式十分类似:
if(表达式)
语句;
while(表达式)
语句;
但 if 语句只会执行一次,而while语句则会根据判断条件执行多次,达到循环效果。while后面的圆括号中的表达式是判断表达式,圆括号后面的语句是循环体。执行while 语句时,首先计算判断表达式的值。如果值不为零(即真值),那么执行循环体,接着再次判定表达式。这个过程(先判定判断表达式,再执行循环体)持续进行直到判断表达式的值变为零才停止。如果控制表达式的值始终非零,while 语句将无法终止。如while(1) ... 就构成了无限循环,如果循环体中不含有跳出循环控制的语句(break 、goto 、return)或者调用了导致程序终止的函数,否则上述形式的while 语句将永远执行下去。
4. do-while循环
do-while循环的格式如下:
do
语句;
while(表达式);
do-while循环和while循环关系紧密,do-while循环的本质就是while语句,只不过是其判断表达式每次都是在循环体执行完进行判定,所以do-while语句中的循环体是至少执行一次的。
5. for循环
for循环是c语言三种循环中使用最多也是最强大的一种循环,非常适用于使用”计数“变量的循环中,for语句的格式如下:
for(表达式1 ; 表达式2 ; 表达式3)
语句;
表达式1用于循环变量的初始化,表达式2用于循环结束条件的判断,表达式3用于循环变量的调整。for循环的执行流程如下:
首先执行表达式1 初始化循环变量,接下来就是执行表达式2 的判断部分, 表达式2 的结果如果==0,则循环结束; 表达式2 的结果如果!=0则执⾏循环语句,循环语句执行完后,再执⾏表达式3 ,调整循环变量,然后再去表达式2 的地方执⾏判断, 表达式2 的结果是否为0,决定循环是否继续。
整个循环的过程中,表达式1初始化部分只被执行1次,剩下的就是表达式2、循环语句、表达式3在循环。
for语句和while语句关系紧密,除了一些极少数的情况以外,for 循环总可以用等价的while 循环替换,下面是while循环和for循环的对比:
for和while在实现循环的过程中都有初始化、判断、调整这三部分,但是for循环的这三部分非常集中,便于维护,而while循环这三部分就比较分散,从形式上看,for循环更优一些。
还需要说明的一点是将上述代码的for 循环示例中的i++用++i 来替换,这种做法对循环没有任何影响,因为for 语句中第一个表达式和第三个表达式都是以语句的方式执行的,所以它们的值互不相关。
for 语句远比目前看到的更加灵活,通常for 语句用三个表达式控制循环,但是有一些for 循环可能不需要这么多,可以省略任意或全部的表达式。如果省略第一个 表达式,那么在执行循环前没有初始化的操作。如果省略了for 语句中的第三个 表达式,循环体需要确保第二个表达式的值最终会变为假。当for 语句同时省略掉第一个 和第三个 表达式时,它和while 语句没有任何分别。如果省略第二个表达式,那么它默认为真值,因此for 语句不会终止(除非以某种其他形式停止)。
6. 退出循环
6.1 break
有些时候需要在循环中间设置退出点,甚至可能需要对循环设置多个退出点。break 语句可以用于有上述这些需求的循环中。前面已经讨论过break 语句把程序控制从switch 语句中转移出来的方法。break 语句还可以用于跳出while 、do 或for 循环。break的作用是用于永久的终止循环,只要break被执行,就会直接跳出循环。要注意的是:break 语句把程序控制从包含该语句的最内层 while 、do 、for或switch 语句中转移出来,当这些语句嵌套时,break只能跳出一层。
看下面这个break例子:
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
break;//当i等于5后,就执⾏break,循环就终⽌了
printf("%d ", i);
i = i+1;
}
return 0;
}
执行的结果为:1 2 3 4,当i等于5之后,循环在break的地方终止,不再循环。只要break被执行,break外的第一层循环便终止了。
6.2 continue
除了break语句外,还会看到continue语句和goto语句,continue语句也能够跳出循环,但它只能跳出本次循环中continue之后的代码,但是不会跳出整个循环。goto 语句允许程序从一条语句跳转到另一条语句。
break语句刚好把程序控制转移到循环体末尾之后 ,而continue 语句刚好把程序控制转移到循环体末尾之前 。用break 语句会使程序控制跳出循环,而continue 语句会把程序控制留在循环内。
再看下面continue的例子:
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
if(i == 5)
continue;
printf("%d ", i);
i = i+1;
}
return 0;
}
这次运行的结果则是在输出1 2 3 4后,程序便陷入了死循环,原因是当i等于5后,便会执行continue,而执行continue语句后,便会跳过continue之后的代码语句,从而跳过执行i= i + 1 ,所以i的值便一直为5,从而陷入死循环。
而在for循环中,break也是用于终止循环的,continue的作用是跳过本次循环中continue后的代码,直接去到循环的变量调整部分。看一下下面这段代码:
#include <stdio.h>
int main()
{
int i = 1;
for(i=1; i<=10; i++)
{
if(i == 5)
continue;
printf("%d ", i);
}
return 0;
}
这次运行的结果则是:1 2 3 4 6 7 8 9 10,并没有和上面的while循环一样陷入死循环,原因就是while循环中的变量调整在continue之后的代码中,而for循环的变量调整则是在continue执行完继续要执行的,并没有被跳过。
6.3 goto
break 语句和continue 语句都是跳转语句,但它们都是受限的,而goto语句则可以跳转到同一个函数中任何有标号的语句处。例如:
#include <stdio.h>
int main()
{
printf("hehe\n");
goto next;
printf("haha\n");
next:
printf("跳过了haha的打印\n");
return 0;
}
如果C语言没有break 语句,可以用goto 语句提前退出循环。goto语句如果使用不当,就会导致在函数内部随意跳转,打乱程序的执行流程,所以能不用尽量不用。但如果有多层循环,如果想快速跳出,使用goto语句就很方便了。
for(...)
{
for(...)
{
for(...)
{
if(disaster)
goto error;
}
}
}
error:
//...
上面的代码有三层for循环,想要提前退出就要使用break语句,但一个break只能跳出一个for循环,有3层for循环,就要使用3个break语句才能跳出循环。在这种情况下,使用goto语句便可以快速跳出循环。