资讯详情 C语言基础大全:从语法到文件操作的七块知识主线
📅 2026/10/29 11:51:45
简介《C语言基础大全》PDF文档是一份面向编程初学者与在校学生的语法速查型学习资料聚焦C语言入门阶段的知识梳理与查漏补缺适合课前预习、课后复习以及笔试面试前的快速回顾。压缩包内共1个PDF文件大小约68KB篇幅集中、便于检索正文按基本语法、数据类型、运算符、控制结构、函数、指针与文件操作七个模块依次展开其中数据类型细分整型、字符型、单双精度浮点以及枚举、数组、结构体、共用体等构造类型运算符与控制结构逐项说明用法与常见写法。指针章节覆盖指针变量的定义初始化、取地址与取内容运算、指针同数组及函数的配合方式文件操作部分则梳理fopen与fclose、fscanf与fprintf、fgets与fputs以及fseek、ftell等常用接口的用途。目前已有901人学习下载可当作课堂笔记的补充材料或知识框架参照。1. C语言知识地图七块内容连起来才是一条线C语言这门课最容易让人误判的地方是它看起来只有七块内容基本语法、数据类型、运算符、控制结构、函数、指针、文件操作。很多人复习时按顺序从第一块背到最后一块背完发现指针还是不会用文件还是不敢碰。真正的主线其实是这样一条链语法规定动作怎么写类型规定数据占多少内存运算符和控制结构规定流程怎么走函数规定代码怎么分工指针规定内存怎么被访问文件规定数据怎么进出程序。前四块是地基后两块是把地基连成完整程序的水泥。这份 C 语言基础大全按的正是这条线适合两类人正在系统学第一遍、需要一张地图的人以及学过一遍、想快速查漏补缺的人。2. 语法骨架与数据类型从分号到大括号的最小闭环把「基本语法」和「数据类型」拆成两章背是很多人第一遍学 C 语言时踩的坑。这两块其实是一件事的两面语法决定你能写出什么形状的代码类型决定这些代码在内存里占多大、怎么解释。这一章先把语句、表达式、语句块这三个最小单位讲清楚再把 int、char、float、double 的字节情况和 enum、数组、struct、union 四种构造类型摊开对照。2.1 语句、表达式与语句块三个最小单位语句是 C 程序里最小的执行单位标记是结尾的分号。一个语句可以写在一行里也可以拆成多行写还可以把好几个语句挤在同一行——编译器只认分号不认换行。这也是新手最容易翻车的地方格式化工具和肉眼都觉得「换行了就是一句」编译器却还在等那个分号。int a 3, b 4; /* 一条声明语句用逗号一次声明两个变量 */ int sum a b; /* 表达式 a b 求出值再赋给 sum */ printf(%d\n, sum); /* 语句结尾必须有分号 */表达式和语句不是一回事。表达式是用来求出值的计算式a b、i、x y都是表达式它们本身有值语句则是以分号收尾的完整动作。区分这两者的意义在于if (x 5)里括号内是一个表达式它的值是 5恒为真编译器的警告往往就藏在这种地方。语句块用一对大括号把多条语句包起来形成复合语句同时开出一个新的作用域。块内声明的变量出了大括号就失效这一点在嵌套循环和 if 分支里尤其要注意很多人把循环体需要的变量声明在块内出块后再去引用编译直接报未声明。{ int tmp 10; /* tmp 只在这个块内有效 */ printf(%d\n, tmp); } /* 这里再写 printf(%d, tmp); 就编译不过了 */2.2 空格、注释与可读性编译器眼里什么是空的空格在 C 语言里的作用是分隔语法单位防止intx这种连写被当成一个标识符。除字符串和字符常量内部之外多余的空格对编译结果没有影响但对读代码的人影响极大。写代码时用空格把运算符和操作数隔开是最低成本的可读性投资。注释有两种//管到行尾/* */管到闭合符号。最容易忽略的是注释在预处理阶段不是被「删掉」而是被替换成一个空格。这个细节直接引出一个经典陷阱int/**/x;会被替换成int x;合法但如果你写a/**/b想表达a和b两个记号紧挨着替换后中间多了空格语义可能就变了。另一个硬性规则是多行注释不能嵌套。/* 外层 /* 内层 */ 后面 */里编译器看到第一个*/就认为注释结束剩下的后面 */会变成真正的代码报一堆莫名其妙的语法错误。调试时想「注释掉一大段」而那段里本来就有块注释最容易踩这个。提示临时屏蔽大段代码时优先用#if 0 ... #endif它不会和内部的块注释打架。2.3 基本数据类型与字节账int、char、float、doubleC 语言的基本类型里考试和面试最爱问的是「占几个字节」。原料给的答案是 int 通常 4 字节、char 1 字节、float 4 字节、double 8 字节这个「通常」两个字很关键C 标准只规定了最小范围和相对大小关系比如sizeof(char)恒为 1sizeof(short) sizeof(int) sizeof(long)具体字节数由编译器和平台决定。所以复习时不要死背数字要记住「用 sizeof 验证」这个动作。类型常见字节数表示范围常见平台典型用途与坑char1-128 ~ 127 或 0 ~ 255存字符是否有符号由实现决定int4约 ±21 亿最常用溢出是未定义行为float4约 67 位有效数字精度有限比较相等要小心double8约 1516 位有效数字默认浮点类型数学函数多用它void无无表示无类型不能声明普通变量#include stdio.h int main(void) { printf(char%zu int%zu float%zu double%zu\n, sizeof(char), sizeof(int), sizeof(float), sizeof(double)); return 0; }这段代码用sizeof打印各类型的字节数%zu是size_t类型的格式符写%d在某些平台上会因为类型宽度不匹配而输出错值。把它在你自己的机器和课程要求的平台上各跑一次比背十遍「int 占 4 个字节」有用得多。浮点类型还要注意一点float 大约只有 6 到 7 位有效十进制数字所以0.1 0.2 0.3这种判断在浮点世界里基本不成立。复习时记住正确写法是判断差值绝对值小于一个很小的阈值而不是直接用。注意sizeof是运算符不是函数sizeof(int)的括号在跟类型名时必须写跟变量时可以省略。2.4 构造类型四件套enum、数组、struct、union构造类型是 C 语言里真正开始「组织数据」的地方。enum 把一组相关的整型常量起名字数组把同类型元素排成一列struct 把不同类型成员打包成一个整体union 让多个成员共用同一段内存。前三个理解起来不难union 是这四个里最容易被跳过、也最容易在考试里出坑的一个。类型内存特点访问方式复习重点enum本质是整型常量集合直接用枚举名枚举值是整型可参与运算数组同类型元素连续存放下标或指针下标从 0 开始越界不报错struct成员各占各的空间可能有对齐填充.或-大小 ≥ 各成员之和union所有成员共用一段内存.或-大小 最大成员写一个会覆盖另一个#include stdio.h struct S { int i; char c; }; /* 成员各自占空间中间可能有填充 */ union U { int i; char c; }; /* 成员共用一段内存大小取最大成员 */ int main(void) { struct S s {1, A}; union U u; u.i 0x41424344; /* 先写整个 int */ printf(sizeof struct%zu, sizeof union%zu\n, sizeof(struct S), sizeof(union U)); printf(u.c %c\n, u.c); /* 再读 char取到的是其中一部分字节 */ return 0; }在常见的 4 字节 int、默认对齐的平台上sizeof(struct S)通常是 8int 占 4char 占 1后面补 3 个字节凑对齐而sizeof(union U)通常是 4因为两个成员共用一段空间大小取最大的那个。读u.c得到什么取决于机器是大端还是小端——这就是 union 常被拿来考字节序的原因。复习时把「struct 各占各的、union 共用一段」这句话和这张 sizeof 结果绑在一起记比单背定义牢靠。2.5 void 与类型转换那些不起眼但会扣分的地方void 表示「没有类型」它有三个用法修饰函数返回值表示不返回放在参数列表里表示不接收参数以及声明void *通用指针。前两个是规范写法第三个要注意void *可以接任何类型的指针但反过来把它赋给具体类型指针时需要显式转换而且不能直接对它做解引用。类型转换分隐式和显式。隐式转换会发生在赋值、函数传参、混合运算的时候规则是「窄的往宽的方向靠」显式转换就是强制类型转换。最容易出错的是浮点转整型(int)3.99的结果是 3直接截断而不是四舍五入以及有符号和无符号混算一个负数和一个无符号数比较负数会被解释成很大的正数条件判断直接反掉。复习这一节的标准很简单能说清void的三种用法能判断一个混合表达式里各操作数最终被提升成什么类型能解释(int)3.99为什么是 3。这三点在选择题里出现的频率相当高。3. 运算符与控制结构优先级、副作用与分支循环运算符和控制结构这一块知识点本身不难难在「细节量」。运算符有五大类优先级有十几级控制结构只有顺序、选择、循环三种但每一种都带着自己的坑自增自减的副作用、逻辑运算的短路、switch 的穿透。这一章按「先分类、再抠副作用、最后落到三种结构」的顺序梳理。3.1 五类运算符与优先级记忆法原料把运算符分成五类算术 - * / % --、赋值、关系 !、逻辑 || !、三目? :。分类本身好记真正要背的是优先级和结合方向因为考试里a b || c、*p、x y z这类表达式几乎必出。优先级运算符结合方向记忆点1()[]-.左到右括号和成员最高2!~---*sizeof右到左单目运算3*/%左到右乘除取余4-左到右加减5左到右移位6左到右大小关系7!左到右相等判断8^|左到右位运算三者优先级不同层9左到右逻辑与10||左到右逻辑或11?:右到左三目12及复合赋值右到左赋值13,左到右逗号最低表里第 8 行的位运算符实际分三层高于^高于|为了记忆方便才合并成一行。复习时不用逐级背数字抓住三句话就够括号和单目最高赋值和逗号最低逻辑运算低于关系运算。a 1 0之所以是坑就是因为比优先级高实际被解释成a (1 0)。3.2 自增自减与短路求值副作用的两个高发区和--分前置和后置。前置是先加再用后置是先用再加。这个规则本身不复杂复杂的是把它们塞进同一个表达式里。int i 0; int a i; /* 后置a 拿到 0之后 i 变成 1 */ int b i; /* 前置i 先变成 2b 拿到 2 */ /* 下面这种写法不要出现在任何作业和考试答卷里 i i; 同一表达式内多次修改同一变量求值顺序未定义 */最后一行注释是重点。C 标准没有规定一个表达式内部各个子表达式的求值顺序所以i i这种「同一变量在一个表达式里被修改多次」的写法属于未定义行为换编译器、换优化等级结果都可能不同。复习时记住一条铁律一个表达式里对同一个变量只修改一次。短路求值是逻辑运算符的另一个高发区。左边为假时右边不再计算||左边为真时右边不再计算。这个特性常被用来写「先判空再解引用」的安全判断但反过来如果右侧表达式带副作用比如自增、函数调用短路会让它根本不执行。int *p NULL; if (p ! NULL *p 0) { /* 左边为假右边不会被求值不会崩 */ printf(positive\n); }这段代码能安全运行靠的就是短路指针为空时*p 0压根不执行。复习时要把「短路是一种求值顺序保证」和「副作用可能不执行」这两面都记住只记一面容易在判断题上失分。3.3 顺序、选择、循环三种控制结构的骨架顺序结构就是按书写顺序执行没有额外语法考的是「你能否看懂代码的执行次序」。选择结构有if...else和switch两种循环结构有for、while、do...while三种。这五种语句的语法都不长重点是分清楚什么时候用哪种。if...else适合区间判断和复杂条件switch适合对一个整型表达式的离散取值做分支。for适合循环次数明确的场景把初始化、条件、更新三件事集中在一行while适合次数不确定、靠条件退出的场景do...while的特点是循环体至少执行一次因为条件判断放在末尾。三种循环最容易混的是「循环变量在什么时候更新」。for的更新表达式在每轮循环体执行完之后才运行while需要你自己在循环体里写更新语句漏写就是死循环。复习时拿同一个求和任务用三种循环各写一遍比看定义有用。3.4 switch 的穿透与 break一个标号引发的连锁反应switch的语法是switch (表达式)后面跟若干个case 常量:和一个可选的default:。两个硬性要求括号里的表达式必须是整型char 也算整型每个 case 后面必须是整型常量表达式不能是变量。真正的高发点是穿透。case 在 C 语言里只是跳转标号不是分支边界。匹配到某个 case 之后程序会一直往下执行直到遇到break或者 switch 结束。switch (grade) { case A: puts(优秀); /* 这里故意不写 break会继续执行下面的 case B */ case B: puts(良好); break; /* 到这里才跳出 */ default: puts(待提高); break; }这段代码里如果 grade 是A会连续输出「优秀」和「良好」两行。有时候这种穿透是故意的多个取值共享同一段处理逻辑但更多时候是忘了写 break 导致的 bug。判断标准很简单如果几个 case 之间共享逻辑就把共用的代码放到最后一个 case 里上面的 case 留空如果没有共享意图每个 case 都要有 break。default的位置也不是必须放最后。它只是「所有 case 都不匹配时」的入口放中间照样生效而且如果它后面没有 break也会往下穿透。复习时把「case 是标号不是边界」这句话记牢穿透问题基本不会再错。4. 函数、指针与文件操作C语言的两个硬骨头加一个出口函数、指针、文件操作这三块是整份基础大全里分量最重的部分。函数解决的是「代码怎么分工」指针解决的是「内存怎么被直接访问」文件操作解决的是「程序的数据从哪来、到哪去」。前两个是 C 语言公认的难点第三个是很多人学完之后一直不敢用的部分。这一章按「函数打底、指针攻坚、文件收口」的顺序梳理。4.1 函数定义、调用与返回值把代码切成可复用的块一个函数定义包含四部分函数类型返回值类型、函数名、形参列表、函数体。函数调用时通过函数名加实参列表触发实参的值会按顺序传给形参。返回值通过return语句交回给调用者return同时结束函数的执行。#include stdio.h int add(int x, int y) { /* 返回类型 int函数名 add两个形参 */ return x y; /* return 把结果交回调用处并结束函数 */ } int main(void) { int sum add(2, 3); /* 2 和 3 是实参按顺序传给 x 和 y */ printf(%d\n, sum); return 0; }几个必须分清的点形参是函数内部的局部变量函数返回后失效C 语言传参默认是值传递函数内改形参不会影响外面的实参想改外面的值必须传地址返回类型为void的函数不返回值但可以用return;提前结束。还有一个高频考点是函数声明与定义分离调用发生在定义之前时必须先有函数声明原型否则编译器无法检查参数类型。main函数的参数void写法也常被问到int main(void)表示不接收命令行参数int main(int argc, char *argv[])表示接收。两种写法都合法区别只在于是否需要处理命令行输入。4.2 指针的定义、初始化与取地址解引用先把概念钉死指针变量存的是地址。定义一个指向 int 的指针写int *p;取地址用解引用用*。这三个符号是整块内容的地基把它们的含义记准后面的指针与数组、指针与函数才不会乱。int a 10; int *p NULL; /* 先置空避免野指针 */ p a; /* p 保存 a 的地址 */ *p 20; /* 解引用后赋值a 变成 20 */ printf(%d %d\n, a, *p); /* 两个都输出 20 */这里的关键是分清「指针本身的值」和「指针指向的值」。p的值是一个地址*p才是那个地址上存放的 int。a取的是 a 的地址*p是顺着地址找回去。指针不初始化就使用它指向的地址是随机的读写都可能直接让程序崩掉这类问题在调试时表现为「莫名其妙的内存错误」很难定位。复习这一节必须能徒手画出「变量 a 在内存里一个格子、指针 p 在另一个格子里、p 的格子里写着 a 的地址」这张图。图画得出来指针的加减、指针做参数、指针和数组的关系都能顺下来画不出来后面全是背定义。注意指针声明里的*是类型的一部分int *p, q;里只有 p 是指针q 是普通 int。一行声明多个指针时每个都要带星号。4.3 指针与数组、指针与函数两组最常被拿来考的对应关系数组和指针的关系核心就一句话数组名在大多数表达式里会退化成指向首元素的指针。所以a[i]和*(a i)是等价的a[i]和a i也是等价的。这个等价关系能解释很多看起来奇怪的写法但也带来一个高频错误数组名不是指针变量不能被赋值a p这种写法编译不过。int a[5] {1, 2, 3, 4, 5}; int *p a; /* 等价于 p a[0] */ printf(%d\n, *(p 2)); /* 输出 3等价于 a[2] */ printf(%d\n, p[2]); /* 指针也能用下标同样输出 3 */指针与函数的关系考的是两个名字很像的东西函数指针和指针函数。区分方法是看名字先跟谁结合。int (*fp)(int, int);里(*fp)有括号fp 先跟*结合所以 fp 是函数指针指向一个返回 int、接收两个 int 的函数。int *f(int, int);里 f 先跟()结合所以 f 是函数返回类型是int *叫指针函数。int add(int x, int y) { return x y; } int (*fp)(int, int) add; /* 函数指针指向 add */ int r fp(2, 3); /* 通过函数指针调用等价于 add(2, 3) */ printf(%d\n, r);函数指针的实际用途是「把函数当参数传」比如写一个通用的排序函数把比较规则作为函数指针传进去。复习时先记住「有括号先跟星号是指针没括号先跟括号是函数」这条口诀再拿两个声明各写一遍、各调用一次基本就不会混。4.4 文件操作打开、读写、定位三件套文件操作是整份大全里最贴近实际使用的一块。流程固定为三步打开、读写、关闭。打开用fopen关闭用fclose读写有一组函数定位有一组函数。原料提到的函数可以按用途归类记忆。用途函数说明打开fopen返回FILE *失败返回 NULL关闭fclose释放文件句柄成功返回 0格式化读写fscanf/fprintf按格式串读写类似 scanf/printf按行或字符串读写fgets/fputs读一行或写字符串比 fscanf 安全定位fseek移动文件位置指针定位ftell返回当前位置偏移量打开模式是另一个必考项r只读文件不存在直接失败w只写文件存在则清空、不存在则新建a追加写在末尾带表示可读可写带b表示二进制模式。模式选错要么读不到内容要么把原文件清空这两种情况在实验课上都很常见。#include stdio.h int main(void) { FILE *fp fopen(data.txt, r); if (fp NULL) { /* 打开失败必须立刻判断 */ perror(fopen); /* 打印失败原因 */ return 1; } char buf[128]; while (fgets(buf, sizeof buf, fp) ! NULL) { /* 读到 NULL 表示结束 */ fputs(buf, stdout); } fclose(fp); /* 用完必须关闭 */ fp NULL; /* 关闭后置空防止误用 */ return 0; }这段代码里有两个必须养成的习惯fopen之后立刻判空fclose之后立刻把指针置空。不判空的话文件不存在时fp是 NULL后续所有读写都是对空指针操作程序直接崩。不置空的话关闭后误用这个指针属于悬空指针问题更隐蔽。fgets的第二个参数是缓冲区大小读取时会自动留一个位置给字符串结束符所以最多读sizeof buf - 1个字符。文件定位的典型用法是先用fseek(fp, 0, SEEK_END)移到末尾再用ftell(fp)拿到文件长度最后fseek(fp, 0, SEEK_SET)回到开头。这个组合常被用来计算文件大小考试里出现的频率不低。5. 易错点清单五个反复翻车的地方这一章单独立出来是因为下面这五个点几乎覆盖了 C 语言基础阶段绝大多数的「明明看懂了却写错」。每一条都按「易漏点是什么、为什么会漏、怎么补」三层写复习时对着自查一遍比重新翻一遍教材效率高。5.1 分号与空语句多一个分号就多一个空动作易漏点if (x 0);后面多写了一个分号或者for (i 0; i n; i);后面多了一个分号。为什么漏格式化之后分号在行尾几乎看不见而且加了分号编译器完全不报错只是逻辑悄悄错了。if (x 0);会被解释成「条件成立时执行一个空语句」后面的代码永远执行for后面加分号则会让循环体变成空语句循环白跑一遍。怎么补养成「if、for、while的右括号后面如果直接换行下一行必须是左大括号」的书写习惯中间不给自己留写分号的机会。编译时把警告等级开高很多编译器会对空语句给出提示。代码 review 时专门扫一遍所有右括号后面紧跟分号的位置。5.2 整数除法与取余/和%的两个隐含前提易漏点5 / 2的结果是 2 不是 2.5%只能用于整型写成5.0 % 2直接编译报错负数取余的结果符号和被除数一致。为什么漏数学课上除法就是除法到了 C 语言里「两个整数相除结果还是整数」这条规则被默认隐藏了教材往往只写一句「整数除法向零取整」做题时很容易忘。怎么补想要小数结果至少让一个操作数是浮点写5 / 2.0或(double)5 / 2。取余只用于整型浮点求余要用fmod函数。负数取余按「结果符号跟着被除数」记-7 % 3是 -1。复习时把这三条写成一行便签贴在显示器边上比反复翻书有效。5.3 sizeof 与数组退化函数里量不出数组长度易漏点在函数参数里对数组用sizeof求长度结果永远是 4 或 8指针大小不是数组真实长度。为什么漏数组名在传入函数时会退化成指针函数内部看到的int arr[]实质就是int *arrsizeof量的是指针而不是数组。这个错误在写「数组长度」工具函数时几乎人人踩过一次。怎么补数组长度必须在数组还没退化的地方算好通常是在定义数组的那个作用域里用sizeof(arr) / sizeof(arr[0])求出来再作为参数传给函数。函数参数里同时传数组和长度是 C 语言的标准写法不要指望函数内部自己量。记住sizeof是编译期运算它量的是类型的大小不是运行时的实际元素个数。5.4 struct 对齐与 union 共用内存大小不等于成员之和易漏点以为sizeof(struct)就是把所有成员大小加起来以为 union 可以同时保存所有成员的值。为什么漏教材讲 struct 时常常先讲语法后讲内存对齐规则被放在很后面union 的定义只有一句「共用内存」具体共用成什么样没展开。怎么补struct 的大小要把对齐填充算进去规则是每个成员的偏移量必须是该成员大小的整数倍结构体总大小必须是最大成员大小的整数倍。union 的大小等于最大成员的大小写一个成员会覆盖其他成员的内容读出来的值依赖字节序。复习时拿struct { int i; char c; }和union { int i; char c; }各跑一次 sizeof把两个结果记在笔记里比背规则直观。5.5 指针未初始化与文件返回值不检查两个「不报错但会崩」的习惯易漏点声明指针后直接解引用fopen之后不判断返回值就读写。为什么漏这两种错误在编译阶段都不报程序可能在小数据量下跑得好好的换一台机器、换一个输入就崩属于典型的「黑匣子」问题。很多人调试半天最后发现就是少了一行判空。怎么补指针声明时一律初始化为NULL使用前判空fopen之后立刻判空失败就打印原因并返回。这两个动作写起来只多两行但能省掉大量排查时间。养成习惯之后可以进一步在fclose之后把文件指针置空free之后把堆指针置空形成一套固定的收尾动作。6. 复习清单按模块自查把漏掉的补回来复习到这一步与其再从头翻一遍不如按模块做一次自查。下面这张表把七块内容的必会点和自测问法列在一起能答上来的打勾答不上来的回到对应章节补。自测问法这一列是关键它逼着你用「输出」的方式检验「输入」比单纯重读有效得多。模块必会点自测问法基本语法语句以分号结尾、表达式有值、语句块开作用域、注释不嵌套能说出int/**/x;为什么合法吗数据类型四种基本类型的字节与范围、构造类型四件套的区别能徒手写出 struct 和 union 的 sizeof 结果吗运算符五类运算符、优先级三句话、自增自减的副作用能解释a 1 0为什么不等于预期吗控制结构三种结构、switch 的穿透、do...while 至少执行一次能说出 case 后面不写 break 会发生什么吗函数定义四要素、值传递、声明与定义分离能在函数里改掉外面的变量吗怎么改指针取地址与解引用、指针与数组的等价、函数指针与指针函数能画出指针指向变量的内存图吗文件操作fopen 模式、读写函数、fseek/ftell 定位能写出「读文件并逐行输出」的完整代码吗自查的时候还有个技巧不要只判断「这个知识点我背过没有」要判断「这个知识点我能不能在一分钟内写出一段能跑的代码」。背过和写出来之间差着一次编译和一次调试而恰恰是编译和调试的过程会把那些模糊的地方暴露出来。#include stdio.h int main(void) { int arr[5] {1, 2, 3, 4, 5}; int len sizeof(arr) / sizeof(arr[0]); /* 长度必须在数组未退化时算 */ int sum 0; for (int i 0; i len; i) { /* 循环变量就地声明作用域限定在循环内 */ sum arr[i]; } printf(len%d sum%d\n, len, sum); /* 期望输出 len5 sum15 */ return 0; }上面这段代码建议作为最后一道自测题它同时用到了数组、sizeof、循环、函数返回值这几块内容。如果你能不看提示写出等价代码并且能解释len为什么不能在别的函数里算那基础部分基本就过关了。写不出来或者写出来结果不对回到第 2 章和第 5.3 节把数组退化和sizeof的关系再捋一遍。我带过的一个习惯是每次复习完一个模块合上资料用空白文件从零写一个能跑的小程序只允许查函数原型不允许抄代码。第一次这么做的时候我以为自己指针已经懂了结果在「把数组传进函数再改元素」这一步卡了二十分钟最后发现是把int *和int的参数类型写混了。从那以后我每次复习 C 语言都强制走一遍「合上资料手写一遍」的流程宁可写得慢也不留那种「看懂了但写不出」的侥幸。希望这份梳理能帮到你把基础这块地基打扎实。本文还有配套的精品资源点击获取