资讯详情

C语言经典100道前10题拆解:从基础语法到工程实践

📅 2026/10/17 5:21:01 | 华诺云谱 👁 阅读
C语言经典100道前10题拆解:从基础语法到工程实践
先说个真实感受这套“C语言经典100道”我从学生时代做到工作以后带新人1-10题一直都是我推荐的起点。别小看这几道题它们看着基础却把顺序、分支、循环、数组、字符串这些C语言最核心的骨架全过了一遍。很多人刷题坚持不下去往往就是死在“第一题太简单不想做、第五题开始看不懂”的中间地带。这篇把1-10题完整拆开每题给出可直接运行的代码、涉及的知识点、以及我实践里踩过的坑。适合刚学完C语言语法、想找题练手的初学者也适合笔试面试前想快速过一遍基础语法的人参考。1. 为什么这套经典100道值得一刷再刷1.1 题目设计的前后逻辑这套题流传了很多年网上能找到的版本少说也有七八个但前10题的排列逻辑基本一致从最简单的输出开始逐步引入变量交换、条件判断、数学计算、循环嵌套、数组和字符串操作。这种顺序不是随手排的它遵循了学习语言的天然路径——先学会让程序开口说话再学会让程序做选择最后让程序处理批量数据。很多人问过我“现在网上刷题平台那么多为什么还要刷这种老古董题”我的看法是刷题平台的题目更偏算法竞赛上来就是动态规划、图论对初学者并不友好。而这套100题更像是“语法关卡”每道题对应的语法点非常明确比如第3题考分支、第5题考循环边界、第8题考嵌套循环输出格式。刷完能建立一套“看到题目就能定位到语法点”的直觉这对后续学数据结构、刷算法题帮助很大。1.2 前10题是整个系列的基础底座前10题覆盖的知识点恰恰是后面90题反复使用的基本功。以我的经验很多人做到第30题以后开始吃力回头一排查发现是第5题的循环边界、第6题的取模运算没真正搞懂。前10题里每个知识点都值得反复打磨题号核心知识点关键语法点1标准输入输出printf、转义字符、return 02变量与赋值运算中间变量交换、printf格式控制3分支结构if/else、逻辑与或、取模运算4循环与经典算法while循环、辗转相除法5循环边界优化for循环、sqrt函数、break6循环与取位运算数位分解、while循环7迭代与递归思想斐波那契数列、递归写法8嵌套循环与格式化双重for循环、字段宽度控制9一维数组操作数组逆序、双指针10字符串处理字符串反转、字符数组这张表建议保存下来刷完前10题对照自查一遍哪个格子是空的就回去补哪块。2. 1-10题逐题拆解与可运行代码2.1 第1题Hello World不是只写一行就完事题目在屏幕上输出“Hello World”。这题几乎是所有人C语言的第一行代码。但我想说的是如果你只是把代码敲进去、看到输出就觉得自己会了可能错过了不少细节。我在面试实习生时会问一个很刁钻的问题“printf(Hello World\n)里的\n到底干了什么不写行不行”有人答不上来有人会说“换行”但很少有人能说清楚——它是转义字符代表回车换行如果不写下一次输出内容会和Hello World挤在同一行。#include stdio.h int main() { printf(Hello World\n); return 0; }这段代码里值得留意的点有三个#include stdio.h是预处理指令告诉编译器我们要用到标准输入输出函数printf就在这个头文件里声明。漏了它编译直接报“隐式声明”警告。main函数是程序入口标准写法必须是int main()有些人图省事写void main()这在某些编译器里能过但不是标准C不建议养成这种习惯。return 0;表示程序正常结束返回0给操作系统。不写的话编译器隐式补上但强烈建议显式写出来。我见过不少初学者在环境配置阶段就被劝退了其实第一题不用急着折腾复杂的IDE随便找个在线编译平台把这段代码贴进去跑通先建立“我能写出可运行程序”的信心再慢慢研究本地环境。2.2 第2题交换两个变量的值面试也爱考这道变体题目定义两个变量交换它们的值。这道题最简单直观的办法是引入第三个临时变量。有些资料会教你不用临时变量也能交换比如用加减法或异或运算。我建议初学者先把中间变量法写熟因为它的思路最通用——以后学数组、学链表时交换两个元素这件事会反复出现临时变量的方案永远是最稳妥、最不容易出错的。#include stdio.h int main() { int a 3, b 5; int temp; printf(交换前: a%d, b%d\n, a, b); temp a; a b; b temp; printf(交换后: a%d, b%d\n, a, b); return 0; }这里有个新手常犯的误区写成a b; b a;。你想啊第一句执行完a已经变成5了第二句再把5赋给b两个变量都变成5值没交换成。所以我带新人时总是让他们在草稿纸上画三个盒子把变量名写在盒子上把值写在盒子里然后手动走一遍三行赋值印象一下就深了。至于异或法a a ^ b; b a ^ b; a a ^ b;我个人的态度是知道有这么回事就行不要在初学阶段用它炫技可读性太差而且面试官不一定认同这种写法。2.3 第3题判断闰年逻辑表达式组合的经典练习题目输入一个年份判断是否为闰年。闰年的规则是能被4整除但不能被100整除或者能被400整除。这道题考察的是把自然语言描述翻译成逻辑表达式的能力对应C语言里的if/else和逻辑运算符、||、!。#include stdio.h int main() { int year; printf(请输入年份: ); scanf(%d, year); if ((year % 4 0 year % 100 ! 0) || year % 400 0) { printf(%d 是闰年\n, year); } else { printf(%d 不是闰年\n, year); } return 0; }很多初学者第一次写会漏掉“不能被100整除”这个条件输出结果里像1900年这种能被100整除但不能被400整除的年份就被错误地判成了闰年。我建议写完代码后用几个特殊年份验证2000年闰年、1900年不是闰年、2024年闰年、2023年不是闰年。把边界值测一遍比蒙头跑十次普通年份有效得多。另外提醒一句scanf(%d, year)这里的不能忘。它表示取地址把输入的值存到year这个变量的内存地址上。漏掉是C语言初学阶段出现频率最高、最容易让人崩溃的编译或运行错误之一后面我还会再提。2.4 第4题求最大公约数辗转相除法为什么优雅题目输入两个正整数求它们的最大公约数。最笨的办法是从两个数里较小的那一个开始往下一个个试直到找到能同时整除两个数的最大数。这个思路没问题但效率太低。真正经典的解法是欧几里得的辗转相除法核心逻辑只有一句话两个数的最大公约数等于较小数和两数相除余数的最大公约数。用while循环不断把“被除数”和“余数”替换直到余数为0剩下的就是最大公约数。#include stdio.h int main() { int m, n, r; printf(请输入两个正整数: ); scanf(%d %d, m, n); while (n ! 0) { r m % n; m n; n r; } printf(最大公约数是 %d\n, m); return 0; }第一次看到这个算法的人多少会有点“还能这样操作”的惊讶——两行替换就把一个暴力遍历问题变成了O(log n)的快速问题。这个“用余数缩小问题规模”的思路在后面学递归时还会再次遇到。如果你已经掌握了函数的概念可以把它写成递归版本会更精简int gcd(int a, int b) { return b 0 ? a : gcd(b, a % b); }我个人建议先把while版本写熟理解循环跳出的条件是什么再去看递归版本。不要一上来就递归容易把自己的脑子绕进去。两个版本都要会手写面试里这道题出现的频率真的不低。2.5 第5题判断素数循环边界决定你的算法水平题目输入一个正整数判断它是否为素数质数。素数是指除了1和它本身以外不能被其他正整数整除的数。最直接的想法是让n从2除到n-1只要有一次能整除就不是素数。不过这个写法有冗余——如果n能被某个数整除那这个数最多到根号n就够了因为因子总是成对出现的。比如判断49试到7就够了因为7×749如果7除不尽再往后的数也不可能除尽。#include stdio.h #include math.h int main() { int n, i; int isPrime 1; printf(请输入一个正整数: ); scanf(%d, n); if (n 2) { isPrime 0; } else { for (i 2; i sqrt(n); i) { if (n % i 0) { isPrime 0; break; } } } if (isPrime) { printf(%d 是素数\n, n); } else { printf(%d 不是素数\n, n); } return 0; }关键点有两个循环上界是sqrt(n)而不是n。这需要引入math.h头文件用sqrt函数计算平方根。有些编译器环境对sqrt要求链接数学库如果报错可以在编译命令里加-lm参数。break的使用很讲究——一旦确认某个数能整除立刻跳出循环没必要继续试下去。这不仅是效率问题更是一种“及时止损”的编程思维。我见过不少人忘记处理“n小于2”的情况。1既不是素数也不是合数2是最小的素数。边界条件不处理程序逻辑就会出现漏洞。这也是我在解题时反复强调“先处理特殊情况再写主逻辑”的原因。2.6 第6题水仙花数数位分解的入门必修课题目输出所有水仙花数。水仙花数是指一个三位数其各位数字的立方和等于该数本身例如153 1³ 5³ 3³。这道题的重点在于掌握如何从一个整数中拆出各个数位。对于一个三位数abc百位是n / 100十位是n / 10 % 10个位是n % 10。这里用到了整数除法和取模运算的配合。#include stdio.h int main() { int number, a, b, c; number 100; while (number 1000) { a number / 100; b number / 10 % 10; c number % 10; if (a * a * a b * b * b c * c * c number) { printf(%d\n, number); } number; } return 0; }运行结果就是153、370、371、407这四个数。这道题真正想让你练的是把“数字”和“数位”互相转换的能力。后面做题会遇到“回文数判断”“数字反转”“整数转字符串”等题目全部建立在数位分解的基础上。所以不要只是把这段代码抄一遍要自己多试几个变体比如改成输出所有的四叶玫瑰数四位数各数位四次方之和等于本身你会发现思路只要稍微迁移一下就能跑通。2.7 第7题斐波那契数列递归与迭代的分水岭题目输出斐波那契数列的前n项。斐波那契数列从第1项开始前两项是1、1从第3项起每一项都等于前两项之和。这道题有两种思路递归和迭代。递归写法非常直白几乎就是把数学定义翻译成代码int fib(int n) { if (n 2) { return 1; } return fib(n - 1) fib(n - 2); }但如果你让这个函数计算fib(50)程序会慢到怀疑人生。原因很简单递归会大量重复计算。fib(5)要算fib(4)和fib(3)fib(4)又要算fib(3)和fib(2)同一个fib(3)被算了两遍。n越大重复计算越夸张。这就是为什么我建议用迭代版本#include stdio.h int main() { int n, i; unsigned long long f1 1, f2 1, next; printf(请输入项数: ); scanf(%d, n); if (n 1) { printf(1\n); } else if (n 2) { printf(1 1\n); } else { printf(1 1 ); for (i 3; i n; i) { next f1 f2; printf(%llu , next); f1 f2; f2 next; } printf(\n); } return 0; }这里用unsigned long long是因为斐波那契数列增长极快到第50项左右就会超过int的范围。很多初学者不管数据类型统一用int跑到第40项就发现输出了负数还以为是自己逻辑写错了。这个“用合适类型承接运算结果”的意识越早建立越好。迭代版本的思路是滚动更新每次计算完next把f2赋值给f1把next赋值给f2相当于两个滑块依次前进。理解了这一点你就掌握了动态规划里“状态转移”的雏形。2.8 第8题九九乘法表嵌套循环与输出格式的磨刀石题目打印九九乘法表即从1×1到9×9的三角形乘法表。这道题一看就是双重for循环。外层循环控制行数内层循环控制每一行打印多少项。但真正折磨人的是“格式对齐”——不同乘积位数不同不控制宽度输出就会歪歪扭扭。#include stdio.h int main() { int i, j; for (i 1; i 9; i) { for (j 1; j i; j) { printf(%d*%d%-2d , j, i, i * j); } printf(\n); } return 0; }关键在%-2d这个格式控制-表示左对齐2表示占两个字符宽度。这样1×11和2×36这种单位数结果输出时也能整齐对齐。你可以试着把-2d改成2d看看输出变成什么样就知道左对齐和右对齐的区别了。输出应该是1*11 1*22 2*24 1*33 2*36 3*39 ...我遇到的常见错误是内层循环的条件写成j 9结果每行都打出9项变成了一个矩形而不是三角形。这里要时刻记住内层循环的结束条件依赖外层循环变量的值这是嵌套循环的重要考察点。2.9 第9题数组逆序双指针思想的第一次实战题目定义一个整数数组将数组元素顺序颠倒。数组逆序的经典做法是双指针一个指针指向第一个元素一个指针指向最后一个元素交换它们然后左指针右移、右指针左移直到两个指针相遇。#include stdio.h #define N 6 int main() { int arr[N] {1, 2, 3, 4, 5, 6}; int left 0, right N - 1; int temp, i; while (left right) { temp arr[left]; arr[left] arr[right]; arr[right] temp; left; right--; } for (i 0; i N; i) { printf(%d , arr[i]); } printf(\n); return 0; }这里用到了第2题学的交换逻辑所以我说前10题是一环扣一环的。循环条件是left right如果数组长度是奇数中间那个元素不用动如果是偶数左右指针会在中间相遇后自然退出循环。我见过有人用“定义一个新数组把原数组从后往前拷进去”的写法功能上没错但多占了一倍的存储空间。双指针法是在原数组上直接操作空间复杂度O(1)。养成“优先考虑原地操作”的习惯对后续算法学习很重要。2.10 第10题字符串反转从数组到指针的一道坎题目输入一个字符串将其反转后输出。字符串在C语言里其实就是一个以\0结尾的字符数组。所以反转字符串的思路和反转整型数组很相似只不过你要注意结束标志\0不能被挪到开头去。这里我先提醒一个很多教材还在用、但我强烈不建议的新手写法gets(str)。gets函数不检查缓冲区容量用户输入稍微长一点就会越界写坏内存属于公认的不安全函数新版C标准里已经被移除了。我用fgets代替#include stdio.h #include string.h int main() { char str[100]; int left, right; char temp; printf(请输入字符串: ); fgets(str, sizeof(str), stdin); str[strcspn(str, \n)] \0; left 0; right strlen(str) - 1; while (left right) { temp str[left]; str[left] str[right]; str[right] temp; left; right--; } printf(反转后: %s\n, str); return 0; }这里有个非常隐蔽的坑fgets会把输入时的回车换行符也读进数组里。如果不处理strlen得到的长度就多算了1反转结果里会带一个换行符。所以str[strcspn(str, \n)] \0;这行的作用就是把换行符替换成字符串结束标志。strcspn是“返回字符串中第一次出现指定字符的位置”的函数正好用来找换行符。计算right时用strlen(str) - 1减1是为了跳过字符串结束标志\0。这个细节一旦漏了反转后的字符串会缺字符或者出现乱码。做题时多想想“字符数组的末尾发生了什么”比死记代码有用得多。3. 实操环境与排查方法3.1 用什么环境跑这10道题最省心这一节是给读者实操时参考的我不只推荐一个方案按上手难度排三个层次在线判题平台完全不需要本地配置打开浏览器注册即用写完提交直接看运行结果和报错信息。对于1-10题这种基础题目在线平台完全够用还能看到其他人提交的写法方便对比。缺点是网络依赖强而且有些平台对scanf的输入输出有严格格式要求需要适应。Dev-C老牌Windows平台免费IDE界面朴素但功能齐全自带编译器适合刚接触编程、还不太会用命令行的人。缺点是停止维护多年在高分屏下显示比较模糊。VS Code GCC编译器写代码体验好配置也不算复杂。我现在的日常环境就是这个组合。如果你之前没配过照着官方文档一步步来大概二十分钟能搞定。对于后面90题来说这个环境能支撑更复杂的调试需求。我的建议是如果只是刷完这套题选在线平台或Dev-C就够了不要花太多时间折腾环境。敲代码的工具有很多真正重要的是你敲进去的每一行代码。3.2 我实际使用最多的三个调试技巧前10题虽然简单但不代表不会出错。我总结三个调试技巧不花哨但很顶用printf大法在关键步骤输出中间变量的值。比如交换两个数后先把a和b打印出来判断循环边界时先打印循环变量。这个方法看着原始却能在五分钟内定位大部分问题。调试完记得把临时打印删掉不然输出会变得很乱。边界值测试法不要只测试正常输入。第3题用1900和2000测闰年判断第5题用1、2、4测素数判断第7题用n1和n50测边界项。把边界情况测一遍你的程序才算真正能交出去。人肉模拟运行拿一张纸写下所有变量的初始值逐行执行代码每次赋值就划掉旧值写新值。这个方法在排查循环逻辑错误时非常好用尤其是第4题gcd那种变量不断互换的算法走一遍纸面运行整个流程瞬间就清楚了。3.3 编译报错后先看什么初学者遇到报错最容易慌。我总结一个原则看编译器给你的第一个错误、最上面的那条信息。很多时候编译器只报了一个错后面的错误都是由它连带引发的。前10题里最常见的编译问题有这么几类未声明函数多半是忘了#include对应的头文件。printf、scanf要stdio.hsqrt要math.hstrlen要string.h。main函数返回值类型不对标准C要求int main()写成void main()或干脆漏掉返回类型有的编译器会警告有的直接error。中英文标点混用中文输入法打个全角分号或括号编译器看得一脸懵。一旦报错内容是“expected ‘;’”先检查标点是不是全角。找不到头文件通常是文件名写错比如studio.h这种手滑。4. 常见错误与避坑清单4.1 前10题最典型的五个错误把这几天我在问题排查中反复见到的错误整理成一张速查表错误现象根本原因解决办法scanf后变量值一直是0忘记写取地址符scanf(%d, n)而非scanf(%d, n)输出全是乱码或负数数据类型范围不够斐波那契等大数用long long或unsigned long long循环死循环不停循环变量没更新或更新条件写错检查循环体内是否有i或等价操作字符串反转结果不对忘记处理换行符或\0fgets后去掉换行符strlen后减1判断结果和预期相反逻辑表达式优先级或边界写错用括号明确优先级特殊值单独测试第2个错误最隐蔽因为有时候int够用表象不明显但一旦n到47左右斐波那契就溢出数据变成负数很让人费解。我建议养成习惯凡是数值可能比较大的变量优先用long long不要等到输出错了才回头改类型。4.2 从“能跑通”到“写规范”的三点建议会写出正确结果不等于代码质量合格。我面试新人时不会只看答案对不对还会看代码长什么样。前10题是你养成好习惯的最佳时期这三点建议可以直接套用变量命名要见名知意。写n、a、b没有问题但更好的是year、number、left、right。单字母变量在简单题里能看懂到了复杂题里只会让你自己怀疑人生。统一缩进与大括号风格。条件语句和循环体即使只有一行也建议写大括号。省了那两对大括号后面加逻辑时很容易漏加而改变执行结构。写完代码先做一次“人肉编译”。也就是逐行读一遍自己的代码检查有没有漏了分号、、#include。这个习惯能让你在编译器报错之前就消灭一半的问题别人还在改bug的时候你已经可以把运行结果截图发出去了。4.3 如何高效复刷而不是机械抄答案刷题最忌的是“对着网上的答案复制粘贴”。我推荐一个做法先把答案盖住只看题目的原始描述自己在纸上写出思路哪怕是伪代码然后动手实现跑通了再对照参考代码看差异。如果没思路那就先看答案弄懂以后再合上答案自己完整写一遍。第二次能独立写出来这道题才算真正掌握。做完1-10题别急着往下冲。花一点时间把每道题的变体想想比如第5题改成“输出100以内的所有素数”第7题改成“输入n输出第n项”第8题改成“用while循环实现一遍”。这些变体一旦能独立完成说明你不是背会了代码而是理解了思路这才是刷题的核心目的。最后聊几句这套1-10题我前前后后带过不少人刷过。有人一天就全部跑通有人卡在第7题斐波那契整整一周都很正常。我个人最深的体会是第4题gcd和第5题素数判断这两道题值得多花时间它们第一次把一个“笨办法”优化成“巧办法”的过程展示在你面前那种恍然大悟的感觉是很多人对算法感兴趣的开端。做题期间如果遇到运行结果和预期不符别急着翻答案先按上面说的边界测试法多测几组输入。实在搞不定把代码丢到一边休息半小时再回来思路往往会突然打开。这是我自己踩过无数坑之后总结出来的最实在的一条经验。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑