资讯动态

C++期末复习手记:考点地图、易错点与三轮复习法

发布时间:2026/10/14 1:08:52 来源:尧图企业网站定制
期末周的图书馆里基本上每个角落都坐着抱着C教材发愁的人。有人抄笔记抄到手指发酸有人对着往年卷子边翻书边叹气还有人盯着目录纠结“模板那几节到底考不考”。如果你也处在这个状态那这份关于《C语言程序设计》期末复习的手记应该能帮你省下不少瞎忙活的功夫。我自己前后把这门课完整复习了三轮挂过科、补过考最后也拿到了说得过去的分数这里面哪些地方容易踩坑、哪些知识点性价比最高、怎么安排时间最合理我都帮你踩过了一遍。这篇手记不是教材的浓缩版而是按“真实期末复习会遇到什么问题”来写的。适合基础一般、想系统突击的同学也适合平时代码能跑但一做卷子就手生的同学。我会尽量把考点背后的原理讲透再配上一些复习方法上的建议让你看完之后心里能有一张清晰的复习地图。1. 别急着翻书先搞清楚C期末到底在考什么1.1 考点地图一学期的内容其实就这六块很多人复习C的第一反应是从第一章看到最后一章最后发现看了前面忘了后面效率极低。我第一轮就这么干过结果浪费了整整两天。后来我找了几所高校的期末卷子逐题比对发现不管题型怎么变考点基本都落在这六块语法基础与指针、函数与递归、类与对象、继承与多态、STL、C11新特性。这六块的权重差别很大。以我见过的几份卷子来看类与对象、继承与多态加一起能占到40分左右指针和内存相关占20分上下STL和C11合起来大概15到20分剩下的就是函数、数组、字符串等基础内容。我建议你第一轮复习时先按这个权重分配精力别在冷门细节上死磕。什么叫冷门细节比如位运算的奇技淫巧、模板的偏特化这些要么不考要么只有一两道选择空花整个下午去研究完全不划算。真正决定你能不能过的是指针、类、多态和STL容器这几个部分值得反复咀嚼。1.2 “能跑出来”和“能拿到分”是两回事这是我在复习中最深的一个体会平时写代码习惯了一出问题就编译、运行、看报错但期末卷子上的程序阅读题是没有编译器给你兜底的。去年末我拿一套往年卷模拟考试其中有一道题把指针自增、引用传参、逗号表达式全揉在了同一个函数里。我先在纸上推演推出来的结果和上机运行结果差了整整两个数。后来把卷子拿给同学看那同学在纸上算出来的也是错的。这就说明一个问题如果你复习时只靠编译器验证结果其实是在逃避真正的薄弱点——你的脑子并没有学会像机器一样逐步执行代码。所以我在手记后面专门写了“人脑编译器”训练法核心就一句话复习期间做程序阅读题先把代码抄下来或截图关掉输出自己一步步写出结果再和真实运行结果对照。这个习惯一旦养成你在考场上遇到阅读题时会从容很多因为平时已经把犯错的机会都用完了。2. 指针与内存管理这门课最容易翻车的大章节2.1 四类指针错误选择题和改错题都爱考指针为什么难因为它加了一层间接性。你操作的是一个“存放地址的变量”而不是直接操作值本身。我总结了期末卷子上最常出现的四类指针错误基本每套卷子都会沾一两个。第一类是野指针。声明了一个指针变量但没有初始化它里面存的是一个随机地址访问它就像在大街上随机敲一家门。这在改错题里很常见写了int *p;就直接*p 5;程序崩了都不知道去哪找原因。正确操作是定义时要么初始化要么先置空比如int *p nullptr;。第二类是悬空指针。指针本来指向一块动态分配的内存但那个内存已经被delete释放了指针还留在原处。之后再去解引用访问的是已经归还给系统的内存行为完全不可预知。这也是为什么后续讲智能指针时大家会觉得它是救命稻草——它能在很大程度上避免这种问题。第三类是数组越界。C不会检查数组下标int arr[5]你去访问arr[5]编译器不报错运行时也不一定立刻崩但它已经踩到了别的内存区域。这类错误在程序阅读题里经常隐藏得很深尤其是循环边界条件写错的时候。第四类是指针运算误用。p到底跳过几个字节取决于p的类型。int*自增一次跳4个字节char*自增一次跳1个字节这在选择填空中属于送分题但由于大家平时太习惯用数组下标反而容易在这里被绊倒。2.2 new/delete的配对问题与内存泄漏动态内存管理是期末的常客而最基础的考点就是new和delete的配对。配对规则其实只有三条new配deletenew[]配delete[]谁new的谁负责delete。我记得有一道改错题代码里用new int[10]分配数组释放时却写成delete p;很多人第一眼根本看不出来问题因为它能编译也能运行只是行为未定义程序大概率会崩或留下隐患。内存泄漏这个点考试很少让你写代码演示更多是问概念程序不断new却不delete时间一长可用内存越来越少最终可能直接挂掉。其实大家写小作业时感受不深因为程序跑几十秒就退出了系统会回收内存。但你要理解在长期运行的服务程序里内存泄漏等于慢性死亡。复习这一块时我建议你把“RAII”这个概念理解一下哪怕书上只有一段话。RAII的全称是“资源获取即初始化”意思是把内存资源的生命周期绑定到对象的生命周期上对象构造时分配内存对象析构时自动释放。C11出现的智能指针就是RAII思想的产物后面我会专门讲。2.3 引用和指针的分工期末最爱出辨析题引用和指针的辨析题几乎是期末必出而且考得非常细。我把最核心的差异列成了一张表对比维度指针引用是否必须初始化不要求但建议初始化定义时必须初始化能否改绑其他对象可以随时改变指向不能绑定后就跟到“死”是否占用独立空间本身是一个变量存地址可以理解为被引用对象的别名能否为空可以为空或野不能规范上不允许空引用传参语义传递的是指针值可模拟传引用直接操作原对象更直观为什么期末喜欢考这个因为很多人写代码时只求“能跑”不太留意两者本质区别。比如void swap(int *a, int *b)和void swap(int a, int b)都能实现交换但阅读题会问如果调用时传的是普通变量哪个版本能真正修改外部变量答案是引用版本。如果你没想明白指针版本在函数内部交换的只是两个地址副本这题就整道白送分了。还有一点容易忽略const引用可以绑定临时对象。比如const int r 5;是合法的因为编译器会给字面量5生成一个临时变量引用再绑定它但非const引用不能这样。这个点在“函数参数能不能传字面量”的选择题里经常出现。3. 类与对象把构造析构和多态串成一条线3.1 构造与析构的执行顺序一道经典必考题类与对象这一章第一个绕不开的考点就是构造和析构的顺序。很多人背结论都背混了构造时先基类后派生类先成员后自身析构时顺序完全相反。举一个我印象深刻的例子。假设有这样一个场景一个派生类Derived继承自Base同时有一个成员对象成员Member m_。构造Derived d;时实际执行顺序是先调用Base的构造函数再调用Member的构造函数最后执行Derived自己的构造函数体。析构时则反过来先执行Derived自己的析构函数体再析构成员对象m_最后析构Base。为什么是这个顺序因为派生类需要先准备好基类的部分才能在此基础上添加自己的成员。而析构时要先处理最“外层”的派生部分再逐层向基类收尾。这个顺序真的会被反复考选择题、填空、阅读题都能出。复习时不要只背顺序最好自己写一个小程序在每层构造函数里打印一句话运行一遍然后亲手验证输出顺序。我记得我第一次自己写这个验证程序时发现顺序和书上写的一模一样但那一瞬间我才真正理解为什么而不是靠死记硬背。3.2 深浅拷贝默认拷贝构造函数到底帮你干了什么类里只要有指针成员深浅拷贝就是一个躲不掉的坎。期末卷子上考这个知识点的方式通常很直白给定一个类里面有一个char* name或int* data问默认拷贝构造有什么问题。很多人对“默认拷贝构造是逐成员拷贝”这句话理解不够深。逐成员拷贝对于普通变量没问题比如一个int age拷贝就是把值复制一份彼此独立。但指针成员就麻烦了默认拷贝只是把指针的值复制一份于是两个对象的指针指向同一块堆内存。这会造成两种典型后果——修改一个对象的数据另一个对象跟着变析构时两个对象都去delete同一块内存第二次释放就是未定义行为程序很可能直接崩溃。解决办法是深拷贝自己写拷贝构造函数为指针成员新分配一块内存然后把原对象那块内存里的内容复制过来。同样的道理也适用于拷贝赋值运算符。复习到这里我建议你把著名的“三法则”一起记住如果类中出现了需要手动管理的动态内存资源那么拷贝构造、拷贝赋值、析构这三个成员函数通常都得自己实现或者干脆用智能指针替代裸指针让编译器生成的默认版本变得足够好。3.3 虚函数、多态和“必须写虚析构”的理由多态是C的招牌考点也是区分度很高的部分。期末卷子里最常见的形式是基类定义了一个虚函数virtual void speak()派生类重写它然后用基类指针指向派生类对象调用speak()问你输出什么。理解这个问题的关键是“动态绑定”。所谓动态绑定就是程序在运行时根据对象的真实类型来决定调用哪个函数而不是根据指针的静态类型。基类指针的静态类型是Base*但它指向的对象其实是一个Derived所以调用虚函数时会走派生类的版本输出的也是派生类里的实现。如果没有virtual那就是静态绑定基类指针调用基类版本即使它实际指向派生类对象也调不到派生类的函数。真正让我栽过跟头的是虚析构。如果基类析构函数不是虚函数那么delete 基类指针时编译器只会调用基类的析构函数不会调用派生类的析构函数。结果是派生类里动态分配的资源没有被释放内存泄漏就藏在这种看似无害的代码里。所以一个经验法则只要基类里有虚函数析构函数就该声明为virtual。这个知识点几乎是期末必考而且经常以“指出下面代码的问题”的改错题出现。4. STL不是选修内容复习性价比极高4.1 容器选型先搞懂每种容器的性格很多教材把STL放在最后一章老师上课也是一带而过导致相当一部分同学以为它期末不考。但我翻了近几年的卷子STL出现的频率明显上涨选择题、填空题、编程题都可能涉及。你可以不背全部源代码级的实现细节但每种容器适合干什么必须门儿清。vector是连续内存的动态数组尾部插入快随机访问快但中间插入删除慢。list是双向链表中间插入删除快随机访问慢。deque是双端都能快速插入的队列。map通常基于红黑树存储的是键值对按键有序查找复杂度是 O(log n)set是有序集合元素不重复。unordered_map用哈希表实现查找平均 O(1)但元素无序。考试常考的就是“如果需要有序且要快速按键查找选哪个”“频繁在头部插入选哪个”这类选择题。我当时复习时把这几个容器的特性总结成了一张表考前扫一遍效果很好。编程题里如果允许使用STL我强烈建议优先用vector和map因为它们最通用、也最少出错。4.2 迭代器失效程序阅读题里的高频雷区迭代器失效是STL里最阴险的考点。它不问你容器的基本接口而是给你一段循环里删除元素的代码问程序会怎样或者要求你补全正确的写法。典型的错误写法是这样for (auto it v.begin(); it ! v.end(); it) { if (*it % 2 0) { v.erase(it); } }问题在erase(it)之后it这个迭代器已经失效了it变成了对失效迭代器的操作程序行为未定义。正确的写法是for (auto it v.begin(); it ! v.end();) { if (*it % 2 0) { it v.erase(it); } else { it; } }关键在于erase返回被删除元素之后的有效迭代器所以直接把返回结果赋值给it就能安全继续。这个技巧不仅期末考试会考工作以后写代码也经常要处理类似问题。复习到这一步建议你亲手在电脑上分别跑一遍错误版本和正确版本亲眼看看错误版本到底会出现什么诡异结果记忆会深刻得多。4.3 算法库和lambda表达式编程题的救命稻草如果你的编程题还停留在手写for循环找最大值、手写冒泡排序的阶段那就太亏了。algorithm头文件里的东西能让你在考场上几分钟内解决一些平时要写半天的功能。我复习时最常用的组合是sort加自定义比较。当时有一类编程题是“按学生成绩排序同分按学号排序”用sort只需要写三五行。如果你已经学了C11那lambda表达式更是让代码短到不能再短。一个典型的写法长这样sort(stu.begin(), stu.end(), [](const Student a, const Student b) { if (a.score ! b.score) return a.score b.score; return a.id b.id; });lambda的语法是[捕获列表](参数列表) - 返回值类型 { 函数体 }其中返回值类型在很多情况下可以省略。考试填空经常考捕获列表的含义[]表示按值捕获所有局部变量[]表示按引用捕获所有局部变量[]表示什么都不捕获。千万别只背格式要能读懂一段lambda里捕获的变量到底是副本还是原对象这决定了对变量修改会不会影响外面。5. C11新特性很多教材讲得不深但卷子在考5.1 智能指针从裸指针到自动管理如果你的教材比较老可能只讲了auto_ptr但现在很多学校的期末卷子已经开始出现unique_ptr、shared_ptr的概念题。这部分复习起来不亏因为考得很浅理解几个核心点就够了。unique_ptr是独占所有权同一时间只能有一个智能指针拥有这块内存拷它是非法的想转移所有权只能用std::move。shared_ptr是共享所有权内部维护一个引用计数最后一个引用销毁时内存才被释放。weak_ptr是配合shared_ptr用的“不增加引用计数”的观察者主要用来解决循环引用问题。什么叫循环引用举个简单的例子两个类互相持有对方的shared_ptrA里有B的指针B里有A的指针它们的引用计数永远不可能降到零内存就泄漏了。如果把其中一边换成weak_ptr打破环问题就解决。期末考这个点时一般会问“循环引用如何破除”或“shared_ptr与unique_ptr的区别”属于死记硬背也能拿分的内容。5.2 移动语义与右值引用绕但考选择填空就够了移动语义这块内容理解起来有点抽象但期末考得并不深。你需要抓住的核心是移动操作的本质是资源转移而不是复制。以前返回一个大的vector可能要复制整个数据效率很低有了移动构造之后可以把内部指针直接“挪”过来新对象接管这块数据旧对象变成空壳。std::move并不是“移动了什么东西”它只是一个转换函数把左值强制转换成右值引用让你能调用移动构造函数或移动赋值运算符。我把这个点反复跟自己强调了好几遍不然很容易产生“std::move会自己移动数据”的错觉。考试选择题最常见的问法是下面哪段代码调用了移动构造哪段代码调用了拷贝构造还有一个常考点是“左值引用和右值引用分别用什么声明”和的区别。搞清楚这两个移动语义的选择题基本拿下了。5.3 auto、nullptr、范围for小题里的高频细节这三样是C11里最“亲民”的特性复习成本低、提分快期末小题里特别喜欢出现。auto的类型推导规则有个容易被忽略的细节auto默认会忽略顶层 const但会保留引用。比如const int x 5; auto a x;得到的a是int不是const int而auto r x;中r则是const int。填空题如果考这个很多人失分就是因为平时写代码根本不在乎这些细节。nullptr和NULL的区别也常考。NULL本质上是整数0的宏在某些重载场合会产生歧义nullptr是真正的空指针类型类型是std::nullptr_t。建议写新代码时一律用nullptr。范围for循环更是在编程题里高频出现。要注意的是如果你想在循环里修改容器元素必须写成引用形式for (auto x : vec) { x * 2; }如果写成for (auto x : vec)你拿到的只是每个元素的拷贝改它根本不会影响原容器。这种“看不见的错误”在程序阅读题里特别容易出出题人只要在循环体里加一行x 1;再问你最终容器内容是什么就够不少同学当场发懵了。6. 我的三轮复习时间线不同基础怎么安排6.1 三轮复习法从扫盲直接干到压题我自己的复习安排分了三轮每轮目标完全不一样。第一轮是扫盲大概花5到7天。目标是过一遍教材和课堂PPT把前面说的六大考点逐一理解不留死角。这一轮不要刷题不要纠结细节重点是把每条知识点的“是什么”和“为什么”弄明白。比如虚函数为什么需要动态绑定、深拷贝为什么必须自己写这些都想通了后面刷题会特别顺。第二轮是刷题大概花3到4天。目标是往年试卷和课后习题里的程序阅读题、改错题。这一轮的核心方法是“人脑编译器”每道阅读题都先自己在纸上推演再对照运行结果。做错的题要整理到一个错题本上把错误原因写出来是指针自增算错、引用传参看漏了还是对析构顺序没记清。第三轮是冲刺考前2到3天。目标已经不再追求全面而是集中攻克两件事编程题的常用模板以及需要死记硬背的知识点。编程题模板指的就是“输入输出、排序、查找、统计”这几个固定套路考前每类练一两道保持手感。背诵清单我放在下一节。这个时间线适合“考前两周开始复习”的情况。但如果你平时基础比较薄弱连构造函数和析构函数都分不清那第一轮至少得给自己留出10天别硬压缩。6.2 程序阅读题用“人脑编译器”练法程序阅读题是我考前最焦虑的部分后来找到一个对我很管用的练习方法这里详细说说。准备一个小本子或一个文本文件把要练的阅读题代码完整抄一遍但把输出结果全部遮住。然后学编译器的方式逐步跟踪声明了哪些变量初值是多少进入循环时条件成立不成立每轮循环结束之前哪些变量的值被改了函数调用时参数是值传递还是引用传递。要在纸上写出每个步骤的状态就像你在调试器里看变量一样。写完自己推断的结果后再把代码放进真实环境里运行。如果结果一致说明这个考点过关如果不一致一定要把出错的那一步找出来弄清楚是你对语法理解错了还是执行顺序想错了。我印象很深的一次是把i和i的执行时机搞混了在纸上推演了三次都没发现直到运行后看到输出才恍然大悟。这个方法听起来笨但真的有用。考场上没有调试器你唯一能依靠的就是在大脑里模拟程序执行的能力。练得越多你模拟得越精确阅读题的正确率也就越稳定。6.3 考前需要死记硬背的清单和答题技巧最后一两天有些东西还是得靠背。我给清单分了几个层次。必背概念层纯虚函数和抽象类的关系抽象类不能实例化静态成员属于类而不是某个对象const成员函数里不能修改成员变量除非成员用mutable修饰运算符重载中前置自增和后置自增的区别——前置返回引用后置返回旧值且参数列表带一个占位int。细节陷阱层sizeof一个空类通常返回1数组名作为函数参数时退化成指针全局变量和局部变量同名时函数内部优先访问局部变量逗号表达式从左到右依次执行整个表达式的值是最后一个子表达式的值逻辑表达式里左右两边的求值顺序有短路规则左边为假右边就不会执行了。答题技巧层做程序阅读题时先定位主函数从主函数开始画变量表不要从函数定义开始看遇到函数调用时重点关注参数是按值传入还是按引用传入这比函数内部逻辑更重要遇到循环先把循环边界条件圈出来再逐轮跟踪变量的变化。把这三层过一遍加上前面说的考点梳理基本上该拿的分都能拿到。我自己是先把清单打印下来考前两天每天背一遍进考场前再看一眼心里特别踏实。最后讲一个从同学那里学来的小技巧我自己后来也一直在用考前把教材目录当成提纲对着每一个小节标题问自己能不能在五分钟内讲清楚“这个知识点是什么、解决什么问题、常见坑在哪里”。能讲清楚就是对复习最好的验证讲不清楚就回翻教材。C这门课复习到最后比的不是谁背得熟而是谁的机制理解更通透。祝顺利。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑