7.9 用break与continue语句来控制循环

如果想从嵌套较深的结构里面跳出来,除了使用goto,其实还可以使用C语言所提供的另外两种机制。这两种机制就是break与continue,它们的功能与goto类似,但是更加规范。

break语句会让程序跳转到该语句所在的这个语句块之外,continue语句则用在循环体里面,它会让程序跳过这条语句与当前这轮循环的末尾之间的所有语句,并立刻进入下一轮循环。

我们在上一章讲switch语句时其实用过break,那时是为了让程序立刻跳出switch结构,并从该结构之后的那条语句开始继续往下执行。其实break也可以用在循环结构里面,用来跳出它所在的整个循环。

下面这个判断受测数字是否为素数(也称为质数)的isPrime()函数就用到了break语句,它的每一轮循环都会判断受测数字num是否能为当前的计数器i所整除,如果能,那就表明num不是素数,于是就通过break跳出整个for循环。

这个isPrime()函数写在primes.c程序文件里面:

刚才这个演示break语句用法的例子是相当简单的。本来我们在发现num可以为计数器所整除时只需要让程序通过return false;语句直接从函数中返回就好,但那样就没办法演示break语句的意义了。我们这样举例是想让大家看到,break不仅能用来跳出switch结构,而且能够跳出for这样的循环结构,让程序从整个循环结束之后的那语句(也就是紧跟在循环体末尾那个右花括号之后的那条语句)开始继续往下执行。

与break语句不同,continue语句只能用在循环结构的循环体里面。它会让程序跳到本轮循环体即将结束之前的那一点(也就是循环体的右花括号之前那一点),然后准备执行下一轮循环。当然,程序还是得先判断循环条件是否为true,如果是,才会执行下一轮循环。

比方说,我们想分别统计出1~N的所有素数之和与所有非素数之和。为此,我们可以分别编写相应的函数,并在函数的循环体里面调用刚才写的isPrime()函数,以判断当前这个数是不是素数。在统计所有素数之和的时候,如果发现当前这个数字不是素数,那我们就不用再继续考虑它了,而是可以直接开始判断下一个数字。下面就是统计所有素数之和的sumPrimes()函数,这个函数写在primes.c程序文件里面:

与这个函数类似,我们还可以写一个sumNonPrimes()函数以统计所有的非素数之和,这个函数也写在primes.c程序文件中:

用continue语句跳过本轮循环时,一定要记得更新计数器的值,而不要让continue语句把调整计数器的取值所用的操作也跳过去。忘记更新计数器的取值容易导致无限循环(也就是“死循环”)。

刚才写的三个函数演示了break与continue语句的用法,现在我们把这三个函数纳入prime.c程序,该程序的main()函数要做三件事。第一,通过for()...循环调用isPrime()函数,简单地验证一下这个函数的功能是否正确。第二,调用sumPrimes()函数并把调用结果通过printf()打印到控制台。第三,调用sumNonPrimes()函数并把调用结果通过printf()打印到控制台。如果整个程序的逻辑全都没有错误,那么1~100的所有素数之和与所有非素数之和相加就应该跟早前那个求和程序所给出的结果一致(也就是5050)。我们可以通过这一点来验证prime.c程序是否正确。这个程序的main()函数是这样写的:

创建名为prime.c的文件,把main()函数以及早前写的isPrime()函数、sum-Primes()及sumNonPrimes()函数的代码录入该文件。然后执行cc primes.c -o primes命令编译程序,最后运行程序。你应该会看到类似下面这样的输出信息。

通过刚才的截图可以看到,我们首先用1~7的整数来验证isPrime()函数实现得是否正确。然后,我们打印1~100所有的素数之和与非素数之和,大家不仅能够看到求和结果,而且还能看到素数与非素数这两部分之中的每一个具体数字,我们也可以通过这些数字来验证这个程序是否正确。这两个求和结果(也就是1060与3990)加起来跟1~100的所有整数之和(也就是5050)相等。

下面我们通过一段模板代码来对比break、continue、return与goto这四种语句:

这段代码有两层for()...循环。break语句只会跳到离它最近的那个代码块之外,具体到这个例子来看,就是跳到内层for循环的循环体之外,并开始执行其后的那条语句(那条语句位于外层for循环的循环体中)。continue语句会让程序跳到离该语句最近的那一层循环体即将结束前的那一点,并准备执行下一轮循环。goto ERROR语句会让程序跳转到函数体末尾的ERROR:标签,使得程序能够在该函数返回之前,先处理错误。ERROR:标签上方有一条return 0语句,这样写的意思是,如果函数在执行过程中没有遇到错误,那么就会在这里正常地返回,而不会执行ERROR:标签下方的错误处理逻辑。