首页 / 资讯中心 / 文章详情

C语言循环练习题通关指南:从while到嵌套循环的23道实战精讲

C语言循环练习题通关指南:从while到嵌套循环的23道实战精讲 ★ FEATURED ARTICLE
简介C语言循环练习题共23题是一份面向初学者的C语言循环专项训练资料适合正在学习循环结构、需要通过大量编程题巩固基础的在校学生也可用于计算机二级备考、上机实习或教师课后练习布置。资源集中覆盖最大数、素数判断、数列求和、西瓜售卖天数、成绩判定、水仙花数、数位之和、奇数和、特殊数值、征税、因数个数、最大公约数、斐波那契数列、数根、平均成绩等经典题型充分涵盖了for循环、while循环、嵌套循环以及循环配合条件判断、算术运算和数位拆分的常见用法。每道题都按照题目描述、输入格式、输出格式、输入输出样例、测试输入输出组织部分题目还给出了详细解析与解题思路便于学习者先思考再对照。资料包共1个docx文档压缩包大小3.85MB文档结构清晰可直接打印或导入笔记软件使用。当前已有4248人学习下载对于希望系统性巩固C语言循环结构、提升编程逻辑能力的初学者来说是一份高效率的练习素材。1. C语言循环练习题练到第几题才算真正过了循环这一关很多学C语言的人都有过这种体验语法书看完了switch、指针、结构体都能背出来一打开C语言循环练习题却卡在第三题。不是看不懂题目而是不知道循环体里那句赋值该放在哪个位置。输出结果要么是死循环要么比预期少一次。这套共23题的练习册把循环里最常见的形态都收进来了——单层循环、嵌套循环、循环里套判断、循环里改循环变量的值。它的价值不在于题目数量而在于每一题都对应一种真实会踩到的流程控制问题。23题练完的标准不是“能做对”而是“拿到一道没见过的题能在草稿纸上画出循环的进入条件、退出条件、循环体里哪些变量在变”。做到这一步后面的数组、指针、链表都会顺很多。适合三类人刚开始学循环的学生、准备补考或期末冲刺的人、以及带新人的工程师想快速找出对方哪里没理解透。下文按题目类型拆解每类给出可以直接抄的代码骨架和必坑点。2. 先分清循环三兄弟的骨架while、do-while、for 各自的长处和软肋2.1 用“读入直到0结束”这道题说清 while 的终止条件设计while 循环最容易翻车的地方是终止条件写反或者循环体里没有改变条件的语句。常见练习题是从键盘输入一批整数以0为结束标志计算这些数的平均值。#include stdio.h int main() { int n 0; // 当前读入的数 int sum 0; // 累加和 int count 0; // 有效数字个数 scanf(%d, n); // 先读第一个数 while (n ! 0) { // 0 是哨兵值遇到就停 sum n; count; scanf(%d, n); // 每次循环末尾重新读入这是循环能结束的关键 } if (count 0) { printf(平均值为: %.2lf\n, (double)sum / count); } return 0; }这段代码的逻辑是“先判断再执行”初始值必须提前读一次否则 while 条件里那个 n 是未定义行为。循环体最后重新调用 scanf这是整道题的命门——如果漏掉这行n 永远不变条件恒为真直接死循环。参数上注意 sum 和 count 是 int除法前要转 double否则两个 int 相除结果会被截断成整数。count 为0时第一个数就是0要单独处理不能让 sum 除以 count。2.2 do-while 的唯一不可替代场景至少执行一次的循环体do-while 在 23 题里通常只有一种题型会用到菜单选择、猜数字这类“无论如何先跑一次再判断”的场景。用“猜数字游戏”来理解最直观程序生成一个1到100之间的数玩家输入猜测值直到猜中。#include stdio.h #include stdlib.h #include time.h int main() { srand(time(NULL)); int target rand() % 100 1; // 1 ~ 100 的随机数 int guess 0; int tries 0; do { printf(请输入猜测值: ); scanf(%d, guess); tries; if (guess target) { printf(大了\n); } else if (guess target) { printf(小了\n); } } while (guess ! target); // 条件在循环体后面 printf(猜中了! 共用 %d 次\n, tries); return 0; }while 和 do-while 的区别在于while 第一次进循环前就要判断条件可能一次都不执行do-while 保证循环体至少跑一次。猜数字这个场景里玩家必须先输入第一次才能判断对不对所以 do-while 最契合。这里要注意一个坑如果输入的不是整数scanf 会返回0guess 里的值不会更新循环条件可能一直不满足形成死循环。一套稳健的写法是检查 scanf 的返回值发现输入不合法就清空缓冲区重读。2.3 已知循环次数时for 的三个表达式怎么写才不会绕for 循环是 23 题里出场率最高的因为它把初始化、条件、更新三个动作放在一行里结构清晰。但很多人踩坑在没有分清三个表达式分别执行几次——初始化只执行一次条件每次进循环前判断更新在每次循环体结束后执行。#include stdio.h int main() { int sum 0; // i 从 1 到 100步长为 1每次累加 for (int i 1; i 100; i) { sum i; } printf(1 2 ... 100 %d\n, sum); return 0; }这段代码的 i 是循环变量i 1是初始条件i 100是继续循环的条件i是每次循环结束后的更新。最容易错的地方是把条件写成i 101或i 100前者合法但别扭后者少算了最后一次。另一个常见误区是在循环体里修改 i 的值比如i i 2这会让步长变得隐晦调试时极难追踪。如果确实需要跳步不如把更新表达式直接写成i 2让意图一目了然。3. 23题里的嵌套循环难点图形打印与双重遍历的拆解套路3.1 用双重循环打印菱形先找行、列与数学关系图形打印题在循环练习题里非常有辨识度——输出一个由星号组成的三角形、菱形或者数字金字塔。这类题核心不是循环语法而是找到“第几行输出几个空格、几个星号”的数学关系。拿打印一个高度为 n 的菱形来说上半部分和下半部分是完全对称的两段。#include stdio.h int main() { int n 5; // 菱形对角线的长度必须是奇数 // 上半部分: 从第 1 行到第 n 行 for (int i 1; i n; i) { // 每行先输出空格: 数量为 n - i for (int j 1; j n - i; j) { printf( ); } // 再输出星号: 数量为 2 * i - 1 for (int j 1; j 2 * i - 1; j) { printf(*); } printf(\n); // 一行结束后换行 } // 下半部分: 从第 n - 1 行到第 1 行 for (int i n - 1; i 1; i--) { for (int j 1; j n - i; j) { printf( ); } for (int j 1; j 2 * i - 1; j) { printf(*); } printf(\n); } return 0; }这里的关键是行号 i 与空格数、星号数的线性关系第 i 行空格数是n - i星号数是2 * i - 1。下半部分把 i 从 n-1 递减到1数学关系不变。常见的翻车点是内层循环变量和外层都用 i导致两个循环互相干扰。我一般会约定外层用 i、内层用 j、再内层用 k保持统一。其次图形题很容易犯“每行末尾多打一个空格”的问题判题系统如果严格比对会直接报错所以printf(\n)应该在星号循环结束后单独执行而不是放在内层循环里。3.2 双重循环处理矩阵冒泡排序里的交换与边界控制矩阵类题是嵌套循环的另一大阵营常见的有输出九九乘法表、打印杨辉三角、矩阵转置以及数据结构里的冒泡排序。冒泡排序在23题里出现的频率不低因为它完美演示了双重循环里内层边界如何依赖外层变量。#include stdio.h void bubbleSort(int arr[], int n) { // 外层控制轮数最多 n - 1 轮 for (int i 0; i n - 1; i) { // 内层控制每轮比较次数: 第 i 轮只需比较前 n - 1 - i 对 for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { // 交换两个相邻元素 int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; } } } } int main() { int arr[] {64, 34, 25, 12, 22, 11, 90}; int n 7; bubbleSort(arr, n); for (int i 0; i n; i) { printf(%d , arr[i]); } printf(\n); return 0; }内层循环的终止条件是n - 1 - i这是理解冒泡排序的钥匙第0轮结束后最大的数已经沉到数组末尾下一轮就不用再碰它所以每轮比较次数递减。新手写内层条件时容易写成j n - 1虽然结果不会错但多了很多无意义的比较。另一个常见问题是交换时漏掉 temp 变量写成arr[j] arr[j 1]; arr[j 1] arr[j];结果是两个元素都变成同一个值。这里要强调一点C语言里没有直接交换两个变量的语法糖必须引入第三方变量或者用异或运算。3.3 双重循环里 break 和 continue 的边界控制嵌套循环里用 break 只会跳出最内层的一层循环这是23题里专门有一道题考察的。先看一个具体例子在一个5行5列的矩阵中找到第一个值为0的元素并打印其坐标然后终止所有循环。#include stdio.h int main() { int matrix[5][5] { { 1, 2, 3, 4, 5}, { 6, 7, 8, 9, 0}, {11, 12, 13, 14, 15}, {16, 0, 18, 19, 20}, {21, 22, 23, 24, 25} }; int found 0; // 标志位: 是否找到 for (int i 0; i 5 !found; i) { for (int j 0; j 5; j) { if (matrix[i][j] 0) { printf(找到0的位置: (%d, %d)\n, i, j); found 1; break; // 只跳出内层循环 } } // 外层的循环条件里有 !found, 所以会立即退出 } return 0; }这里的技巧是把“是否找到”放进外层循环的条件里。只在内层 break 的话外层还会继续执行下一轮结果就是找到多个0。这个问题在23题里专门有一道意图就是让学习者意识到 break 的作用域只限于当前一层。处理方式有两种一种是上面这种标志位写法另一种是直接用goto跳转但不推荐。实际上还有第三种更简洁的做法把查找逻辑封装成一个函数在函数内部直接return这比标志位更干净因为函数一旦返回所有循环自然终止。4. 循环里的经典算法隐藏考点倒序、素数判定与累加迭代4.1 整数倒序输出的循环模型除10取余法23题里有一道非常经典输入一个正整数按逆序输出它的每一位数字例如输入 12345输出 54321。这题考察的是对一个数反复执行“取余和整除”的操作循环终止条件是数变为0。#include stdio.h int main() { int n; printf(请输入一个正整数: ); scanf(%d, n); while (n 0) { int digit n % 10; // 取出最低位 printf(%d, digit); n n / 10; // 去掉最低位 } printf(\n); return 0; }核心逻辑是n % 10取出最低位n / 10把最低位去掉。每循环一次n 的位数就减少一位直到 n 变成0。这个模型非常重要因为它就是后面“十进制转二进制”“判断回文数”的基础。这里有一个边界坑如果输入的是 0while 条件不会成立什么都不会输出。正确的做法是单独判断if (n 0) printf(0\n)或者改用 do-while 结构。另一个坑是负数-123 % 10在C语言里结果是 -3而不是 3所以处理负号要单独判断。我一般会先取绝对值处理数字部分最后根据原数符号打印负号。4.2 素数判定的循环边界为什么只需要检查到 sqrt(n)素数判定几乎出现在每套循环练习题里23题也不例外。题目通常是输入一个正整数 n判断它是否为素数。很多人的第一版代码会把循环写到 n其实只需要检查到sqrt(n)这背后是数学上的一个事实如果 n 有一个大于sqrt(n)的因子那么它必然也有一个小于sqrt(n)的因子。#include stdio.h #include math.h int main() { int n; scanf(%d, n); if (n 2) { printf(不是素数\n); return 0; } int isPrime 1; // 先假设是素数 // 只需要检查到 sqrt(n), i sqrt(n) 可以写成 i * i n for (int i 2; i * i n; i) { // 避免每次循环都调用 sqrt() if (n % i 0) { isPrime 0; break; } } if (isPrime) { printf(是素数\n); } else { printf(不是素数\n); } return 0; }注意这里我把循环条件写成了i * i n而不是i sqrt(n)。这不是玄学而是性能考量sqrt()是一个浮点运算如果在循环条件里调用每次迭代都要算一次而i * i是整数乘法快得多。当然如果 n 很接近INT_MAXi * i可能溢出稳妥做法是先把sqrt(n)算出来存进变量。还有一个细节2 是素数1 和小于1的数不是循环开始前要把 n2 的情况单独处理掉。很多人在这个边界上翻车把 1 判成了素数。4.3 斐波那契数列和累乘的迭代模型后一个值依赖前两个值斐波那契数列是循环题里另一个高频考点它比单纯的累加累乘多了一个点当前值依赖前面两个值。题目通常是输出前 n 个斐波那契数其中前两项为 1 和 1。#include stdio.h int main() { int n; printf(请输入要输出的项数: ); scanf(%d, n); if (n 0) { return 0; } long long a 1, b 1; // 前两项 if (n 1) printf(%lld , a); if (n 2) printf(%lld , b); for (int i 3; i n; i) { long long next a b; // 当前项是前两项之和 printf(%lld , next); a b; // 向前滚动 b next; } printf(\n); return 0; }这个模型的精髓在于变量的滚动更新算出next之后把原来的 b 赋给 a把 next 赋给 b为下一轮迭代做准备。这比每轮都从数组里取数要节省内存。使用long long是必要的因为第 50 个斐波那契数已经超过了int的表示范围。这里的思考方式值得手抄凡是“当前值依赖前几个值”的题都可以用这种方式定义几个滚动变量在循环体末尾统一更新。23题里还有一道“求最大公约数”的题也是同样套路用辗转相除法时每轮把 b 赋给 a把 a%b 赋给 b直到 b 为0。5. 23题里最容易丢分的踩坑点死循环、边界错位、break和continue、输入残留5.1 死循环循环变量没更新或条件恒为真现象程序运行后卡住不动控制台疯狂输出或者完全无响应只能按 CtrlC 终止。原因通常有两个循环体里漏掉了更新循环变量的语句或者循环条件写成了恒真的表达式。比如把while (i 10)写成while (i 0)i 一直递增条件永远为真。解决方法是先看循环体最后一行有没有让条件中的变量发生变化——如果没有加一句更新语句其次是打印循环变量值确认它每次都在朝结束的方向走。另一个隐蔽的死循环来源是浮点数比较。用double做步进时x ! 1.0这种条件可能永远为真因为浮点数的二进制表示不精确累加结果永远不等于目标值。遇到这种情况改成比较绝对误差比如fabs(x - 1.0) 1e-6。这题在23题里不是直接考察但叠加在其他题目上会让结果莫名其妙。5.2 少一次或多一次边界条件i n与i n的区别现象输出结果总是比预期少了最后一个数或者多了一行无意义的输出。比如求 1 到 100 的和结果却是 4950少加了100。原因是循环条件写成了i 100而不是i 100。这种问题在单层循环里容易发现进入嵌套循环后就会被掩盖——内层循环的边界错误会导致图形打印错位排查起来费劲。我的排查习惯是先拿边界值手算一遍。想验证i n是否正确就把 in 代入循环体看它是否需要被计算。如果循环条件是i n那么 in 时不会进循环。在23题的图形打印题里行数经常是用户输入的边界差一就会导致最后一行缺失或多出空白行。建议写代码前在注释里写明循环范围比如// i: 1 ~ n 包含n给自己立一个规矩。5.3 continue 的隐蔽坑跳过了循环变量更新语句现象程序在循环里某个条件满足后就开始死循环或者输出结果错乱。原因是continue跳过了循环体末尾的更新语句。比如下面这个例子int i 1; while (i 10) { if (i 5) { continue; // 直接跳到条件判断, i 没有递增 } printf(%d , i); i; }当 i 等于5时continue让控制流跳回 while 条件判断此时 i 还是5再次进入循环再次 continue无限循环。在 for 循环里不会有这个问题因为 for 的更新表达式会在每次continue之后照常执行只要不是死代码路径但在 while 和 do-while 里更新语句在循环体内部continue会让它被跳过。解决方法是把更新语句放在循环体最前面或者用 for 循环代替 while。这是23题里专门有一道考察点正确改法是把i移到continue之前或者改为 for 循环。5.4 输入缓冲区的残留scanf 与循环之间的换行符纠缠现象循环读入时第一次 scanf 正常后面几次直接跳过不等待输入就往下走了。原因在于 scanf 读取数值时不会吃掉输入流里的换行符残留的\n会在下一次输入时被%c或%s读取导致读取到空字符或空串。这道题在23题里通常是组合出现的——先读一个整数 n再循环读 n 个字符就会撞上这个问题。#include stdio.h int main() { int n; printf(请输入一个整数: ); scanf(%d, n); getchar(); // 吃掉残留的换行符 char c; for (int i 0; i n; i) { printf(请输入字符: ); scanf(%c, c); // 现在能正常读到了 getchar(); // 再次吃掉这个字符后面的换行 } return 0; }关键点是每次用%c读取输入前确保输入缓冲区里没有残留的空白字符。getchar()是最直接的清理手段但要注意它不能无限使用——如果缓冲区里没有残留字符它会阻塞等待输入。更稳妥的做法是用scanf( %c, c)在%c前加一个空格让 scanf 自动跳过所有空白字符。如果你在23题里遇到读入字符后输出不对的情况第一优先怀疑这个原因而不是循环逻辑本身。这是C语言学习者最容易忽视的黑匣子因为程序看起来没毛病但行为就是不对。6. 进阶技巧把23题改成自己的24题用三步法验证循环代码是否可靠23题练完后真正的检验是自己出题、自己改题。改题比做题更能暴露出对循环的理解是否到位。我常用的方法是从三个维度去改换数据范围、换条件类型、换循环结构。例如原题是“求 1 到 n 的和”改成“求 1 到 n 之间所有奇数之和”再改成“求 n 个输入数中正数之和”最后改成“用 while 实现同样的功能”。每改一次都是在强迫自己重新检查边界条件和循环终止条件。改到第24题、第25题时你会发现自己已经不再对着循环体发呆而是能直接写出正确骨架。验证循环代码时有个三步法可以帮助你减少低级错误。第一步用最小输入测试n1 或者只循环一次的场景确认循环体能执行且结果正确。第二步用边界输入测试循环条件的边界值比如i n中的 n 本身确定它是否应该被包含。第三步用随机数据对拍如果实现了两种解法可以用随机生成的数据同时跑两遍对比结果是否一致比如用 while 和 for 各写一遍互相验证。这三步下来绝大部分逻辑错误都能被筛掉。还有一个被我反复强调的调试习惯每轮循环开头打印循环变量的值。不需要用调试器只需在循环体第一行临时加一句printf(i%d\n, i)运行一次后删掉。这一招能快速定位死循环和边界错位比盯着代码空想快得多。当年我带新人时很多人的循环卡了半小时都找不到问题加了这行打印后三秒就明白了。这不算什么高级技巧但它是排除“循环到底跑了几次”这个疑问的最直接方法。我自己练循环练习题时最有价值的收获不是记住了多少个算法模板而是养成了一个习惯每写一个循环先问自己三个问题——循环什么时候进入什么时候退出循环体里哪个变量的变化在推动进程走向退出。把这三个问题想清楚了循环题就没有难题。如果你的 23 题正好卡在某道图形题或素数判定上试着用上面这些拆法重新过一遍如果哪一步跑不通把断点打在循环条件判断上看它是不是按你预想的方向走。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站