
1. 顺序结构不是知识点而是程序执行的底层规律很多人刚学C语言的时候会把顺序结构当成一个特别简单、翻两页就能跳过的知识点。我的看法不太一样——顺序结构不只是一个语法概念它是理解程序到底是什么的第一块基石。你可以把它理解成做菜的步骤洗菜、切菜、下锅、调味、出锅每一步都有先后顺序不能把最后一步提到最开始。C语言程序也是一样的道理。C语言在执行代码时默认就是从上到下、逐条语句依次执行这也就是顺序结构这个名字的由来。听起来很简单对不对但深入想一层既然默认就是顺序执行为什么还要专门讲顺序结构因为没有顺序作为参照你后面学选择结构if、switch和循环结构while、for时就说不清楚程序到底是怎么跳来跳去的。我见过不少学生在学到if语句时突然懵掉原因就是没建立程序是一条语句一条语句推进的心智模型。一旦你脑子里有了指令按顺序流动这条主线后面理解分支、循环、函数调用其实都是在回答同一个问题——下一步执行谁1.1 main函数程序是从哪里开始跑的C语言程序的执行入口是main函数。不管你的代码文件里声明了多少个变量、定义了多少个自定义函数真正让程序跑起来的永远是main函数里的第一条语句。#include stdio.h int main() { printf(第一步这句先执行\n); printf(第二步这句后执行\n); return 0; }这段代码的输出结果很简单第一步这句先执行 第二步这句后执行注意return 0;表示程序正常结束返回0给操作系统。这里的0是退出状态码0通常意味着一切正常。有些教程里会写return 0;有些会省略但我建议你刚入门时每次写完整这能帮你提前建立函数要有返回值的意识。提示不要把main函数里的代码写得像流水账一样密密麻麻。即使程序是按顺序执行的你也应该在每一段逻辑之前用注释标明这一步做了什么这能让代码的可读性提升好几倍。1.2 语句和表达式顺序结构的两个基本单元顺序结构的最小单位是语句。一条语句往往由一个完整的表达式加一个分号;组成。分号就是C语言里这句话说完了的句号。int age 18;声明一个整型变量age赋值为18age age 1;把age的值加1再存回ageprintf(%d, age);把age的值打印出来这三条语句如果放在一起程序会老老实实按顺序执行最终输出19。理解了这一点你就理解了顺序结构最核心的运转方式语句是一条一条执行的上一条语句的结果可能会影响下一条语句的输入。很多新手搞不清表达式和语句的区别。简单说表达式是一个有值的计算比如a b、x * 2。而语句是让程序执行一个动作比如赋值、打印、跳转。表达式加上分号就成了表达式语句比如a b;但它没有任何实际意义因为计算出来的结果被丢掉了。所以写代码时一定要想清楚这条语句到底产生了什么效果2. 入门必须打通的三个环节输入、计算、输出顺序结构在C语言里的经典形态就是输入 - 处理 - 输出三段式。写任何一个小程序脑子里先过一遍这三个环节代码结构就不会乱。我把这三个环节分别拆开讲清楚附带一些新手容易掉的坑。2.1 用scanf接收数据最容易错的是符号scanf是C语言里最常用的输入函数。它的作用是从键盘读取数据并按照你指定的格式存到变量里。基本用法是int num; scanf(%d, num);%d表示这里要读入一个整数num表示把这个整数存到num的内存地址中。这里我要重点强调一下这个符号它表示取地址。新手最容易犯的错误就是漏写导致程序运行时直接崩溃或者出现无法解释的垃圾值。注意scanf里漏写是C语言初学者最高频的错误之一。编译时可能不报错但程序一运行就异常。如果在网上搜scanf 非法地址这类问题绝大多数原因都是这里写错了。记住scanf要的是变量的地址不是变量的值。对照一下printf里就不需要int num 10; printf(%d, num);为什么printf不需要因为printf只需要拿到num的值去做格式化然后输出不需要修改num。而scanf是要往num里写入数据的所以必须告诉它该往哪写。这个区别刚学的时候一定要死记下来。读入一个小数则用%lf对应double类型、%f对应float类型。注意double对应的读入格式是%lffloat对应%f很多人在这个细节上翻车。2.2 运算符与表达式让数据活起来的关键拿到输入的数据之后顺序结构主要靠赋值表达式和算术运算来处理数据。C语言里的算术运算符一共有五个最基础的运算符作用示例结果加法7 310-减法7 - 34*乘法7 * 321/除法7 / 32%取余/取模7 % 31看到表格里的7 / 3 2了吗这里有一个极其重要的坑两个整数相除结果是整数小数部分直接丢弃。7除以3数学上是2.333...但在C语言中int类型的7除以int类型的3结果被截断为2。如果你希望得到2.333...需要把其中一个操作数改成浮点数例如7.0 / 3、(double)7 / 3或1.0 * 7 / 3。这个知识点几乎每次上机都会有人踩因为编译正常结果却永远不对劲。后面写摄氏度转换程序时你会亲身体验到这个坑。另外%取余运算符只适用于整数表示整除后的余数。比如7 % 3的结果是1。它常用于判断奇偶、判断能否整除、取数字的个位等场景。我建议你现在就养成习惯遇到是否整除的问题第一反应就是%。2.3 用printf组织输出占位符和转义字符输出环节99%的情况都用printf。它的核心思路是用一个格式字符串描述输出长什么样再用占位符把变量的值填进去。int a 5; int b 10; printf(a %d, b %d, a b %d\n, a, b, a b);输出结果为a 5, b 10, a b 15%d是整型占位符%f是浮点型占位符%c是字符型占位符%s是字符串占位符。这个表建议打印出来贴在电脑旁边初期几乎天天用到占位符对应类型示例输出%dint-100、99%ffloat3.140000%lfdouble3.140000%ccharA%s字符串hello%.2f保留两位小数的浮点数3.14转义字符\n表示换行\t表示制表符相当于按了一下Tab键。很多时候题目要求输出两行数据如果你忘记\n结果全部挤在一行里判题系统直接给你判错。所以printf里哪里该换行、哪里该加空格必须仔细。3. 把自然语言翻译成顺序语句新手最缺的一步有读者可能会说语法我都看懂了但拿到一道题还是不知道怎么写。这是正常现象因为你缺的不是语法是翻译能力——把中文题面翻译成C语言语句序列的能力。这一节我就聊怎么培养这个能力这也是顺序结构真正的价值所在。3.1 题目拆解四步法拿到任何一道编程题不要急着敲代码。先拿一支笔按下面四步拆解找输入题目要从外部读入哪些数据用什么类型存找输出题目要求输出什么格式有没有特殊要求找计算过程从输入到输出中间经历了哪些数学步骤能不能用一条或多条赋值表达式表示排顺序这些步骤哪个先做、哪个后做有没有依赖关系举个例子。题目说输入一个三位正整数输出它的个位。拆解如下输入一个整数n用int n;输出n的个位数计算个位数 n % 10顺序先读取n再计算n % 10最后输出结果代码就是#include stdio.h int main() { int n; int digit; scanf(%d, n); digit n % 10; printf(%d\n, digit); return 0; }你发现没有整个过程就是顺序结构读进来 - 算一下 - 打出去。没有分支没有循环。这就是顺序结构的标准套路先把这个套路练熟你才会有底气应对更复杂的程序。3.2 变量声明集中放还是用时再放我在代码里把int n;和int digit;放在了main函数开头。早期C标准C89要求变量声明集中在语句之前而C99及以后允许在使用前声明。现在的编译器基本都支持C99或更新的标准但很多考试环境、老旧教材的惯例仍然是变量声明放前面。这里我给一个折中建议初学阶段变量声明统一放在main函数开头。这样有什么好处代码结构清楚编译兼容性好老师改代码时也容易看懂。等到了会在循环体里临时声明循环变量的水平你自然就知道什么时候该灵活处理了。int main() { int num1; int num2; int sum; num1 12; num2 30; sum num1 num2; printf(sum %d\n, sum); return 0; }注意赋值语句的执行顺序sum num1 num2;执行时num1和num2必须已经赋过值否则sum拿到的是随机值。这就体现了顺序的重要性——语句顺序一旦颠倒逻辑全错。3.3 让代码说人话变量命名和注释很多教材示例里喜欢用a、b、c这种变量名方便是方便但可读性很差。我强烈建议你从第一天起就养成用有意义的变量名的习惯。比如计算温度的题目变量名用celsius和fahrenheit比a和b好一百倍。同时不要吝啬注释。我理解的好注释标准是给两星期后的自己看能一眼想起当时这段代码在干嘛。比如/* 把华氏温度转换为摄氏温度 */ double fahrenheit; double celsius;这段注释和变量名配合信息已经非常清楚了。程序写出来不光是给机器执行的也是给别的开发者——以及未来的你——看的。顺序结构虽然简单但一个程序的总长度拉长时毫无注释的代码谁看了都头疼。4. 新手最容易踩的几个坑以及编译器视角的真相顺序结构看似简单但因为它是所有程序的地基在实践中的各种翻车情况也很多。我在这里把新手最容易遇到的几个坑集中列一遍每一个都是我见过或者经历过的。4.1 分号多写少写程序直接变妖C语言中分号代表一条语句的结束。少写了分号编译器会报错这种还好办根据报错提示加上就行。更阴险的是多写分号比如int x 10; if (x 5); // 这里多了一个分号if条件后直接跟空语句 { printf(x 5\n); }这段代码的输出会是什么很多人会觉得什么都不输出或者条件满足后输出。然而真相是分号让if (x 5);成为一条独立的空语句if的执行体就是那个空语句后面的花括号块与if完全无关无论如何都会执行。所以在顺序结构里也要养成好习惯每个语句只写一个分号写完立刻检查。4.2 printf里占位符和数据类型不匹配double pi 3.14159; printf(%d\n, pi);这段代码能编译通过吗在很多编译器下会报警告但你可能忽略了它继续运行。结果是乱码或垃圾值——因为printf按整型的规则去解释一个double类型的二进制表示。这不是结果精度差一点的问题而是彻底读错了数据。printf和scanf里的占位符必须与变量类型严格对应不能混用。假如编译器出现warning别当没看见先把它解决掉。4.3 浮点数判等和精度问题顺序结构里你可能会写这样的判断逻辑当然现在还没学到ifdouble result 0.1 0.2;如果后面要输出result你可能会看到0.300000但也可能在某些精度约定下显示成0.30000000000000004。计算机里浮点数不是精确的十进小数它们用二进制近似存储。在学习初期的整数除法、温度换算这类题目中建议尽量用整数运算或者使用double后再格式化输出不要直接拿浮点数做相等比较。这是很多老程序员都会默认避开的坑新手提前知道能省很多时间。4.4 读入多个数据时的输入格式一道题里可能要输入两个数例如输入两个整数a和b。你可能会写scanf(%d%d, a, b);也可以写成scanf(%d %d, a, b);这两种写法都能正确读入a和b。但注意如果你写了scanf(%d, %d, a, b);那输入时就必须在中间输入一个逗号否则第二个变量读不到值。所以在用scanf时格式字符串里的字符必须精确匹配输入。我建议初学时一律用%d%d这种紧凑写法输入时用空格或换行分隔数据最不容易出错。4.5 编译报错不可怕要学会看第一行错误很多新手一看到编译器输出一堆红色错误头皮就发麻。实际上编译器报错信息里第一行高亮信息通常是最关键的。比如error: expected ; before printf这句话的意思是在printf之前少了一个分号。它甚至会告诉你发生在哪一行。只要养成看第一行错误、定位到对应行、往上一两行找问题的习惯排查会非常快。顺序结构的程序里90%的编译错误都是分号、括号、引号的问题这些都很好修。为了让你更清楚我把常见的编译/运行异常整理成一张表现象常见原因处理思路编译报错 expected ;上一行语句缺少分号按提示行号往前找编译报错 undeclared identifier变量未声明或拼写错误检查变量名和声明位置运行时输出乱码printf的%d与变量类型不匹配检查占位符与类型运行时scanf卡住或崩溃scanf漏写符或格式字符串不匹配检查和格式串是否与输入一致输出结果与数学计算结果不符整数除法截断把某个操作数转成double或浮点5. 两个带练案例从题目到验证的完整闭环讲再多理论不如实际写两个程序。我选了两个非常经典、适合顺序结构的入门题目带着你从头到尾走一遍包括验证。你自己最好也动手跑一遍。5.1 案例一摄氏度与华氏度转换题目输入一个华氏温度F输出对应的摄氏温度C。转换公式为C 5 * (F - 32) / 9结果保留一位小数。这个题目是顺序结构教科书级别的例子因为它有输入、有计算、有输出而且藏着一个典型陷阱整数除法。先看初版代码#include stdio.h int main() { double fahrenheit; double celsius; scanf(%lf, fahrenheit); celsius 5 * (fahrenheit - 32) / 9; printf(Celsius %.1f\n, celsius); return 0; }我故意设计成double类型因为温度几乎一定是小数。%lf读取double%.1f输出时保留一位小数。运算表达式5 * (fahrenheit - 32) / 9中因为fahrenheit是double所以整个表达式自动转成浮点运算不会出现整数除法截断。但如果我把代码写成int fahrenheit; int celsius; scanf(%d, fahrenheit); celsius 5 * (fahrenheit - 32) / 9; printf(Celsius %d\n, celsius);输入100输出是多少数学上应该是37.777...但程序会输出37。如果题目要求保留小数你却用了int就会被判定错误。严格来说温度的中间计算过程几乎必然涉及小数所以这里用double才是正确选型。你觉得反正保留一位小数int也能四舍五入——不对int不会四舍五入它直接把小数部分截断。写完之后一定要做几组边界测试。比如输入32输出0.0输入212输出100.0输入0输出-17.8。拿已知结果验证才能确认程序没有潜伏的问题。5.2 案例二简单数字拆分并反向输出题目输入一个三位正整数按顺序输出它的个位、十位、百位。这个题目的重点是取余和整除的组合。给定n 123如何拿到每一位个位n % 10得到3十位(n / 10) % 10n除以10得到12整数除法再对10取余得到2百位n / 100得到1对应代码#include stdio.h int main() { int n; int ones, tens, hundreds; scanf(%d, n); ones n % 10; tens (n / 10) % 10; hundreds n / 100; printf(%d %d %d\n, ones, tens, hundreds); return 0; }输入123输出3 2 1我为什么强调这个题目因为n % 10和n / 10这套组合拳你后面学逆序输出数字、统计数位、判断回文数等等题目都会反复用到。它就是用最基本的顺序结构完成了一个从具象数据到抽象技巧的训练。建议你在自己的电脑上亲手敲一遍再自己改成输入四位数、逆序输出的版本试试。5.3 验证环节从输入到输出全程测试程序写完不等于结束真正的程序员习惯是先跑通再跑对。所谓跑通就是程序能正常执行跑对是指在多组输入下结果都正确。我一般会准备三组测试数据普通数据比如123验证最常规路径边界数据比如100验证最小值附近的逻辑大一点的数据比如999验证是否会溢出或出现格式问题顺序结构的程序逻辑简单边界测试往往能直接暴露表达式问题。拿上面那个三位逆序输出来说输入100时输出应该是0 0 1如果你用%d依次输出能正常看到结果这个程序就没问题。如果某位是0你要小心printf不会因为零值自动省略它一定会老老实实打出来所以在题目要求保留前导零时占位符要写%d不需要特意写%03d这类。6. 顺序结构的边界什么时候该考虑分支和循环顺序结构是地基但不是全部。当你写了几个完整程序之后一定会遇到这种需求如果x大于0就输出正数否则输出负数或者要把一段操作重复十次。这就是分支和循环出场的时候了。我明确告诉你一个信号当你的程序需要根据某个条件来决定做还是不做或者做A还是做B顺序结构就撑不住了必须引入if或switch。同样当代码里连续出现好几行几乎相同的内容比如重复三遍打印同一句话循环结构能帮你大幅简化代码。这其实是很好的体验——先写累赘的顺序代码再学了循环后回头看你才能真正感受到循环结构的价值而不是为了学语法而学语法。6.1 从顺序到选择一个最自然的过渡int score; scanf(%d, score); if (score 60) printf(及格\n); else printf(不及格\n);这段代码执行流程就不再是一条道走到黑了。程序先读取score然后根据score的值做判断决定打印哪一行。这就是选择结构它会改变语句的执行顺序。你之所以能看懂这个例子恰恰是因为你已经理解了顺序结构——程序先执行scanf再执行if判断if里面再根据条件选择执行路径。6.2 从顺序到循环把重复交给机器int i; for (i 1; i 5; i i 1) { printf(%d\n, i); }for循环的语义我现在不展开但你可以先感受到它和顺序结构的区别顺序结构要求你写五遍printf循环结构只需要写一遍。循环是重复执行某个块而块内部可能仍然是顺序结构。所以顺序结构不是被循环替代了而是循环体内往往嵌套着顺序执行的多条语句两者是叠加关系。6.3 为什么先练顺序结构特别重要从教学角度看顺序结构是所有控制结构里最自然的一个它不需要任何跳跃、回溯只需要跟着代码一行一行走。但正是这份自然让你能专注在语法和调试的基础操作上。等顺序结构练熟了变量、赋值、输入输出这几个基础概念都扎实了再去理解if和for你只要把握住执行流程被改变了这一条主线学习曲线就会平滑很多。我在实际教学中发现顺序结构阶段多花时间的学生后面遇到指针、结构体等难点时反而比急着赶进度的人更稳。为什么因为编程的核心不是背语法而是把脑子里的思路翻译成计算机能执行的步骤。顺序结构就是这种翻译能力的最初训练场。语句数量少变量少流程一目了然错了也好查正适合反复打磨这种能力。最后聊两句我自己的习惯再分享一个小技巧我在跑顺序结构的练习时只要输出结果不对第一反应不是盯着代码发呆而是先人肉模拟一遍程序。找张纸把每个变量画成一个格子然后逐行执行更新格子里面的值看最终跟预期差在哪。这个看起来很笨的办法对我的帮助比任何调试器都大因为它在训练一种把自己当作CPU去执行代码的能力。等程序规模大了很多bug其实都是某个变量的值和你想象中不一样导致的而这种从第一天开始培养的敏感度会伴随你整个编程生涯。