拒绝死循环:大一新生C语言分支与循环语句生存指南
 {
printf("优秀,奖励自己一杯奶茶\n");
} else if (score >= 60) {
printf("及格,勉强维持生命体征\n");
} else {
printf("挂科预警,准备重修费\n");
}
核心避坑指南:悬空 else 问题 这是新手最容易踩的坑之一。当 if 嵌套时,else 到底跟谁走?C语言的规则是:else 总是与最近的、未配对的 if 匹配。
int a = 1, b = 2;
if (a == 2)
if (b == 2)
printf("a和b都是2\n");
else
printf("a不是2\n"); // 你以为这是外层if的else?错!它属于内层if
为了防止这种”渣男/渣女”行为,强烈建议:只要涉及分支,永远加上大括号 {}。这不仅是规范,更是保命符。
switch 开关:精准打击的利器
当你面临多个固定值的判断时,if-else if 会显得冗长,这时 switch 就是你的最佳选择。
int day = 3;
switch (day) {
case 1:
printf("周一,痛苦的开始\n");
break; // 千万别忘了break!
case 2:
printf("周二,还在缓冲\n");
break;
case 3:
printf("周三,小周末\n");
break;
default:
printf("其他日子,继续熬\n");
break;
}
核心避坑指南:穿透效应 (Fall-through) switch 语句中,如果没有 break,程序会从匹配的 case 开始,一路向下执行所有代码,直到遇到 break 或大括号结束。这被称为”穿透”。除非你刻意利用这个特性(比如多个 case 执行相同逻辑),否则每个 case 后面必须老老实实写上 break。另外,case 后面的标签必须是整型常量表达式,不能是变量。
逻辑与条件操作符:让判断更优雅
写分支语句离不开各种操作符,这里我们重点聊聊那些容易让你”翻车”的细节。
关系操作符:别把赋值当判断
关系操作符包括 ==, !=, >, <, >=, <=。
千古恩怨:== 与 = == 是判断相等,= 是赋值。新手最常犯的错误就是把 if (x == 3) 写成 if (x = 3)。在C语言中,x = 3 这个表达式的值是 3(非0即为真),所以这个判断永远为真,而且还会把 x 的值强行改成 3。
防御技巧:养成习惯,把常量写在左边,写成 if (3 == x)。如果你不小心写成了 if (3 = x),编译器会直接报错,因为常量不能被赋值。
连环坑:关系运算符不宜连用 数学里我们写 1 < x < 10 觉得很正常,但在C语言中,1 < x < 10 的实际执行顺序是 (1 < x) < 10。(1 < x) 的结果是 0 或 1,然后再和 10 比较,结果永远为真。正确写法是 x > 1 && x < 10。
逻辑操作符与短路特性
逻辑操作符有三个:! (非), && (与), || (或)。
这里有一个极其重要且面试常考的概念:短路特性。
•&&:如果左边的表达式为假,右边的表达式根本不会执行。
•||:如果左边的表达式为真,右边的表达式根本不会执行。
int a = 0, b = 5;
if (a != 0 && b++ > 0) {
// 因为 a!=0 为假,b++ 永远不会执行,b依然是5
}
利用短路特性,我们可以写出非常安全的代码,比如 if (ptr != NULL && ptr->data > 0),如果指针为空,就不会去访问 data,从而避免了程序崩溃。
条件操作符(三目操作符)
expr1 ? expr2 : expr3 是 if-else 的极简版。当逻辑简单且需要返回值时,用它能让代码更紧凑。
*int max = (a > b) ? a : b;* // 优雅地取最大值
循环三剑客:让程序学会”重复干活”
计算机最擅长的就是不知疲倦地重复。C语言提供了三种循环语句,各有千秋。
while 循环:先判断再执行
int i = 0;
while (i < 10) {
printf("%d ", i);
i++;
}
先判断条件,条件为真才执行循环体。如果初始条件就不满足,循环体一次都不会执行。
for 循环:结构最严谨的打工人
for(初始化; 判断; 调整) 将循环的三个核心要素集中在一行,逻辑清晰,是实际开发中最常用的循环。
// 标准写法
for (int i = 0; i < 10; i++) {
printf("%d ", i);
}
扩展知识:for 的花式玩法
1.C99 标准允许在初始化部分声明变量(如上例),变量的作用域仅限于循环内部,非常安全。
2.省略表达式:for(;😉 等同于 while(1),是一个死循环。
3.逗号表达式:for(i=0, j=10; i<j; i++, j–) 可以同时控制多个变量,常用于数组的首尾遍历。
do-while 循环:先斩后奏
int i = 0;
do {
printf("%d ", i);
i++;
} while (i < 10);
先执行一次循环体,再判断条件。无论条件是否成立,循环体至少执行一次。这非常适合用于”菜单选择”等场景,因为菜单至少要显示一次让用户选。
扩展:三种循环怎么选?
•次数明确:首选 for。
•次数不明确,可能一次都不执行:选 while。
•次数不明确,但至少执行一次:选 do-while。
break 和 continue:循环中的”刹车”和”跳过”
在循环中,我们需要更精细的控制权。
•break:永久终止整个循环,直接跳到循环体外的下一条语句。
•continue:跳过本次循环剩余的代码,直接进入下一次循环的判断。
高能预警:continue 在 for 和 while 中的行为差异 这是考试和面试的绝对重点!在 while 中,continue 会直接跳到条件判断处。但在 for 循环中,continue 会先执行表达式3(调整部分),然后再去判断条件。
for (int i = 0; i < 5; i++) {
if (i == 2) continue;
printf("%d ", i); // 输出:0 1 3 4
}
理解了这个差异,你就超越了 80% 的新手。
循环嵌套:套娃的艺术
循环里面套循环,就是嵌套。最经典的例子就是打印图形、九九乘法表,以及寻找素数。
// 寻找 100-200 之间的素数
for (int i = 100; i <= 200; i++)
{
int is_prime = 1; // 假设它是素数
for (int j = 2; j < i; j++)
{
if (i % j == 0)
{
is_prime = 0; // 能被整除,不是素数
break; // 找到因子就跳出,没必要继续试了
}
}
if (is_prime)
{
printf("%d ", i);
}
}
性能优化提示:在找素数的例子中,内层循环其实不需要试到 i-1,只需要试到 sqrt(i) 即可。这就是算法优化的魅力,同样的逻辑,时间复杂度天壤之别。
随机数与实战:猜数字游戏
学了这么多,我们来做一个真正能玩的游戏。C语言通过 rand() 函数生成伪随机数,但它每次生成的序列是一样的,需要用 srand() 配合 time() 设置随机种子。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game() {
int guess = 0;
int target = rand() % 100 + 1; // 生成 1~100 的随机数
while (1) {
printf("请输入你猜的数字: ");
scanf("%d", &guess);
if (guess < target) printf("猜小了\n");
else if (guess > target) printf("猜大了\n");
else {
printf("恭喜你,猜对了!\n");
break;
}
}
}
int main() {
srand((unsigned int)time(NULL)); // 用时间戳做种子
int input = 0;
do {
printf("1. 开始游戏 0. 退出游戏\n");
scanf("%d", &input);
switch (input) {
case 1: game(); break;
case 0: printf("下次再来玩\n"); break;
default: printf("输入错误,请重新选择\n");
}
} while (input);
return 0;
}
这个程序完美融合了 do-while 菜单控制、switch 分支、while 游戏循环以及随机数生成,是检验分支循环掌握程度的最佳试金石。
goto 语句:最后的”核武器”
goto 是一个充满争议的语句。在绝大多数现代编程规范中,它被明令禁止,因为它会破坏程序的结构化,让代码变成难以维护的”意大利面条”。
但是,在极少数特定场景下,它依然有存在的价值。比如在 Linux 内核和 SQLite 的源码中,goto 被集中用于统一的错误处理和资源释放。当函数中有多层嵌套和多次内存分配时,如果中途出错,用 goto 跳转到统一的清理标签,比层层 return 或嵌套 if 要清晰得多。
对于大一新生,请记住:知道它的存在,理解它的危害,但在你成为顶级系统程序员之前,永远不要用它。
扩展知识总结:常见面试坑与最佳实践
为了让你在期末和未来的面试中游刃有余,这里总结几条黄金法则:
1.永远不要省略大括号:哪怕只有一行代码。无数安全漏洞(如著名的苹果 goto fail 漏洞)都是因为少写了大括号。
2.常量放左边:if (NULL == ptr) 优于 if (ptr == NULL)。
3.警惕浮点数比较:不要直接用 == 比较两个浮点数,由于精度问题,应该判断它们的差值是否在一个极小范围内(如 fabs(a - b) < 1e-6)。
4.循环边界条件:写 for 循环时,永远想清楚是 < 还是 <=,数组越界是C语言最常见的崩溃原因。
5.避免死循环:确保循环体内有改变条件的语句,且该语句一定会被执行到(注意 continue 的影响)。
6.switch 必须有 default:即使你认为所有情况都覆盖了,也要加上 default 并做适当处理(如报错或断言),这是防御性编程的基本素养。
结语
分支和循环,赋予了程序逻辑的灵魂。从这一刻起,你写的不再是死板的指令序列,而是能够思考、能够适应、能够解决复杂问题的智能体。
这篇博客涵盖了课件中的核心知识点,但纸上得来终觉浅。C语言是一门必须”敲”出来的语言。把文中的代码自己敲一遍,故意改错看看编译器的反应,尝试修改猜数字游戏的难度,在循环嵌套中打印出更复杂的图形。
不要害怕报错,每一个红色的 Error 都是你通往高手之路的垫脚石。祝各位大一新生在C语言的海洋里乘风破浪,永不 Segmentation Fault(段错误)!
336



被折叠的 条评论
为什么被折叠?



