1. 实验内容与设计思路1.1 题目原意与核心考点这两个题目在大学C课程里出现频率有多高不用我多说。十进制转二进制和杨辉三角形几乎是国内所有高校C程序设计课的大一标配实验。看起来简单但实际上每一个都精准踩中了C入门阶段最核心的几个知识点循环结构、数组操作、函数封装还有最容易被忽略的输出格式控制。先说十转二这道题。它表面上是“写一个进制转换函数”但本质考的并不是数学而是循环与数组的综合运用。经典的除2取余法需要你反复对2取模、整除把每一步的余数保存下来再倒序输出。这中间涉及“先算出来的数反而要后输出”这一反直觉逻辑恰恰是初学者最容易绕晕的地方。换句话说这道题是在用进制转换这个壳考你对数据存储顺序的理解。再说杨辉三角形。这道题核心考的是二维数组的递推关系和多层循环的协作。杨辉三角第i行第j列的数等于上一行第j-1列加第j列边界位置全是1这个递推关系一旦理解了代码本身并不复杂。但如果你想输出一个漂亮的等腰三角形而不是一个歪歪扭扭的直角三角就必须认真处理空格与数字宽度这就牵扯到格式化输出。很多同学代码逻辑全对就因为输出格式不对被扣分非常可惜。这两个题目放在一起做实验其实是有讲究的一维数组问题过渡到二维数组问题顺序输出过渡到倒序输出再过渡到复杂格式控制难度梯度刚好做一遍几乎能把C入门阶段最有价值的语法点全部过一遍。1.2 整体方案选型在开始写代码之前我建议你先想清楚用哪种思路实现而不是拿起键盘就敲。以我批改过的实验报告和辅导过的学生来看提前做方案选择的人代码质量和报告完成度普遍高一个档次。对于十进制转二进制我推荐优先掌握经典的**“短除法数组倒序”**写法。理由很直接代码逻辑直观每行代码都能和数学步骤一一对应评审老师一眼就能看懂你在做什么。在此基础上可以额外补充递归写法和位运算写法放到报告里作为“扩展思考”能让你的实验报告显得有深度。对于杨辉三角形二维数组递推是标准答案必须会写。但如果报告里能额外展示一维数组滚动更新的优化版本再配上空间复杂度从O(n^2)降到O(n)的分析这就不是普通学生作业的水平了而是有工程意识的代码。后面我会把这两种写法都贴出来。工具方面简单题目不需要复杂工程结构。用VS Code配好C/C环境或者直接用Dev-C都能顺利完成。这部分我在第四章讲环境配置和运行命令时会细说。2. 十进制转二进制三种写法帮你彻底吃透除2取余2.1 原理为什么是“除2取余逆序输出”十进制转二进制的标准做法大家都背过不断用2去除十进制数记下余数直到商为0再把余数从下往上倒着写。但这个“倒着写”的原理很多同学没有真正理解只是记住了结论。其实道理不复杂。十进制数13二进制是1101。13可以拆成841也就是2^3 2^2 2^0所以二进制表示在从右往左的第0位、第2位、第3位为1写成1101。我们做短除法时第一步13除以2余1这个1代表的是“有没有2^0的份额”是最低位的值第二步6除以2余0代表“有没有2^1的份额”是倒数第二位。也就是说短除法先算出来的是低位后算出来的是高位。正因如此如果要按人类的阅读习惯从左往右输出就必须把计算顺序倒过来。如果你接触过栈的概念你会发现短除法的天然数据结构就是栈——先算出的余数压栈最后弹栈输出。C实验阶段一般还没有系统讲栈所以更常见的做法是用数组存余数循环结束后从数组末尾反向遍历输出。本质上这就是在手动模拟栈的“后进先出”。这里有一个很容易忽略的边界情况输入为0时循环一次都不会执行如果没有提前处理程序会什么都不输出或者输出一个空行。所以0的处理务必写到代码里直接输出一个0即可。很多同学的代码在这里翻车不是逻辑错是边界条件漏了。2.2 写法一短除法配数组倒序最标准、最适合实验报告的写法是数组存储余数后倒序输出。下面这段代码可以直接抄进实验报告但我建议你先不看答案自己写一遍卡住了再对照。#include iostream using namespace std; void decToBin(int n) { if (n 0) { // 边界处理不能漏 cout 0; return; } int bin[32]; // int类型32位足够 int idx 0; while (n 0) { bin[idx] n % 2; // 取余存低位 n / 2; // 整除准备下一轮 } for (int i idx - 1; i 0; i--) { // 倒序输出 cout bin[i]; } } int main() { int n; cin n; decToBin(n); cout endl; return 0; }这段代码有两个细节需要重点说明。第一数组大小设为32是因为int类型在绝大多数编译环境下是32位能覆盖全部非负int值的二进制位数。第二while循环结束的条件是n 0如果写成n 0程序就会死循环因为n不断被2整除永远不会大于等于0的同时跳出——它会一直除以2直到变成0再除以2还是0无限循环下去。这个错误非常经典见过不少同学在循环条件上翻车。运行效果输入: 13 输出: 11012.3 写法二递归的另类视角递归写法代码量少得惊人只用了三行函数体却能完美体现“逆序输出”的本质。核心逻辑是先把n除以2的商递归地转成二进制再输出当前余数。因为递归是“先深入到最底层再一层层返回”所以高层次的值反而先被打印出来天然实现了倒序。#include iostream using namespace std; void decToBin(int n) { if (n 1) { decToBin(n / 2); } cout n % 2; } int main() { int n; cin n; decToBin(n); cout endl; return 0; }注意这里递归的终止条件我写的是n 1而不是n 0。如果是n 0那么n为1时也会递归调用decToBin(0)最终多输出一个0。写成n 1后n为1或0时都不再递归直接输出n % 2结果完全正确。这个细节我试过初学阶段很少能一次写对但理解了调用栈之后你就会发现递归在这里是比数组更自然的解决方案。关于递归的性能二进制的位数是O(log n)所以递归深度非常浅完全不需要担心栈溢出。实际递归调用次数也就二三十次对于int范围内任意数字来说绰绰有余。2.4 写法三位运算一步到位如果你已经熟悉位运算还有一条更“计算机味道”的路线直接判断n的每一位是0还是1。方法是让n逐位右移再和1做按位与运算。第i位是1还是0取决于(n i) 1的结果。#include iostream using namespace std; int main() { int n; cin n; bool flag false; // 用flag跳过前导0 for (int i 31; i 0; i--) { int bit (n i) 1; if (bit ! 0) flag true; if (flag) cout bit; } if (!flag) cout 0; // n为0时补一个0 cout endl; return 0; }位运算写法的好处是不需要数组也不需要递归直接把“每一位是几”算出来。但坏处是必须用flag处理前导0的问题——如果不加flag那么输入13时会输出一个31位长度的“000...01101”一大堆前导0显然不是我们想要的。这个flag本质上是一个开关一旦遇到第一个1后面的0才允许被输出。三种写法我建议都亲手敲一遍。数组写法是基本功递归写法能加深对函数调用栈的理解位运算写法能帮你打通计算机组成原理课上学的“数据在内存里就是0和1的排列”这个认知。实验报告里如果能把三种方法横向对比再各附一段运行截图老师很难不给高分。2.5 全局观察二进制和计算机的关系做完十转二建议你停下来想想一个问题为什么偏偏是二进制因为计算机硬件层面只有“高电平、低电平”两种稳定状态用0和1来表示最可靠。这意味着你在代码里写的所有int、float、char在内存里都是二进制形态。十转二的实验看起来只是“把一个数换一种写法”实际上背后是人类计数习惯与计算机存储方式之间的翻译过程。理解了这一层再去看补码、位运算、浮点数的存储结构都会顺很多。3. 杨辉三角形从递推公式到漂亮输出的完整实现3.1 数学原理这其实是二项式系数表杨辉三角形在课本上有时候叫帕斯卡三角形它每行每个位置的数都对应着二项式(ab)^n展开后各项的系数。比如(ab)^3 a^3 3a^2b 3ab^2 b^3系数是1、3、3、1正好是杨辉三角的第4行从0行开始数。这个背景很多实验报告里都会写但它直接引出了最核心的递推公式第i行第j列的数 第i-1行第j-1列的数 第i-1行第j列的数写成代码就是a[i][j] a[i-1][j-1] a[i-1][j]。每一行的最左边和最右边都是1因为任何一行第0列和“第i行第i列”都只能从上一行的一个数累加而来边界上直接设置为1即可。这个递推关系理解清楚之后代码里的双重循环逻辑自然就出来了。如果从组合数角度理解更透彻杨辉三角第n行第k个数字就是组合数C(n,k)。而组合数本身满足C(n,k) C(n-1,k-1) C(n-1,k)这和杨辉三角的递推公式完全对应。写实验报告时把这个对应关系点出来会让报告的数学深度显著提升。3.2 标准写法二维数组递推输出下面是最标准的二维数组实现完整可运行。#include iostream #include iomanip using namespace std; int main() { int n; cout 请输入杨辉三角的行数: ; cin n; const int MAX 30; int a[MAX][MAX] {0}; // 全部初始化为0这是关键 for (int i 0; i n; i) { a[i][0] 1; // 每行第一个数 a[i][i] 1; // 每行最后一个数 for (int j 1; j i; j) { a[i][j] a[i-1][j-1] a[i-1][j]; } } // 等腰三角形输出 for (int i 0; i n; i) { for (int k 0; k n - i - 1; k) { cout ; // 每行前导空格用来居中 } for (int j 0; j i; j) { cout setw(4) a[i][j]; // 固定宽度防止数字挤在一起 } cout endl; } return 0; }代码里两处地方值得反复强调。第一int a[MAX][MAX] {0}这个初始化不能省。如果不把数组全部置0默认申请的局部数组内存里是随机值后面打印时只是没用到那些位置而已。更危险的是如果你在递推时不小心访问了未初始化的位置结果就会变成一堆乱七八糟的数字。局部变量一定要初始化这是C新手最容易忽略却最致命的坑之一。第二输出部分的setw(4)是格式化输出的核心。数字是一位数、两位数还是三位数时占用的字符宽度不同如果不统一宽度整个杨辉三角会左歪右斜。setw(4)强制每个数字占4个字符宽度不足的部分补空格这就保证了每列对齐。前导空格的个数是n - i - 1每一层比下一层少一个宽度单位视觉上形成居中效果。运行效果输入51 1 1 1 2 1 1 3 3 1 1 4 6 4 13.3 优化写法一维滚动数组降低空间占用二维数组写法直观但有个明显的浪费杨辉三角是三角形的数据用方形矩阵存必然有一半空间闲置。而且我们计算第i行时只用到第i-1行的数据更早的行在计算完成后就不再需要了。基于这个观察可以用一维滚动数组把空间复杂度从O(n^2)降到O(n)。#include iostream #include iomanip using namespace std; int main() { int n; cin n; int arr[30] {0}; for (int i 0; i n; i) { for (int j i; j 1; j--) { // 必须从后往前更新 arr[j] arr[j] arr[j-1]; } arr[0] 1; // 每行开头始终是1 for (int k 0; k n - i - 1; k) cout ; for (int j 0; j i; j) { cout setw(4) arr[j]; } cout endl; } return 0; }这段代码最需要理解的是内层循环的更新顺序for (int j i; j 1; j--)必须从大往小更新。如果从小到大更新arr[j]已经更新成当前行的值再算arr[j1]时用到的就是新值而不是上一行的旧值整个递推就串了。这就是“滚动数组”典型的实现细节。我第一次教这个知识点时十个人里有五个人在这里翻车做实验时务必留意。空间从二维矩阵压缩成一维数组后整个程序的内存占用小了很多。虽然实验课不会真在意这点内存但“为什么要这样优化”的思路本身就值得在报告里写几行。3.4 组合数直接计算写法选看除了递推也可以直接用组合数公式计算每个位置的值。C(n,k) n! / (k! * (n-k)!)但直接算阶乘有两个问题一是int很容易溢出n到13左右阶乘就超了二是重复计算严重每个位置都算一遍阶乘效率很低。更聪明的做法是利用递推关系C(n,k) C(n,k-1) * (n-k1) / k从C(n,0)1开始一个一个推下去。#include iostream #include iomanip using namespace std; int main() { int n; cin n; for (int i 0; i n; i) { long long val 1; // C(i, 0) 1 for (int k 0; k n - i - 1; k) cout ; for (int j 0; j i; j) { if (j 0) { val val * (i - j 1) / j; // 递推计算组合数 } cout setw(4) val; } cout endl; } return 0; }这个写法的好处是不需要二维数组坏处是乘除交替过程中如果中间值溢出结果就错了。用long long可以把适用范围扩大不少但仍然是“数学漂亮、工程上有限制”的代表。实验报告把这个当作第三种思路列出即可不建议作为主方案因为递推法更符合C数组知识的教学目标。3.5 输出美化从“能跑”到“好看”评审老师看实验报告时第一眼看到的不是代码逻辑而是运行截图里杨辉三角的样子。一个居中的等腰三角和一个歪斜的直角三角印象分差距很大。我自己常用的“美化三步法”分享给你第一步统一数字宽度。用setw(4)或更大的宽度具体视输出行数决定。行数越多最大数字位数越多宽度也要相应调大但注意宽度过大行就会拉得很长。第二步确定前导空格。每行开头输出n - i - 1个双空格配合每行数字占4字符宽就能形成左右大致对称的轮廓。严格来说因为数字位数不同不可能做到像素级居中但视觉上足够美观。第三步行数控制在合理范围。超过15行之后数字位数增加整体宽度会非常大显示效果反而变差。实验课一般输出5到10行就够了。4. 调试、环境配置与实验报告写作经验4.1 用什么环境跑这些代码这两个实验题目都非常基础几乎任何C环境都能运行。我用过Dev-C和VS Code两种给你讲讲差异。Dev-C是很多高校机房预装的轻量IDE打开即用非常适合新手。它的编译器是MinGW GCC对实验课这种单文件程序完全够用。缺点是编辑器提示能力弱调试功能简陋但对本题而言无伤大雅。如果你用的是VS Code需要自己配置C/C环境。步骤如下先安装MinGW-w64并配置bin目录到系统环境变量的PATH中然后在VS Code里安装C/C扩展插件之后写代码时点右上角运行按钮即可。编译命令本质上就是g main.cpp -o main ./main。这里要特别注意VS Code的C/C扩展只负责语法高亮和智能提示真正的编译工作还是由g完成的很多同学不知道这一点配置半天发现代码没法运行就是卡在编译器没装上。我个人的习惯是命令行直接编译运行g -stdc11 -Wall main.cpp -o main-Wall选项可以显示所有警告。实验课阶段建议把警告全开因为像“数组越界”“未初始化的变量”这类问题编译器给了警告你没看到后面找bug的时间会更长。4.2 常见问题速查表这两个题目的报错和逻辑问题我见得实在太多整理成一张速查表你写完代码后逐条对照排查比盲目乱试效率高得多。症状可能原因解决方法十进制转二进制结果完全反了输出时从数组头部向后遍历改成从最后一个有效元素倒序遍历输入0时什么都不显示没有处理0的边界情况在函数开头判断n0直接输出0杨辉三角数字乱码/超大随机数数组未初始化声明时用{0}全部置0杨辉三角第一行多出0二维数组对角线初始化遗漏给每行的a[i][i] 1三角形左歪右斜、参差不齐缺少setw固定宽度输出数字前加cout setw(4)杨辉三角数字是上一行的值滚动数组更新顺序错误内层循环改为从后往前更新程序运行后卡住不动while或for的循环条件写错检查循环条件是否可能永远满足最后一行数字右移了一位前导空格计算错误确认空格数是n-i-1不是n-i排查逻辑还有一个通用心得先确认算法再确认边界最后看格式。如果运行结果数字全对只是丑那是格式化问题如果数字本身错了一定是递推或循环哪里出了问题这时候别急着调输出老老实实回到算法上用笔在纸上手算几行对照代码逐行走一遍问题基本就找到了。4.3 实验报告写作心得代码能跑只是拿基础分实验报告写得好才能拿高分。我总结几个实用的写作思路代码块格式必须规范。用等宽字体或代码块环境代码缩进统一函数命名有意义。老师一天改几十份报告一份排版混乱的报告哪怕逻辑全对阅读体验也极差印象分会受影响。运行结果截图要有代表性。十转二至少截一组正常输入的结果最好再截一组输入0的结果展示边界处理杨辉三角截一个至少5行的等腰三角能看出格式对齐效果。原理讲解部分用你自己的话复述。最忌讳大段抄书。你哪怕只是把“除2取余法的本质是计算各二进制位的权值”用自己的话写明白都比抄三段教材更有价值。做一个“扩展思考”小节。这招亲测有效。比如在实验报告的末尾写一段“本实验还可以做哪些扩展”举出支持负数转换、输入超大整数用字符串转二进制、杨辉三角输出到文件等方向不必把代码完全写出来只描述思路。老师看到这一节会认为你学有余力、有思考深度平时分自然好。4.4 一个容易忽略的数学小细节杨辉三角每一行所有数字之和恰好是2的n次方从第0行开始数。比如第3行1、3、3、1相加等于8正好是2^3。这其实也是二项式定理的一个直接推论。这个性质用来做程序自检特别方便如果你不确定自己递推出来的某一行对不对把这行的所有数加一下看是否等于2的行号次幂。我在教学生的时候经常让他们写完代码后用这个技巧验证结果比肉眼一个个对数字快得多。同理十进制转二进制也有一个验证技巧算完后手动展开每一位乘以2的对应次幂再求和看是否等于输入的原数。这两个“验算小妙招”放在实验报告的运行结果分析里显得你对自己代码的正误有客观判断标准别人看到会觉得你很严谨。说实话这两个实验题目我都不知道写过多少遍了但每次铺开纸讲原理时还是觉得它们特别适合做C入门的“试金石”。十转二考验的是对循环和逆序的直觉杨辉三角考验的是二维数组和格式化输出单独拎出来都不难组合在一起恰好覆盖了C最核心的编程肌肉记忆。做完这两个实验你再去碰指针、链表、类那些硬核内容手感会完全不同。最后再分享一个小习惯我喜欢在完成实验后把代码再精简一轮去掉所有不需要的临时变量把magic number比如数组大小32、30提取成常量。不是为了炫技而是因为后续真正做项目时代码可读性和可维护性比“能跑”重要得多。从这两个简单的实验开始养成习惯你会感谢大一时认真对待这些细节的自己。
