很多人在刚接触 C 语言,尤其是看到代码中出现 `break` 和 `continue` 语句时,心里可能会泛起一丝不安:这样做是不是不太好?会不会显得我功力不够?是不是有什么更“优雅”的写法?
其实,要回答这个问题,我们得先明白 `break` 和 `continue` 在 C 语言里到底是什么。
`break` 语句,顾名思义,就是“打断”。它被放在一个循环(比如 `for`, `while`, `dowhile`)或者一个 `switch` 语句里,作用是立即终止当前所在的这个代码块的执行,然后程序会跳到这个循环或 `switch` 语句之后的下一条语句继续运行。
`continue` 语句则稍微温和一些,它翻译过来是“继续”。它同样出现在循环里,但它不是直接跳出整个循环,而是跳过当前这次循环迭代中 `continue` 语句之后的所有语句,然后直接开始下一次循环的判断(如果是 `for` 循环,还会执行更新部分)。
那么,为什么会有人觉得这是“坏习惯”呢?
最常见的原因是,一些比较推崇“结构化编程”或者“函数式编程”理念的开发者,认为 `break` 和 `continue` 语句会破坏代码的自然流程,让程序的控制流变得比较“跳跃”,阅读起来没有那么直观。他们倾向于将所有的退出条件或跳过逻辑都放在循环的判断条件里,或者通过使用布尔变量来控制循环。
举个例子,如果你要在一个数组里找第一个负数,然后停止。
不用 `break` 的写法可能是这样的:
```c
int arr[] = {1, 2, 3, 4, 5, 6};
int found_negative = 0; // 一个标志变量
int i = 0;
while (i < sizeof(arr) / sizeof(arr[0]) && !found_negative) {
if (arr[i] < 0) {
found_negative = 1; // 设置标志,下次循环会停止
// 这里不继续处理,因为我们已经找到了
}
if (!found_negative) { // 只有没找到才打印,这是一个额外的检查
printf("%d ", arr[i]);
}
i++;
}
// 如果需要找到负数之后做什么,还需要在这里再检查 found_negative
```
而用了 `break`,代码就会变得清晰很多:
```c
int arr[] = {1, 2, 3, 4, 5, 6};
int i = 0;
while (i < sizeof(arr) / sizeof(arr[0])) {
if (arr[i] < 0) {
printf("Found negative number: %d
", arr[i]);
break; // 找到就直接退出循环
}
printf("%d ", arr[i]); // 没找到就打印
i++;
}
```
对比一下,你会发现,使用 `break` 的版本逻辑更直接。它明确地表达了“一旦满足这个条件,我就不需要再往下找了”。而没有 `break` 的版本,需要引入额外的变量来“告诉”循环什么时候该停,并且在循环体内部也需要更多的条件判断来处理“找到”和“未找到”的情况,反而让代码更复杂。
至于 `continue`,它通常用于在循环中跳过某些不满足特定条件的迭代。比如,你想打印一个数组中的所有正数,并跳过负数:
不用 `continue` 的写法:
```c
int arr[] = {1, 2, 3, 4, 5};
int i = 0;
while (i < sizeof(arr) / sizeof(arr[0])) {
if (arr[i] > 0) {
printf("%d ", arr[i]);
}
// else {
// // 什么也不做,隐式地跳过了负数
// }
i++;
}
```
用了 `continue` 的写法:
```c
int arr[] = {1, 2, 3, 4, 5};
int i = 0;
while (i < sizeof(arr) / sizeof(arr[0])) {
if (arr[i] < 0) {
i++; // 注意,如果在这里 continue,i 的自增一定要在 continue 前面,否则会死循环
continue; // 跳过负数,直接开始下一次循环
}
printf("%d ", arr[i]);
i++;
}
```
这里面有个小陷阱,就是 `i++` 的位置。如果 `continue` 放在 `i++` 之后,那每次遇到负数,`i` 就会自增,然后 `continue` 跳到下一次循环。如果 `continue` 放在 `i++` 之前,那么 `i` 就不会自增,会死循环。所以,使用 `continue` 时,你需要格外小心,确保循环的计数变量或迭代逻辑得到正确处理。
从这个角度看,确实需要小心处理 `continue`,但它本身不是“坏习惯”。关键在于你如何使用它,以及是否能让代码更清晰。
那么,到底是不是坏习惯呢?
在我看来,不是。
1. 清晰表达意图:在很多情况下,`break` 和 `continue` 能非常直接和清晰地表达程序员的意图。例如,“找到第一个匹配项就停下”,或者“遇到这种情况就跳过这次处理”。用 `break` 和 `continue` 来实现,代码读起来会非常顺畅,就像在阅读自然语言一样。
2. 避免复杂的嵌套和标志变量:正如上面的例子所示,避免使用 `break` 和 `continue`,往往需要引入额外的布尔变量或者更深的 `ifelse` 嵌套,这反而会让代码变得冗长、难以理解,并且容易引入逻辑错误。
3. 效率:虽然现代编译器很聪明,但有时候,过度的条件判断或者复杂的逻辑,即使最终效果一样,也可能在执行效率上略逊一筹。`break` 和 `continue` 往往能更有效地“剪枝”掉不必要的计算。
4. C 语言的特性:`break` 和 `continue` 是 C 语言(以及很多其他语言)内置的控制流语句,它们被设计出来就是为了解决这类问题。合理使用它们,是掌握这门语言的一部分。
什么时候需要警惕?
过度使用,形成“意大利面条代码”:如果你发现你的循环里充斥着各种 `break` 和 `continue`,并且它们之间相互交织,跳进跳出,那就需要停下来思考一下了。这通常意味着你的逻辑可能过于复杂,或者应该考虑将这段代码拆分成更小的函数,让每个函数承担单一的、清晰的责任。
掩盖了真正的问题:有时候,你可能用 `continue` 来跳过一些本该处理的错误情况,或者用 `break` 提前退出了一个本不该那么早退出的循环。这时,问题不在于 `break` 或 `continue` 本身,而是你对循环逻辑的理解有偏差。
影响可读性(主观):虽然我个人认为 `break` 和 `continue` 在很多时候能提高可读性,但也有一些人会觉得它们让代码“不够整洁”。在这种情况下,可以考虑是否能用更“结构化”的方式(比如在循环条件里)实现,但前提是不能牺牲代码的清晰度。
总结一下:
`break` 和 `continue` 是 C 语言中非常有用的控制流语句,它们是工具箱里必不可少的工具。说它们是“坏习惯”,更多的是一种过于绝对的简化。关键在于 合理、适度地使用。
当你感觉你的代码可以通过 `break` 或 `continue` 变得更简洁、更直观地表达逻辑时,大胆地去用。反之,如果看到一个循环里密密麻麻都是跳进跳出的语句,或者感觉 `break`/`continue` 只是在掩盖一个更深层次的逻辑问题,那就要反思了。
学习编程,最重要的是理解你所使用的工具,并用它们来编写清晰、可维护、能正确工作的代码。`break` 和 `continue`,在大多数场景下,都是达成这一目标的得力助手。