
简介东北林业大学《C程序设计》期末考试试卷是一份面向高校计算机相关专业学生的考前复习资料内容紧密围绕C课程常见考核范围涉及基础语法、函数与递归、指针与动态内存、数组与字符串、结构体与联合体、类与对象、继承与多态、模板、STL容器与算法、异常处理以及文件I/O等核心知识点。整份资源仅1个PDF文件压缩包约160KB体积轻巧、便于打印或导入移动设备随时查阅。目前已有1002人学习下载适合正在准备期末考的学生用于自测与查缺补漏。虽然预览中未能展示试卷完整正文但作为一份结构完整的考试真题它能帮助读者快速定位高频考点、熟悉命题风格并检验对关键语法和面向对象思想的掌握程度建议配合教材例题与上机练习一同使用。1. 一份C程序设计期末试卷到底在考什么拿到《东北林业大学C程序设计期末考试试卷》多数人的第一反应是把它当刷题库存从头做到尾对个答案然后祈祷原题重现。我建议反过来先想清楚这套卷子考的是哪一层能力。C程序设计作为一门语言课期末考试几乎从不考“默写多态定义”它真正在考的是你和编译器之间的默契构造函数什么时候被调用、指针加一到底跳了几个字节、cout p打出来的是地址还是字符串。这些点散在选择、填空、程序阅读和编程题里表面是语法题底层是语言机制的相互作用。这份试卷的典型形态是概念题快速覆盖、程序阅读题占大头、编程题收紧到两到三个。阅读量大于书写量诊断能力大于背诵能力。对复习者来说读试卷的正确方式是先按题型拆出考点密度再决定时间投在哪里。2. 从试卷题型看C程序设计的考点脉络2.1 概念题与填空题语言机制是地基选择题和填空题的覆盖面广但深挖下去就三个方向类型系统、存储期、名字查找。类型系统考隐式转换和强制转换的优先级存储期考栈、堆、静态区名字查找考局部变量遮蔽全局变量、命名空间。这三个方向在编程题里不会单独出现却是判断程序输出题的基本功。概念题里容易被忽略的一个区块是语法细节比如int* p new int[10]; delete p;为什么是未定义行为i和i在操作符重载里的区别const修饰的是指针本身还是指针指向的对象。这类题的复习素材不是“面经”而是编译器的警告信息。用g -Wall -Wextra把往年题里的代码段重新编译一遍比背十遍笔记有用。2.2 看代码写运行结果考的是流I/O与类型转换程序阅读题通常给出一小段完整程序要求写出输出。这类题对初学者不友好的原因不在语法在于低估了隐式转换。看下面这题#include iostream using namespace std; int main() { int x 5; double y x / 2; cout y endl; y static_castdouble(x) / 2; cout y endl; return 0; }第一行输出是2不是2.5。x / 2执行的是整数除法结果2再转成double赋给y打印出来仍然是2。第二行用static_castdouble先把x变成double再做除法结果是2.5。我判卷时见过大量学生在这里丢分原因不是不理解类型而是只盯着cout的格式化能力忘了运算发生的类型环境。这类题的进阶版本会混进char和unsigned。比如char c 0xFF; cout (int)c endl;如果char是带符号类型结果是-1而不是255。复习时把整数提升、算术转换、窄化转换三条规则整理成一张表程序阅读题的正确率会有明显提升。2.3 编程题的评分标准不只看对错笔试的编程题手写代码题和OJ的判题方式完全不同。OJ只看输入输出笔试阅卷通常按评分点拆分常见评分结构如下评分维度常见占比阅卷关注点思路与算法30%算法步骤清楚不靠“碰巧能跑”语法规范30%分号、花括号、头文件、变量声明位置边界处理20%空输入、单元素、最大值、负数、越界访问代码整洁度10%命名可读、缩进统一、没有无意义的变量与题目要求一致10%函数签名、输入输出格式是否匹配这个表格透露出的信号是手写代码题的关键在于“让阅卷人快速看到你会写”而不是炫技。变量名用n、sum、tmp就能追溯到思路不要写a1b2c3这种自造缩写。边界条件可以用注释标出来哪怕这行注释对应的代码没有写全可以拿边界检查的分数。3. C程序设计考场上的指针与内存题3.1 指针与数组数组退化的本质指针是C程序设计考试里最稳定的失分点而数组退化array decay是其中最容易出题的知识点。数组名在表达式里会被隐式转换为指向首元素的指针这个转换发生在函数传参和数组下标运算两个场景里。#include iostream using namespace std; void arr_size(int arr[]) { cout in function: sizeof(arr) endl; } int main() { int a[8]; cout in main: sizeof(a) endl; arr_size(a); return 0; }在我的环境64位、int占4字节里main中输出32arr_size函数中输出8。原因是参数列表里的int arr[]等价于int* arrsizeof作用于它时得到的是指针大小而不是数组大小。答题时要注意数组作为函数参数时无法在函数内部用sizeof获取元素个数必须同时传入长度或者用std::array、std::vector。数组退化的另一个考点是二维数组的指针写法int (*p)[4] a;中p是“指向含4个int的数组的指针”对它做p1会跳过4个int。这和int* p a[0]所代表的语义完全不同。3.2 指针与const的三层语义const和指针的组合是选择题和改错题的常客它实际上只有三种排列列表如下写法语义阅读方式const int* p*p不可修改p可以指向别处先读intconst修饰它int* const pp不可修改*p可以修改先读pconst修饰它const int* const pp和*p都不可修改两者都受const约束记法是从右往左读p右边最近的词是const说明p本身是常量*p右边最近的词是const说明指向的值是常量。考试中常在这一节埋的坑是把const int* p当成“常量指针”把int* const p当成“指向常量的指针”概念记反之后整个程序阅读题全军覆没。3.3 二级指针函数里改指针需要让函数修改指针本身的值时要传指针的地址也就是二级指针。这是链表中插入节点、删除节点的核心操作期末试卷喜欢在改错题里考察。#include iostream #include cstring using namespace std; void alloc_buffer(char** pp, int size) { *pp new char[size]; (*pp)[0] A; } int main() { char* p nullptr; alloc_buffer(p, 64); cout p[0] endl; delete[] p; return 0; }函数alloc_buffer的形参是char** pp它收到的是p这个指针变量的地址。在函数内部*pp new char[size]修改的是调用者main里的p。如果改成void alloc_buffer(char* pp)函数内部只修改形参副本回到main后p仍然是nullptr。考试里如果看到“为什么链表插入失败”答案多半在这一行。3.4 内存释放与野指针的考场检查手写代码时new和delete配对出现是最容易检查的得分点。一个实用的自检习惯是写完一段使用了new的代码检查三条规则。第一条new[]必须配delete[]new配delete混用是未定义行为。第二条释放后的指针立即置为nullptr防止同一个delete被执行两次。第三条不要在析构函数里删除一个不属于本对象的指针除非你已经确认它来自new。考试时用这三条过一遍自己的代码能挡住大部分内存类丢分。提示delete一个空指针是合法操作所以释放后置空是安全的防御性写法。4. C程序设计中的类与对象拷贝控制与多态4.1 构造函数与初始化列表的顺序陷阱类部分的第一个高频考点是初始化列表的执行顺序。注意初始化顺序不由初始化列表的书写顺序决定而是由成员在类中声明的顺序决定。这道题在很多学校的期末卷里都出现过#include iostream using namespace std; class A { public: int a; int b; A(int v) : b(v), a(b) {} void show() { cout a b endl; } }; int main() { A obj(10); obj.show(); return 0; }成员声明顺序是a在前、b在后所以a(b)先执行但此时b还没有被初始化a拿到的是一个未定义值。最后输出大概率不是预期的10 10。用g -Wall编译这段代码会看到-Wreorder警告这正是考试想让你掌握的判断方法编译器的警告比你的记忆更可靠。4.2 深拷贝与浅拷贝字符串数组初始化引出的话题字符串在C程序设计的考题里绕不开char*与string的转换。如果考题让你手写一个字符串类多半要考深浅拷贝。#include iostream #include cstring using namespace std; class String { public: char* data; String(const char* s) { data new char[strlen(s) 1]; strcpy(data, s); } // 缺少拷贝构造时的行为data 指向同一块内存 ~String() { delete[] data; } }; int main() { String a(hello); String b a; // 浅拷贝b.data 与 a.data 相同 cout a.data endl; return 0; }String b a调用默认拷贝构造逐字节拷贝成员b.data和a.data指向同一块堆内存。程序结束时二者各自调用析构函数同一块内存被delete两次运行期报错。正确做法是提供拷贝构造函数String(const String other) { data new char[strlen(other.data) 1]; strcpy(data, other.data); }还要配套写赋值操作符重载否则a b仍然浅拷贝。现代C里更推荐的做法是直接禁用拷贝、改用移动语义但期末笔试通常只要求到深拷贝这一层你要能说清“为什么必须有拷贝构造”。4.3 虚函数与多态的运行期行为多态考点集中在三个地方虚函数表指针的隐式存在、析构函数要不要加virtual、虚调用与重载的区分。#include iostream using namespace std; class Base { public: virtual void f() { cout Base::f endl; } ~Base() {} }; class Derived : public Base { public: void f() { cout Derived::f endl; } }; int main() { Base* p new Derived(); p-f(); delete p; return 0; }输出是Derived::f因为f是虚函数调用按动态类型分派。这里有个容易忽略的细节如果析构函数不加virtualdelete p只调用Base::~Base()Derived的析构函数不执行。当派生类持有资源时这会导致资源泄漏。笔试改错题里“基类析构函数未声明为虚函数”是一个标准答案。4.4 一个完整的类设计题演练期末考试里的编程大题常以“设计一个类”的形式出现。以“编写一个学生类包含姓名、成绩支持排序”为例我一般建议按下面的模板写可以覆盖主要评分点。#include iostream #include cstring using namespace std; class Student { private: char* name; int score; public: Student(const char* n, int s); Student(const Student other); ~Student(); int get_score() const { return score; } void show() const { cout name score endl; } }; Student::Student(const char* n, int s) { name new char[strlen(n) 1]; strcpy(name, n); score s; } Student::Student(const Student other) { name new char[strlen(other.name) 1]; strcpy(name, other.name); score other.score; } Student::~Student() { delete[] name; }这套模板具备三个阅卷人想看到的要点成员私有化、拷贝构造避免浅拷贝、析构函数释放内存。在此基础上再写排序逻辑代码分数不会低。5. STL容器与算法在C程序设计试卷中的考法5.1 vector初始化与遍历的三种写法期末编程题允许使用STL时vector是最高频容器。三种常见初始化方式都必须会写#include vector #include algorithm #include iostream using namespace std; int main() { vectorint v1; // 空容器 vectorint v2(10, 0); // 10 个 0 vectorint v3 {1, 3, 2, 5, 4}; // 初始化列表 for (int i 0; i (int)v3.size(); i) { cout v3[i] ; } cout endl; for (int x : v3) { // 范围 for 只读 cout x ; } cout endl; for (int x : v3) { // 引用可修改 x * 2; } return 0; }第一段下标遍历要注意i v3.size()的类型匹配问题size()返回size_t是无符号类型如果容器的size()是0i v3.size()本身没问题但在其他场景下要小心int与size_t比较时可能出现的隐式转换。第二段和第三段是C11的标准写法阅卷时看到这类新式写法通常会认为你掌握了现代C而不是只背了老教材。5.2 sort与比较器排序是试卷里最常见的算法题。用sort时比较器的几种写法值得对比#include algorithm #include vector using namespace std; bool cmp_desc(int a, int b) { return a b; // 降序 } struct Cmp { bool operator()(int a, int b) const { return a % 10 b % 10; // 按个位升序 } }; int main() { vectorint v {31, 22, 13, 44}; sort(v.begin(), v.end()); // 默认升序 sort(v.begin(), v.end(), cmp_desc); // 函数指针 sort(v.begin(), v.end(), Cmp()); // 仿函数 sort(v.begin(), v.end(), [](int a, int b) { // lambda return a b; }); return 0; }sort要求比较器提供严格弱序a b和b a不能同时为真否则行为未定义。手写cmp_desc时如果写成return a b;会造成未定义行为这道题在考场上是隐蔽失分点。5.3 二分查找与冒泡排序两种考法二分查找通常以填空或编程形式出现。用STL直接做是lower_bound#include algorithm #include vector using namespace std; int main() { vectorint v {1, 2, 4, 4, 5}; auto it lower_bound(v.begin(), v.end(), 4); // it 指向第一个 4 的元素下标为 2 return 0; }如果题目不允许用STL要求手写二分最常见的边界错误是循环条件写成i j还是i j、mid更新时要不要加一。为避免死循环建议固定一套模板while (left right)配合left mid 1和right mid - 1。冒泡排序则是考查点在于“一轮冒泡后的数组状态”这类题的解法是手动模拟int a[5] {5, 3, 4, 1, 2}; for (int i 0; i 4; i) { for (int j 0; j 4 - i; j) { if (a[j] a[j1]) { swap(a[j], a[j1]); } } }内层循环每次比较相邻元素较大值向右移动。模拟一次i0后最大值5到达数组末尾这个中间状态就是填空和选择题的答案。5.4 字符串处理转换与判断质数的优化C程序设计课上字符串处理和数论题经常结伴出现。string转char*是基础操作#include cstring #include string using namespace std; int main() { string s 12345; const char* c s.c_str(); // 只读不能修改 char buf[16]; strcpy(buf, s.c_str()); // 拷贝到数组后可以修改 int n stoi(s); // 字符串 - 整数 return 0; }c_str()返回的指针在string对象被修改后可能失效所以要点是“用完之后再改”不要长期持有。判断质数的优化写法是常见小题候选代码是枚举到sqrt(n)而不是nbool is_prime(int n) { if (n 2) return false; for (int i 2; i * i n; i) { if (n % i 0) return false; } return true; }i * i在i接近sqrt(INT_MAX)时可能溢出严谨的写法是把条件改成i n / i。笔试时写后者更能体现边界意识。6. 用g和VS Code搭一套C程序设计自测环境6.1 本地环境的三个配置重点复习C程序设计最常见的障碍不是语法而是环境。局部装个VSCode配上编译器就够了。Windows下的配置重点是三件事安装MinGW-w64、添加环境变量PATH到g所在目录、安装C/C扩展。很多同学遇到的“error: microsoft visual c 14.0 is required”和这里的g没有关系那是一部分Python包安装器对MSVC构建工具的要求不要混淆。考试自测用g即可g --version g -stdc11 -Wall -Wextra -g main.cpp -o main.exe-stdc11指定标准-Wall -Wextra开启警告-g保留调试信息。复习阶段开着两个警告选项比任何静态检查工具都直接。6.2 用文件重定向模拟标准输入输出期末考试阅读题和编程题依赖大量手动输入输出验证我一般把测试数据保存成文件然后用重定向跑程序./main input.txt output.txt先看output.txt是否符合预期用diff快速对比两版结果diff output.txt answer.txt在Windows终端中用Get-Content input.txt | .\main.exe output.txt也能做到但需要确保输入文件是UTF-8或GBK编码否则读入的字节序列不是你预期的内容。这条流程的价值在于一次写好测试数据程序每改一版都能回归不用反复敲键盘。6.3 用断言锁定关键函数的输出程序阅读题中的单个函数适合用assert做快速验证。假设习题要求实现字符串转整数函数my_atoi就把测试写成下面的形式#include cassert using namespace std; int my_atoi(const char* s); int main() { assert(my_atoi(123) 123); assert(my_atoi(-45) -45); assert(my_atoi(0) 0); assert(my_atoi(2147483647) 2147483647); return 0; }如果其中一条失败程序会停在对应行并中断。调试完成后把断言测试文件另存等复习冲刺阶段再跑一遍。把std::cin切到文件输入这一个动作做顺剩下的事就是对照输出矩阵逐个勾掉。本文还有配套的精品资源点击获取