C语言核心基础:彻底搞懂分支与循环语句
2026/9/15 2:05:01 网站建设 项目流程

1. 为什么要死磕“分支和循环”

C语言学到第三讲,大部分人已经能把“输入、计算、输出”跑通了,也就是从上到下把代码写一遍,运行一下,结果出来。但你会发现,这种能力离“写程序”还很远——因为真实的程序从来不是一条直线往下走的。

举个例子,你写一个“判断成绩等级”的小工具:90分以上是A,80到89是B,70到79是C,60到69是D,60以下是不及格。如果你只会顺序执行,这个需求基本没法做。你得根据不同的分数走不同的逻辑,这就是“分支”。再比如,你要打印1到100的和,总不能写100次加法吧。你得让同一段代码反复执行,这就是“循环”。

说句实话,C语言里真正决定一个人基础扎不扎实的,不是指针,也不是内存管理,恰恰就是这两个东西:分支和循环。指针没学好,你写不了复杂代码。但分支循环没学好,你连复杂代码的门都摸不到。很多人在学指针的时候一脸懵,回头一看,其实是前面的分支逻辑和循环边界都没理清楚。

这一讲只聊“上”,也就是分支和循环的最核心部分:if / else、switch、while、for。不整花活,不搞嵌套地狱,就把最常用的写法和最容易踩的坑讲透。适合刚学完顺序结构、准备进阶的初学者,也适合那些写了一年C语言但老觉得代码不够清爽的人——很多时候,回头补基础比学新东西更有用。

2. 分支语句:让程序学会“看情况办事”

2.1 if / else 的基本姿态

C语言里最常用的分支手段就是if。它的语法结构几乎是个人都能看懂:

if (条件) { // 条件为真时执行 } else { // 条件为假时执行 }

但越是简单的东西,越容易在细节上翻车。我见过太多新手写出这样的代码:

if (a > 0) printf("正数"); else printf("非正数");

这段代码语法没错,也能跑。但如果你后面加代码,很容易加错位置:

if (a > 0) printf("正数"); printf("我要打印两行"); // 这行和 if 没关系! else printf("非正数");

这直接编译不过,因为else前面只有一个语句受if控制,printf("我要打印两行")夹在中间根本找不到配对。就算你给它换行、缩进,也只是让错误更隐蔽。

所以我一直建议:无论if、else、while还是for,只要后面有执行体,一律加大括号。哪怕里面只有一行代码,大括号也写着。这不是强迫症,是为了让你三个月后回来看代码时,不会因为缩进混乱而搞不清逻辑。这个习惯越早养成越好,到了企业里写多分支业务逻辑,一个不小心就会埋雷。

2.2 else if 是代码里的“分诊台”

现实问题很少是非黑即白。成绩分ABCD,交通灯分红黄绿,菜单分1234。这时候你要写多个分支。

新手第一反应是这么写:

if (score >= 90) printf("A"); if (score >= 80) printf("B"); if (score >= 70) printf("C"); if (score >= 60) printf("D"); if (score < 60) printf("不及格");

跑起来好像没问题。但你想想,如果score是95,第一个if就把A打印出来了,后面的if还会继续判断,等于白白浪费了四次比较。更麻烦的是,一旦每个if里不只是printf,而是几十行代码,程序就会同时执行多个分支,结果完全错乱。

正确写法是把它们串起来:

if (score >= 90) { printf("A"); } else if (score >= 80) { printf("B"); } else if (score >= 70) { printf("C"); } else if (score >= 60) { printf("D"); } else { printf("不及格"); }

注意这里的顺序非常关键:90的判断必须在前,然后逐渐往下放低标准。你把顺序写成else if (score >= 60)在前,那60分的人会先走进D,后面全被跳过。分支顺序就是逻辑覆盖范围的大小区间,必须先小后大,先窄后宽。这个用“分诊台”类比最好理解——先看最急的重症,再看普通号。

2.3 switch 的打开方式

当你的分支判断是基于“某个变量的值等于多少”时,switch通常比一堆else if更清爽。

switch (day) { case 1: printf("周一"); break; case 2: printf("周二"); break; case 3: printf("周三"); break; default: printf("未知"); break; }

switch的核心机制是“跳转”:算出day的值后,直接跳到对应case开始执行。如果你不在每个case末尾写break,程序不会自己停在case边界,而会继续往下穿——这叫“贯穿”(fall-through)。

有些时候贯穿是故意用的。比如你要统计“元音字母数量”,可以把a、e、i、o、u放到同一个分支里:

switch (ch) { case 'a': case 'e': case 'i': case 'o': case 'u': count++; break; default: break; }

这是常规操作,没问题。但是对那些“顺手忘掉break”的新手来说,case贯穿就成了最常见的逻辑陷阱。一个原则:如果你没打算贯穿,case后面必须写break。一旦哪天你发现某个case多走了几步,先检查break是不是漏了。

另外要说一个细节:switch里的case后面必须跟着常量表达式,不能写case score > 60:这种范围判断。C语言的switch天生不适合做范围判断,想做范围和多个条件的组合,老老实实用else if。别拿switch硬撑场景,工具选错了,后面全是坑。

2.4 三目运算符:分支的“速写版”

如果你只是想根据条件给变量赋个值,用三目运算符会非常省事:

max = (a > b) ? a : b;

这行的意思是:如果a大于b,max等于a,否则等于b。三目运算符的优点是表达紧凑,缺点是嵌套多了根本没法读。

我见过有人写出这样的代码:

max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);

三四个三目嵌套在一起,说句难听的,写的人过两天自己都看不懂了。你的代码是给人看的,不是拿来炫技的。三目运算符只适合单层、简单的赋值场景。一旦发现需要嵌套,立刻换成if去写,逻辑清晰比代码短重要得多。

3. 循环语句:让程序“转起来”

3.1 while:先想条件,再执行

while是最直观的循环:先判断条件,条件成立就执行循环体,然后回来继续判断。只要条件永远成立,它就一直循环。所以while特别适合处理“不知道要循环多少次,但知道什么时候该停”的场景。

int n = 12345; int count = 0; while (n > 0) { n /= 10; count++; } printf("%d", count);

比如上面这段,统计整数n的位数,每次除以10就少一位,直到变成0。你不需要提前知道12345有几位,循环自己会停。这就是while的典型用法。

但while有个著名的问题:如果条件一开始就不满足,循环体一次都不会执行。这不算bug,但有时候会成为隐患。比如:

int n = 0; while (n > 0) { // 这段根本不会执行 }

如果你是先想着“把这段逻辑循环执行到最后”,后来发现边界值恰好让条件一开始就不成立,那你的处理逻辑就被跳过了。所以用while前,务必先问自己:条件一开始为假时,这段代码应不应该执行?如果应该,那就别用while,考虑do-while。

3.2 do-while:无论如何先做一次

do-while和while的唯一区别是:do-while先执行一次循环体,再判断条件。这就保证循环体至少会执行一次。

适合的场景很典型——用户交互验证。比如菜单选择,你得先让用户输入一次,然后判断输入对不对,不对再重来:

do { printf("请输入1到4之间的数字:"); scanf("%d", &choice); } while (choice < 1 || choice > 4);

如果换成while,你就得在循环前面先写一次scanf,然后在循环里面再写一次scanf,让人感觉重复。do-while直接抹掉了这个别扭。记住这个判断标准:这段逻辑无论条件是否成立,至少应该执行一次吗?是,就用do-while;不是,就用while。

3.3 for循环:把“初始化、条件、步进”写在一行

三个循环里,for是使用率最高的。它的语法结构是:

for (初始化; 条件; 步进) { // 循环体 }

比如打印1到10:

for (int i = 1; i <= 10; i++) { printf("%d\n", i); }

for循环的好处在于把循环的三个关键要素集中到了一行,让人一眼就看清楚循环从哪里开始、到哪里结束、每次怎么变化。而while则要把这三个要素拆开放在不同位置,读起来相对费劲。

很多新手不理解为什么for比while“高级”,其实不是高级,是for更不容易忘东西。三个要素放在一起,你写的时候自然知道该初始化、该写条件、该写步进。而while你只写了while (i <= 10),经常忘记在循环体里写i++,于是死循环就来了。

在条件判断上,我建议用i < n而不是i <= n。不是绝对正确,但i < n的区间是半开区间[0, n),在很多算法、容器遍历的场景里,这个写法能直接避免越界问题,也方便和数组下标对齐(数组下标就是0到n-1)。初学者养成for (int i = 0; i < n; i++)的惯性写法,后面接触数组和指针时,会舒服很多。

3.4 break 和 continue 的正确使用姿势

break在switch里是用来跳出分支的,在循环里则是用来“提前终止循环”。continue和break不同:continue不是退出循环,而是跳过本次循环剩余语句,直接进入下一次判断。

举个例子:打印1到10之间所有偶数。

for (int i = 1; i <= 10; i++) { if (i % 2 != 0) { continue; // 奇数直接跳过,不打印 } printf("%d\n", i); }

而break是找到某个目标后就停。比如:从一个数组里找第一个大于100的数。

for (int i = 0; i < n; i++) { if (arr[i] > 100) { printf("找到:%d", arr[i]); break; } }

这里有几个地方需要特别注意。continue在for循环里会先执行步进,再判断条件,所以使用for循环时continue没什么大问题。但如果你用while循环,continue容易造成死循环。看一下这段:

int i = 0; while (i < 10) { if (i == 5) { continue; // 死循环!没有执行i++ } i++; }

当i等于5时,continue跳过了下面的i++,然后回到while条件判断,i还是5,于是又进入循环,又continue,永远卡死。这种问题在初学者里非常常见,排查起来也特别费劲。我的建议是:能用一个变量统一迭代逻辑的,优先用for循环。除非场景特殊,否则while+continue的组合等于给自己挖坑。

4. 几个必须能独立写出来的经典场景

分支和循环学得怎么样,不是看背了多少语法,而是看能不能拿它解决实际问题。下面这三个场景,是C语言里极其经典的入门题,也是很多面试题的雏形。我建议你打开编译器,每道题都亲手敲一遍。

4.1 判断闰年

闰年的规则是:能被4整除但不能被100整除的年份是闰年;能被400整除的年份也是闰年。用代码写:

#include <stdio.h> int main() { int year; scanf("%d", &year); if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { printf("%d是闰年\n", year); } else { printf("%d不是闰年\n", year); } return 0; }

这个题考的是两件事:一是逻辑运算的优先级,&&的优先级高于||,所以括号可以不用全加,但加上括号更稳妥;二是对数学规则的代码化理解。能把这题写对,说明你的if和逻辑运算掌握得不错。

很多初学者在这里会犯一个错:把year % 100 != 0漏掉,直接写成year % 4 == 0 || year % 400 == 0。那结果就是1900年也会被当成闰年,因为1900能被4整除。可实际上1900不是闰年,少了最关键的限制。

4.2 统计一个数字中某数字出现的次数

比如输入一个长整数n,统计其中数字7出现的次数。这种题是while循环的标准练习,核心思想是用取模和整除把每一位拆出来:

#include <stdio.h> int main() { long long n; int target = 7; int count = 0; scanf("%lld", &n); if (n == 0) { count = (0 == target) ? 1 : 0; } else { while (n != 0) { if (n % 10 == target) { count++; } n /= 10; } } printf("数字7出现%d次\n", count); return 0; }

这里给n加了if (n == 0)的判断,是因为数字0的个位是0,但while循环在n=0时一次都不进,直接输出0次,逻辑不对。这正好呼应前面说的:while条件一开始为假时,循环体不会执行,你必须要考虑这种情况是否被允许。处理数字位数的题目是while循环的灵魂场景,刷几道就理解透了。

4.3 猜数字小游戏

这个题目实在经典,通过生成一个随机数,让用户反复猜,大了提示大,小了提示小,直到猜中。代码里同时用到了循环和分支,还有break控制退出。

#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int answer, guess; srand(time(NULL)); answer = rand() % 100 + 1; while (1) { printf("请输入你猜的数字(1-100):"); scanf("%d", &guess); if (guess > answer) { printf("大了!\n"); } else if (guess < answer) { printf("小了!\n"); } else { printf("恭喜!猜对了!\n"); break; } } return 0; }

这里的while (1)是故意写出来的“死循环”,配合break在合适时机退出。这是循环设计里的常用思路,很多情况下你不知道用户要猜多少次,所以让循环一直跑,直到满足条件再用break跳出。但需要强调:while (1)必须保证循环体内有可靠的退出条件,否则程序会彻底卡死。

这题如果觉得简单,可以再加限制:最多只能猜10次,超过次数强制结束。这就得引入一个计数器count,每次循环内递增,到10次直接break。你试着自己改一下,就能掌握计数器控制的思路,这也是循环里最核心的编程能力。

5. 常见错误与排查技巧

写C语言头一年,90%的时间不是在写代码,而是在查代码为什么不对。下面这几个问题是我见过最多的,你提前知道这些坑,能省下一大把时间。

5.1===

这是所有C语言初学者都会犯的最经典错误。if (a = 1)不会报错,它把1赋值给a,然后判断“a的值为1”这个条件是否为真——结果是真。这种bug让人百思不得其解:明明条件不成立,为什么它还是走进去了?

防止这个错误有一个土办法,把常量写在左边:

if (1 == a) { // ... }

如果你不小心写成if (1 = a),编译器会直接报错,因为不能给常量赋值。这个习惯虽然看起来有点别扭,但对新手来说,它是很实用的“防呆设计”。

5.2 悬挂else(dangling else)

看这段:

if (a > 0) if (a > 10) printf("a大于10"); else printf("a不大于0");

你可能会以为else是和第一个if配对的,但实际上,C语言规定else总是和最近的未配对if配对,所以这个else是和a > 10配对的,导致代码逻辑反转。

避免悬挂else的办法很简单:把所有if和else的大括号写完整。一旦用大括号把分支体框起来,这个坑就不存在了。

5.3 死循环排查

程序卡住不退出,十有八九是死循环。排查思路是:先在循环体里加printf打印当前变量的值,看看循环执行到哪一步,变量值是否符合预期。如果变量根本没有变化,那就是步进没执行或者条件恒真;如果变量变化了但条件还是真,那可能是条件写错了方向,比如小于和大于搞反。

对于实在看不出来的,可以加一个计数上限,比如:

int guard = 0; while (condition && guard < 10000) { // ... guard++; }

等代码正常运行了再删掉guard。这是调试中最实用的小技巧,比盯着屏幕干看效率高得多。

5.4 环境配置:VSCode跑C语言

很多初学者在安装编译器、配置环境这一步就卡住了。如果你用的是VSCode,目前最省心的方式是这样:

  1. 安装MinGW-w64或MSYS2,把bin目录加到系统环境变量PATH里。
  2. 在VSCode里安装C/C++扩展。
  3. 新建一个.vscode/launch.json.vscode/tasks.json,配置好gcc编译命令。
  4. 用终端手动执行gcc 文件名.c -o 文件名.exe也能跑起来。

不少教材一上来就让你用IDE,导致你对编译过程完全没有感知。我的建议是:前期就算用VSCode,也要学会在终端里手动执行gcc命令,哪怕只是运行一下“hello.c”,也要知道编译和运行是两个步骤。不然以后学了多文件工程,你会对“编译链接”完全没概念,遇到undefined reference这类报错会慌得不行。

6. 练习方向与节奏建议

6.1 从“看得懂”到“敲得出来”

很多同学学到这里有个误区:看例题觉得很简单,考试却不会写。原因只有一个——你动手太少。看代码就像看别人骑车,轻松;自己上车,摔跤。

我建议你按这个顺序来练习:

  • 第一轮:把书上的例题自己敲一遍,跑通就过,不要求背。
  • 第二轮:合上书,只看题目要求,自己独立写,写不出来就看提示,看完合上继续写。
  • 第三轮:把同样的功能用不同语句实现。比如同一个统计正负数个数的程序,先只用if写一版,再用if else写一版,再用switch试试能不能改写。

三轮走下来,你对分支循环的语法细节才算真正掌握。剩下的就是题量积累。

6.2 适合这个阶段的题目类型

如果你想找一些练手的题,我推荐这几种类型,网上搜“C语言基础练习题”能找出一大堆,比如“翁恺C语言练习题”就有很多适合这个阶段的题目。优先刷这句话我反复说:只要涉及数字处理逻辑(回文数、水仙花数、最大公约数、最小公倍数、素数判断),都要优先想到循环。这类题专门锻炼你把数学表达式转换为循环逻辑的能力,而这正是C语言工程里最常见的场景。

还有“必背100代码”这个说法,我不建议死记硬背。背代码不是目的,理解过程才是。你把100个代码背下来,不如亲手把10个经典题的断点、变量变化、输出结果分析清楚。

6.3 为后面指针和数组打基础

分支循环学扎实了,后面接触数组时你会觉得顺理成章:数组遍历就是for循环+下标,数组越界就是循环边界写错了。而指针中的“指向链表的节点遍历”本质也是while循环,只不过判断条件和步进方式从整数变成“指针是否为空、指针指向下一个节点”。

所以现在花时间把分支循环的基本功练好,其实是在给指针和链表打地基。很多学指针学到崩溃的人,回头去看翻车原因,不是指针太抽象,而是循环边界不清楚、分支条件写岔了。

7. 下一步:循环的进阶玩法

由于这一讲是“上”,我们只处理了最基础的语法和用法。但实际上,循环还有很多更深入的东西等着你。

比如for嵌套。

for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { printf("%d*%d=%d\t", j, i, i * j); } printf("\n"); }

这是九九乘法表。外循环控制行数,内循环控制每行的列数,两个变量一旦串到同一个表达式里,就是双变量协调,非常考验你对“循环变量变化”的理解。

还有一个角度是循环和数组的结合,你可以把“从1打印到100”升级成“把1到100存进数组,再倒序打印”,这种练习能让你自然过渡到后面的数组知识点,不会感觉突然跳了个台阶。

多提一句:Fibonacci数列、最大公约数、素数筛选这些题,它们不是刷题工具,是让你感受“同一个问题,用循环的不同写法都能有不同优雅程度”的切入点。比如求最大公约数的辗转相除法,用while写只有几行,但有人会写成一大坨if嵌套——对比一下你就知道什么是好代码了。

分支和循环的上篇就聊到这里。如果你能把这篇文章里的代码全部自己敲一遍,再试着改一改条件、换一换循环顺序,那你基础这一关已经稳了。下一次我们会聊循环的嵌套、break和continue更复杂的组合,以及如何用循环优雅地处理实际问题,到时候见。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询