C语言编程入门:数值比较、字母转换与阶乘计算实战解析
1. 项目概述从三个小程序窥见C语言的编程逻辑最近在带几个刚入门C语言的朋友做练习发现很多新手在学完基础语法后面对“写一个完整程序”的任务依然会感到无从下手。他们不缺零散的知识点缺的是将这些知识点串联起来解决实际问题的能力。于是我选了三个非常经典的小程序作为实战案例数值比较、字母转换和阶乘计算。这三个程序看似简单却分别对应了C语言中条件判断、字符处理与循环控制三大核心编程逻辑是构建任何复杂程序的基石。通过这次实战我希望你能掌握的不仅仅是这三个程序怎么写而是理解其背后的设计思路如何将一个问题分解成计算机能执行的步骤如何选择合适的数据类型和控制结构以及如何调试和优化你的代码。无论你是想用C语言做嵌入式开发、系统编程还是仅仅为了通过考试这套从问题到代码的思考过程都至关重要。接下来我们就从最基础的数值比较开始一步步拆解。2. 核心需求与设计思路拆解2.1 为什么是这三个程序在开始敲代码之前我们先花点时间聊聊为什么选这三个程序作为入门实战。这绝非随意挑选而是因为它们精准地覆盖了结构化程序设计的三个基本控制结构顺序、选择和循环。数值比较核心是选择结构分支判断。程序需要根据输入数据的大小关系决定执行哪一段代码。这涉及到关系运算符,,和if-else语句的使用。更深一层它训练的是“条件化处理”的思维这是实现程序智能比如自动判断成绩等级、控制设备开关的基础。字母转换核心是顺序结构中的字符处理。它看起来简单但背后是ASCII码的理解与应用。计算机不认识‘a’或‘A’它只认识数字。大小写转换的本质是对内存中存储的ASCII码值进行算术运算。这个程序让你直面“数据在计算机中如何表示”这一根本问题。阶乘计算核心是循环结构。计算5的阶乘5! 12345你不可能写5行乘法这就需要循环for或while来重复执行相似操作。循环是让计算机发挥其“不知疲倦”优势的关键从遍历数组到处理大量数据无处不在。把这三种结构练熟你就有能力去组合它们解决更复杂的问题。比如一个学生成绩管理系统需要循环读取多个学生成绩循环对每个成绩判断等级选择并可能将姓名格式化输出字符处理。2.2 环境准备与工具选择工欲善其事必先利其器。对于C语言初学者我不建议一开始就追求复杂的IDE集成开发环境。一个轻量级的编辑器加上可靠的编译器能让你更专注于代码本身。编译器这是将你写的C代码翻译成机器指令的核心工具。Windows平台首选MinGW-w64或TDM-GCC。它们都是GCC编译器在Windows上的移植版本稳定且免费。安装时记得勾选“添加到系统环境变量”的选项这样你才能在命令行中直接使用gcc命令。macOS平台安装Xcode Command Line Tools。打开终端输入xcode-select --install即可。它包含了LLVM Clang编译器。Linux平台通常系统自带GCC。可通过gcc --version检查若没有使用包管理器安装如Ubuntu的sudo apt install gcc。代码编辑器推荐使用Visual Studio Code (VSCode)。为什么选它轻量、免费、插件生态丰富。通过安装C/C扩展包你可以获得代码高亮、智能提示IntelliSense、一键编译运行、调试等功能体验接近IDE但又保持了编辑器的简洁。关键配置安装官方扩展C/C(由Microsoft发布)。配置任务Tasks用于一键编译。可以创建一个简单的.vscode/tasks.json文件定义用gcc编译当前文件的命令。配置调试Launch虽然小程序可能用不上调试但提前熟悉断点、单步执行对理解程序流程极有帮助。替代方案如果你喜欢开箱即用也可以使用Dev-C或Code::Blocks这类轻量级IDE。它们内置了编译器无需复杂配置。注意无论选择什么工具请确保你知道如何用命令行手动编译一个C文件。这是理解构建过程的关键。基本命令是gcc -o 输出程序名 源代码.c例如gcc -o compare compare.c。3. 程序一数值比较——掌握条件分支的艺术3.1 问题定义与输入输出设计我们先从最简单的开始编写一个程序从用户那里获取两个整数然后比较它们的大小输出类似“第一个数更大”、“第二个数更大”或“两数相等”的结果。输入/输出I/O设计输入两个整数通过键盘输入。输出一句清晰描述比较结果的话。这个设计看似直白但已经包含了程序与用户交互的基本模型提示输入 - 接收数据 - 处理数据 - 展示结果。3.2 代码实现与逐行解析下面是一个完整的实现我们边看代码边讲解#include stdio.h // 标准输入输出头文件printf和scanf都需要它 int main() { int num1, num2; // 步骤1声明两个整型变量用于存储用户输入 // 步骤2提示用户输入并读取数据 printf(请输入第一个整数: ); scanf(%d, num1); // 是取地址符告诉scanf把读到的整数放到num1的地址里 printf(请输入第二个整数: ); scanf(%d, num2); // 步骤3核心逻辑——比较并输出 if (num1 num2) { printf(%d 大于 %d\n, num1, num2); } else if (num1 num2) { printf(%d 小于 %d\n, num1, num2); } else { // 当 num1 既不大于也不小于 num2 时它们必然相等 printf(%d 等于 %d\n, num1, num2); } return 0; // 程序正常结束向操作系统返回0 }关键点解析#include stdio.h这是预处理指令告诉编译器在编译前把stdio.h这个头文件的内容“复制粘贴”过来。printf和scanf函数的声明就在这里面没有它编译器就不认识这两个函数。int main()每个C程序都必须有一个main函数它是程序执行的起点。int表示这个函数执行完毕后会返回一个整数值给操作系统通常0表示成功。变量声明int num1, num2;在C语言中变量必须先声明后使用。这里声明了两个int整数类型的变量。scanf的这是新手最容易出错的地方。scanf(“%d”, num1)中的num1意思是“变量num1的地址”。scanf需要知道把读取到的数据放在内存的哪个位置所以必须传递地址。如果写成scanf(“%d”, num1)程序很可能会崩溃。if-else if-else结构这是多分支判断的标准写法。程序会从上到下依次检查条件一旦某个if或else if的条件为真非零就执行它后面的代码块然后跳过整个if-else结构剩余的部分。3.3 边界情况与健壮性思考上面的程序在用户乖乖输入整数时工作良好。但现实是用户可能会输入字母、符号或者什么都不输入。这就引出了程序健壮性的概念。一个不健壮的场景如果用户输入了12ascanf(“%d”, ...)会读取前面的数字12但字母a会留在输入缓冲区。当下一个scanf执行时它会直接读取这个a导致读取失败变量值不被更新可能是随机值程序输出错误结果。如何改进初级 我们可以检查scanf的返回值。scanf函数返回成功读取并赋值的变量个数。int main() { int num1, num2; printf(请输入两个整数用空格隔开: ); if (scanf(%d %d, num1, num2) 2) { // 成功读取了两个整数 if (num1 num2) { printf(%d %d\n, num1, num2); } else if (num1 num2) { printf(%d %d\n, num1, num2); } else { printf(%d %d\n, num1, num2); } } else { printf(输入错误请确保输入的是两个整数。\n); // 清空输入缓冲区防止错误输入影响后续操作如果需要 while (getchar() ! \n); // 读取并丢弃缓冲区中直到换行符的所有字符 } return 0; }这个版本通过检查scanf的返回值初步处理了非法输入并给出了友好的错误提示。while (getchar() ! ‘\n’);这行代码是一个常用的技巧用于清空标准输入缓冲区中残留的字符避免它们影响下一次输入。实操心得在初学阶段养成检查关键函数如scanf,malloc,fopen返回值的习惯是写出稳定程序的第一步。虽然这会让代码稍显冗长但能避免很多莫名其妙的bug。4. 程序二字母大小写转换——理解数据在内存中的本质4.1 ASCII码字符背后的数字世界在深入代码之前必须理解一个核心概念ASCII码。计算机内存中存储的只有数字二进制。为了表示字符人们制定了一个对照表给每个常用字符分配了一个数字编号。例如大写字母A的ASCII码是65小写字母a的ASCII码是97数字字符0的ASCII码是48观察规律同一个字母的大小写ASCII码值相差32。‘a’ - ‘A’ 97 - 65 32。这就是大小写转换的数学基础。4.2 两种转换策略的实现知道了原理实现就简单了。我们设计一个程序输入一个字母如果是大写则输出其小写形式如果是小写则输出其大写形式。策略一基于ASCII码值的算术运算这是最直接、效率最高的方法直接操作字符的整数值。#include stdio.h int main() { char ch; printf(请输入一个字母: ); scanf(%c, ch); // 判断是否为大写字母ASCII码值在 A(65) 到 Z(90) 之间 if (ch A ch Z) { // 转换为小写大写字母ASCII码 32 ch ch 32; printf(转换后的小写字母是: %c\n, ch); } // 判断是否为小写字母ASCII码值在 a(97) 到 z(122) 之间 else if (ch a ch z) { // 转换为大写小写字母ASCII码 - 32 ch ch - 32; printf(转换后的大写字母是: %c\n, ch); } else { printf(输入的不是英文字母\n); } return 0; }代码亮点我们直接用字符常量‘A’、‘Z’进行比较而不是数字65和90。这样写意图更清晰可读性更好编译器会自动将它们替换为对应的ASCII码值。转换操作就是简单的加/减32。策略二使用C标准库函数C语言的标准库ctype.h提供了一系列专门处理字符的函数让代码更简洁、更可移植因为考虑了非ASCII编码如EBCDIC。#include stdio.h #include ctype.h // 引入字符处理函数库 int main() { char ch; printf(请输入一个字母: ); scanf(%c, ch); if (isupper(ch)) { // 判断是否为大写字母 ch tolower(ch); // 转换为小写 printf(转换后的小写字母是: %c\n, ch); } else if (islower(ch)) { // 判断是否为小写字母 ch toupper(ch); // 转换为大写 printf(转换后的大写字母是: %c\n, ch); } else { printf(输入的不是英文字母\n); } return 0; }两种策略对比特性算术运算策略库函数策略原理直接操作ASCII码值调用封装好的库函数可读性一般需要了解ASCII规则好函数名即意图islower,toupper可移植性较差假设了ASCII编码绝大多数系统是好库函数内部处理了编码差异性能极高就是一次加法/减法高函数调用有轻微开销但可忽略推荐场景对性能有极致要求的底层代码、嵌入式环境绝大多数应用开发追求代码清晰和安全注意事项ctype.h中的函数如isupper,tolower的参数类型是int但实际传入char是安全的因为char类型在参与表达式计算时会自动提升为int。这些函数内部通过查表实现效率也很高。4.3 扩展处理字符串的转换单个字符的转换学会了那如何转换一个完整的单词或句子呢这就需要用到数组和循环。我们提前预习一下循环的概念。#include stdio.h #include ctype.h #include string.h // 用于strlen函数 int main() { char str[100]; // 声明一个最多存储99个字符1个结束符‘\0’的字符数组 int i; printf(请输入一个字符串: ); fgets(str, sizeof(str), stdin); // 使用fgets安全地读取一行字符串包括空格 // 遍历字符串中的每个字符直到遇到字符串结束符‘\0’ for (i 0; str[i] ! \0; i) { if (islower(str[i])) { str[i] toupper(str[i]); // 小写转大写 } // 可以添加else if处理大写转小写这里演示全转大写 } printf(转换后的字符串(全大写)是: %s, str); return 0; }这个扩展程序引入了几个新概念字符数组char str[100]用于存储一串字符。fgets比scanf(“%s”, …)更安全的字符串输入函数它可以读取空格并指定最大读取长度防止数组越界缓冲区溢出。循环遍历for循环从下标0开始依次处理每个字符str[i]直到遇到‘\0’C语言字符串的结束标志。原地修改直接在原数组str上修改每个字符。通过这个扩展你将字符处理与循环、数组结合了起来向处理真实数据迈出了一大步。5. 程序三阶乘计算——深入循环与递归的奥秘5.1 问题分析从数学定义到算法阶乘的定义很简单n! 1 × 2 × 3 × … × n(其中0! 1)。对于计算机来说这就是一个累乘的过程。我们需要一个变量比如叫result来保存每次乘法的结果初始值为1。然后让一个计数器比如i从1循环到n每次执行result result * i。这里立刻引出一个关键问题阶乘的结果增长极快。10! 3,628,80012! 479,001,60013! 6,227,020,800。而C语言中基本数据类型的表示范围是有限的int32位系统通常范围是-2,147,483,648 到 2,147,483,647。12!还在这个范围内但13!已经超出了。unsigned long long这是C语言中常用的最大基础整数类型范围通常是0 到 18,446,744,073,709,551,615。它能表示到20!(约2.43e18)21!也会溢出。所以在编写程序时我们必须考虑输入范围和数据类型的选择这是工程思维的重要体现。5.2 迭代法实现使用for循环迭代法循环是最直观、最高效的实现方式。#include stdio.h // 使用 unsigned long long 以支持更大的阶乘结果 unsigned long long factorial_iterative(int n) { if (n 0) { // 阶乘通常定义在非负整数上负数输入视为错误 return 0; // 我们可以用0表示错误或者用其他方式处理 } unsigned long long result 1; // 0! 和 1! 都等于1 for (int i 2; i n; i) { // 从2开始乘效率稍高一点 result * i; // 等价于 result result * i; } return result; } int main() { int num; printf(请输入一个非负整数: ); scanf(%d, num); unsigned long long fact factorial_iterative(num); if (num 0) { printf(错误阶乘未定义于负数。\n); } else if (fact 0 num 0) { // 注意当n较大导致溢出时result可能会回绕变成0或其他值 // 更严谨的做法是判断乘法是否溢出这里简化处理 printf(警告结果可能已超出 unsigned long long 的表示范围\n); } else { printf(%d! %llu\n, num, fact); // %llu 用于格式化输出 unsigned long long } return 0; }代码精讲函数封装我们将阶乘计算逻辑封装成factorial_iterative函数。这是一个好习惯提高了代码的模块化和可重用性。参数检查函数开头检查n是否为负这是防御性编程。循环控制for (int i 2; i n; i)是核心。i从2开始因为1乘任何数都不变。循环条件i n确保了会乘到n。溢出处理这是一个简化版本。在实际工程中我们需要在result * i之前判断乘法是否会溢出。例如可以检查result ULLONG_MAX / iULLONG_MAX是limits.h中定义的unsigned long long最大值。5.3 递归法实现函数的自我调用递归是另一种强大的编程技巧。递归函数的特点是自己调用自己。阶乘的递归定义是n! n * (n-1)!且0! 1。这天然适合用递归实现。#include stdio.h unsigned long long factorial_recursive(int n) { // 基线条件Base Case递归的出口防止无限调用 if (n 0) return 0; // 错误处理 if (n 0 || n 1) { return 1; } // 递归步骤Recursive Step将问题分解为更小的同类问题 return n * factorial_recursive(n - 1); } int main() { int num; printf(请输入一个非负整数: ); scanf(%d, num); unsigned long long fact factorial_recursive(num); if (num 0) { printf(错误阶乘未定义于负数。\n); } else { printf(%d! %llu (递归法计算)\n, num, fact); } return 0; }递归过程剖析以计算factorial_recursive(3)为例factorial_recursive(3)发现n3不满足基线条件于是执行return 3 * factorial_recursive(2)。但此时factorial_recursive(2)还不知道结果所以计算暂停先去计算factorial_recursive(2)。factorial_recursive(2)执行return 2 * factorial_recursive(1)。同样需要先去计算factorial_recursive(1)。factorial_recursive(1)满足基线条件n1直接return 1。结果开始回溯factorial_recursive(2)拿到了factorial_recursive(1)返回的1计算2 * 1 2然后return 2。factorial_recursive(3)拿到了factorial_recursive(2)返回的2计算3 * 2 6然后return 6。最终main函数中的fact变量得到了结果6。5.4 迭代与递归的深度对比特性迭代法 (for/while循环)递归法原理通过循环重复执行一段代码。函数调用自身将大问题分解为小问题。性能通常更高。只有函数调用的开销没有额外的栈开销。较低。每次递归调用都会在内存栈中分配空间存在函数调用和上下文切换开销。深度递归可能导致栈溢出。内存占用恒定内存几个变量。占用内存与递归深度成正比。计算factorial_recursive(100)需要约100层栈帧。代码简洁性对于阶乘两者都简洁。对于符合递归定义的问题如树遍历、汉诺塔代码极其简洁直观。思维难度符合直接的“循环累乘”思维。需要理解“自我调用”和“回溯”过程思维更抽象。适用场景绝大多数需要重复操作的场景。问题本身是递归定义的分治、回溯、树/图遍历。重要警告对于阶乘这种问题强烈推荐使用迭代法。递归实现虽然数学上优美但存在明显的性能缺陷和栈溢出风险。这里介绍递归主要是为了让你理解这个概念它在解决其他特定类型问题时无可替代。6. 程序整合与功能扩展6.1 构建一个简单的交互式菜单现在我们把三个独立的小程序整合成一个带有菜单的“小程序集”让用户可以选择要运行的功能。这涉及到switch-case语句和循环的运用。#include stdio.h #include ctype.h #include string.h // 函数声明 int compare_numbers(); void convert_letter(); void calculate_factorial(); void clear_input_buffer(); // 用于清空输入缓冲区的辅助函数 int main() { int choice; do { // 打印菜单 printf(\n C语言基础小程序集 \n); printf(1. 数值比较\n); printf(2. 字母大小写转换\n); printf(3. 阶乘计算\n); printf(0. 退出程序\n); printf(请选择功能 (0-3): ); if (scanf(%d, choice) ! 1) { // 处理非数字输入 printf(输入无效请输入数字\n); clear_input_buffer(); continue; } clear_input_buffer(); // 清空缓冲区中可能存在的换行符等 switch (choice) { case 1: compare_numbers(); break; case 2: convert_letter(); break; case 3: calculate_factorial(); break; case 0: printf(感谢使用再见\n); break; default: printf(无效的选择请输入0到3之间的数字。\n); break; } } while (choice ! 0); // 当用户不选择0时循环继续 return 0; } // 清空标准输入缓冲区 void clear_input_buffer() { int c; while ((c getchar()) ! \n c ! EOF); // 一直读取直到换行符或文件尾 } // 功能1数值比较 int compare_numbers() { int a, b; printf(\n[数值比较功能]\n); printf(请输入两个整数用空格隔开: ); if (scanf(%d %d, a, b) 2) { if (a b) printf(结果: %d %d\n, a, b); else if (a b) printf(结果: %d %d\n, a, b); else printf(结果: %d %d\n, a, b); } else { printf(输入错误\n); } clear_input_buffer(); return 0; } // 功能2字母转换 (这里实现字符串转换) void convert_letter() { char str[100]; int i; printf(\n[字母大小写转换功能]\n); printf(请输入一个字符串: ); if (fgets(str, sizeof(str), stdin) ! NULL) { // 移除末尾可能的换行符 str[strcspn(str, \n)] \0; for (i 0; str[i] ! \0; i) { if (islower(str[i])) str[i] toupper(str[i]); else if (isupper(str[i])) str[i] tolower(str[i]); } printf(转换结果: %s\n, str); } } // 功能3阶乘计算 (迭代法) void calculate_factorial() { int n; unsigned long long result 1; printf(\n[阶乘计算功能]\n); printf(请输入一个非负整数: ); if (scanf(%d, n) 1 n 0) { for (int i 2; i n; i) { // 简单的溢出检查不严谨仅示意 if (result ULLONG_MAX / i) { printf(警告在计算到 %d! 时发生溢出\n, i); result 0; break; } result * i; } if (result ! 0) { printf(%d! %llu\n, n, result); } } else { printf(输入错误请输入一个非负整数。\n); } clear_input_buffer(); }这个整合程序展示了如何构建一个简单的命令行交互界面。关键点包括do-while循环保证菜单至少显示一次并根据用户选择重复显示。switch-case语句根据整数choice的值跳转到不同的功能模块。模块化函数每个功能都是一个独立的函数main函数只负责调度。这使得代码结构清晰易于维护和扩展。输入缓冲区的管理这是交互式程序的难点。混合使用scanf和fgets时缓冲区中残留的换行符‘\n’会导致fgets直接读取空行。clear_input_buffer函数就是用来解决这个问题的。6.2 扩展思路让程序更实用掌握了基础之后你可以尝试以下扩展让程序变得更强大文件操作将每次比较、转换或计算的结果不仅输出到屏幕也追加写入到一个文本文件如result.txt中。这需要学习FILE *,fopen,fprintf,fclose等文件操作函数。批量处理修改“字母转换”程序使其能读取一个文本文件将整个文件的内容进行大小写转换后输出到另一个新文件中。错误日志将程序运行中的错误信息如非法输入、计算溢出写入一个单独的error.log文件方便后期排查问题。支持大数阶乘使用数组或字符串来模拟超大整数的存储和乘法运算实现计算100!甚至1000!的功能。这是算法和数据结构的经典练习。7. 常见问题与调试技巧实录即使理解了所有原理实际编写和运行代码时也一定会遇到问题。下面是我在初学及教学过程中总结的一些高频“坑点”和解决技巧。7.1 编译与链接错误问题undefined reference to ‘printf’或undefined reference to ‘scanf’。原因编译器找到了printf的声明在stdio.h中但链接器找不到它的实现。这通常发生在极简的编译环境或交叉编译时。解决确保你链接了C标准库。对于GCC编译命令通常是gcc -o myprogram myprogram.c它会自动链接标准库。在某些特殊环境如嵌入式可能需要手动指定-lc。问题error: stray ‘\xxx’ in program。原因源代码中出现了非法字符最常见的是直接复制了中文引号、分号或空格。解决仔细检查报错行附近的所有标点符号确保都是英文半角符号。在编辑器中开启“显示空白字符”功能会很有帮助。7.2 运行时逻辑错误问题数值比较程序无论输入什么输出都是“两数相等”。排查检查scanf语句是否漏写了取地址符。scanf(“%d”, num1)是错误的。在scanf后立即打印两个变量的值确认它们是否正确接收了输入。检查if-else的条件逻辑是否误写成了赋值而不是比较。例如if (num1 num2)会把num2的值赋给num1然后判断赋值后的num1是否为非零逻辑完全错误。问题字母转换程序输入后程序直接跳过没有等待输入或输出奇怪结果。排查缓冲区残留问题这是最常见的原因。如果前面有scanf(“%d”, …)它读取了数字但留下了换行符‘\n’。紧接着的scanf(“%c”, ch)会立刻读取这个‘\n’导致程序“跳过”。解决在读取字符前清空缓冲区。可以使用我们上面写的clear_input_buffer()函数或者在scanf格式字符串中加入空格来消耗空白字符scanf(” %c”, ch)注意%c前的空格。问题阶乘程序输入稍大的数如20结果是0或一个很小的数。排查整数溢出这是最可能的原因。20!的结果远远超过了unsigned long long能表示的范围。验证在循环内加入打印语句观察每次乘法后result的值看看是在哪一步开始出现异常比如数值突然变小或变负。解决实现严格的溢出检查或改用大数运算库/自己实现大数乘法。7.3 调试Debug基础技巧调试是比写代码更重要的技能。不要只靠printf。使用调试器GDB/LLDB编译时加入调试信息gcc -g -o myprogram myprogram.c启动调试gdb ./myprogram常用命令break main或b 行号在函数或某行设置断点。run或r运行程序。next或n单步执行不进入函数内部。step或s单步执行进入函数内部。print 变量名或p 变量名打印变量的当前值。continue或c继续运行直到下一个断点或程序结束。quit或q退出GDB。在VSCode中图形化调试配置好launch.json后可以在代码左侧点击设置断点红点。按F5启动调试程序会在断点处暂停。你可以看到所有变量的当前值并可以逐行执行代码。这比命令行GDB直观得多。“橡皮鸭调试法” 当你觉得代码没问题但就是不对时试着向一个不懂编程的人或者桌上的橡皮鸭一行一行解释你的代码在做什么。在解释的过程中你常常会自己发现逻辑上的漏洞。这是一个极其有效的方法。7.4 编码风格与习惯养成好的习惯从第一个程序开始培养。命名变量、函数名要见名知意。用num1,result,factorial而不是a,b,c。注释在复杂的逻辑块前写注释解释“为什么这么做”而不是“做了什么”代码本身应该能表达做了什么。缩进与空格保持一致的缩进通常是4个空格或1个Tab。运算符两边加空格增加可读性。例如result old_value * factor offset;。错误处理永远不要假设用户的输入是正确的也不要假设文件一定能打开内存一定能申请到。检查scanf,fopen,malloc的返回值。这三个小程序就像三块坚实的积木。数值比较教会你如何让程序做决策字母转换让你看清数据在内存中的真实面貌而阶乘计算则展示了如何让计算机高效地重复劳动。当你把它们组合起来加上菜单和错误处理一个具备基本交互能力的命令行工具就诞生了。编程的学习路径就是如此理解微观的语法细节掌握中观的逻辑结构顺序、分支、循环最后在宏观上通过项目实践将它们融会贯通。下一步你可以尝试用文件操作来持久化数据或者挑战一下用数组实现大数阶乘那会是另一个有趣的开始。