第 10 讲:跳转结构
还记得前文学到的质数判断和嵌套循环吗?前文遇到了一个例子,我们尝试跳出循环,但发现知识不足以让我们在期间跳出循环的执行,所以我们在那个时候无能为力,好在这个思路并不会使得代码的逻辑变成错的,它仅仅涉及到代码执行的优化。
今天的内容,我们将会处理这些特殊的处理。我们一共要学习三种语句模式:“break
语句”、“continue
语句”和“标签 + goto
语句”模型。
break
语句:跳出循环
我们省略了一些代码,只关注于循环语句,和 break
语句。break
语句是单独有分号结尾的,否则它将只是一个关键字。
在执行循环的时候,如果发现它已经不是质数了,就没必要再往下执行后面的判断是否除尽的逻辑了,于是在这里就跳出循环,所以写在应当跳出循环的位置上。
很多小伙伴在初学这个语句的时候,以为它可以跳出
if
条件。请注意,break
语句是只跟包含它的这层循环有关,它仅能跳出一层循环。也就是说,如果有两个while
循环,如果把break
语句写在最里面,那么它也只能跳出最内层的循环,而靠外的这一层循环依旧会继续往下执行,比如原本的例子:
这个
break
语句只能让语句跳转到第 14 行继续执行,所以下方的isPrime
的判断条件它照样要走。所以这么考虑起来,这个break
语句的跳转逻辑是没有问题的。
continue
语句:执行下一次循环
要说 break
语句是跳出循环,那么和它写法类似的 continue
语句就不是这个行为了。continue
语句直接终结当前这一次循环的执行逻辑,直接跳转到循环的开头去判断条件(而 for
循环则跳转到增量处)。
上述示例给出了一个从 1 到 10 的循环。当循环执行的过程中发现 n 本身被 3 整除,就会自动跳转到 n++
增量语句处执行,而不会遇到 printf("%d ", n);
语句。这个示例展示了如何输出不是 3 的整倍数的代码。
它和 break
语句不一样的地方在于,continue
不会退出代码,而是跳转到增量(for
循环)或条件(while
或 do-while
循环)处执行。
goto
语句:跳转到任意的执行位置
用 goto
代替循环
最为灵活的语句非 goto
语句莫属,它可以跳转到程序的任意你想要到达的位置上去(当然,在 main
函数外面去是不可以的)。
考虑如下行为。如果我们没有循环的时候,我们可以尝试使用 goto
来达到循环的目的。
上述示例将会输出从 1 到 100 内的数字的和。当执行到条件满足时,将会自动统计结果,并跳转到 Loop
标签处(第 3 行)继续执行代码。直到条件不成立的时候,到 else
部分,输出结果,此时没有 goto
语句,所以会继续往下执行,而不会再返回上面了。
请注意,标签名称的定义依然满足标识符的命名规则(数字、字母、下划线的那些规矩),所以不要乱取名。而且,标签不能位于函数的外面。比如下面的这个示例,就会出现错误:
这段代码是会报错的,不是运行的时候出错,而是直接在运行和编译程序之前就会出现错误。
你叫它标签?
是的,在前文里,使用标识符规则加一个冒号的部分我们称为标签(Label)。标签并非任何执行语句,它没有意义,跟注释差不多。但和注释的区别在于,注释在运行的 exe 里不会出现,而标签你可能还会查到这个东西存在的影子。
标签可以控制程序运行的顺序和行为,比如上面正确的那一则例子,它可以控制语句形成一个循环,来达到模拟循环的效果。而且,在前文介绍的 switch
语句也大量使用到了标签。仔细回忆一下,switch
语句配合 case
语句使用,而 case
语句末尾就是用的冒号。是的,case
语句就是一个特殊的标签,它用于规定 switch
小括号里的表达式的结果的分情况处理行为。当都不成立的时候,语句将会跳转到 default
标签处执行。
但可以发现,它并不影响程序的真正运行规范(比如大括号并不会因为标签的行为而断开和分割开),所以请注意,像是 switch
语句或是上面的这样的例子里,我们不能在同一个范围里定义两个名字完全一样的变量。比如说
或是
两个例子里,变量在错误的代码处是完全可以看到第一行给出的变量的,所以在可以看到的地方重新定义这些变量是有语义冲突的。
不过,C 语言允许第 2 种情况的代码,意味着这个代码不会出错,但错误很隐蔽:在
case 3
和case 5
标签处各定义了一个i
变量,而这两处的i
和第 1 行定义的i
并不是同一个变量,而是两个完全没有关系的变量,只是恰好这里同名了。我们应尽量避免这种定义行为。