ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

C++实验100道习题答案:指针、内存与类对象高频考点解析

C++实验100道习题答案:指针、内存与类对象高频考点解析

简介:这份资源是西北工业大学C++实验课程的100道习题答案合集,面向正在学习C++程序设计的高校学生与自学者,帮助解决实验练习中思路卡壳、答案难以验证的问题。压缩包内共98个文件,全部为cpp源代码文件,整体约32KB,每份代码对应一道习题的完整实现,便于直接编译运行与对照学习。内容覆盖基础语法、函数与递归、类与对象、封装继承多态、数组与动态内存、string字符串处理、iostream输入输出流、结构体与联合体、指针操作、模板泛型以及异常处理等核心知识点,基本对应课程实验的主要章节。每道题的解答都包含清晰的逻辑分析与代码实现,读者可借此理解解题思路而非单纯抄写答案,逐步建立扎实的语法基础与面向对象的编程思维。目前已有901人学习,适合作为实验课复习、期末备考与课后自测的参考材料。

1. 从一道“指针与数组”题说起:这场 C++ 实验考什么、怎么拿分

如果你正在搜“C++实验100道习题答案”,大概率不是想从零学 C++,而是想确认一件事:这套题到底考哪些点,答案长什么样,我写的能不能过。先给一个反直觉的判断——这 100 道题里真正拉开差距的,不是你会不会写for循环,而是指针、内存、类与对象这三块。很多同学前 60 题做得飞快,后 40 题一碰指针和拷贝构造就翻车,最后分数卡在及格线附近。

这场实验考的是“基础语法 + 内存模型 + 面向对象”的组合能力,拿分逻辑很直接:基础题不丢分,指针题拿一半,类与对象题拿稳,总分就不会难看。适合正在做 C++ 实验、准备期末上机、或者要应付类似题型的人。下面按考点分布、高频题、易错点、答题模板四层拆开讲,你照着练就行。

2. 考点分布:100 道题里哪几类反复出现

2.1 基础语法与流程控制:前 30 题的主战场

这部分题量最大,但套路最固定。常见题型包括:判断闰年、求最大公约数、打印图形、数组排序、字符串反转。看起来简单,但实验题往往在边界条件上设坑,比如输入 0 或负数时怎么处理、数组越界怎么防。

以“求最大公约数”为例,很多同学只写辗转相除法,但题目如果要求“同时输出最小公倍数”,就需要先处理乘积溢出的问题。这类题通常这么答:先写核心算法,再补边界判断,最后加一句输出格式说明。

#include <iostream> using namespace std; // 辗转相除法求最大公约数 int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } int main() { int x, y; cin >> x >> y; // 处理负数输入,保证结果为正 if (x < 0) x = -x; if (y < 0) y = -y; if (x == 0 || y == 0) { cout << "输入不能为0" << endl; return 0; } int g = gcd(x, y); // 用 long long 防止乘积溢出 long long l = (long long)x / g * y; cout << g << " " << l << endl; return 0; }

这段代码的关键在long long那一步。实验题里经常出现两个 int 相乘溢出的情况,面试官或者批改老师一看你用了long long,就知道你考虑过数据范围。参数说明:gcd函数只处理正整数,负数在main里先取绝对值;最小公倍数用x / g * y而不是x * y / g,避免中间结果溢出。

2.2 指针与内存:第 31 到 60 题的分水岭

指针题是这套实验里最容易拉开差距的部分。高频题型有:指针交换两个数、指针遍历数组、动态内存分配、指针与字符串。很多同学在这里翻车,不是因为不会写,而是因为没搞清楚“指针指向哪里”和“内存谁释放”。

比如“用指针交换两个数”这道题,常见错误是只交换指针本身,而不是交换指针指向的值。正确做法是传指针进去,解引用后交换。

#include <iostream> using namespace std; // 交换两个整数的值,参数是指针 void swapByPointer(int* a, int* b) { int temp = *a; // 保存 a 指向的值 *a = *b; // 把 b 指向的值赋给 a 指向的位置 *b = temp; // 把保存的值赋给 b 指向的位置 } int main() { int x = 3, y = 5; swapByPointer(&x, &y); cout << x << " " << y << endl; // 输出 5 3 return 0; }

注意swapByPointer的参数是int*,调用时传&x和&y。如果你写成void swap(int* a, int* b)然后里面写int* temp = a; a = b; b = temp;,那只交换了形参指针本身,外面的 x 和 y 根本没变。这类题面试官最想听的是“你清楚指针本身和指针指向的值是两回事”。

动态内存分配题也常考。比如“用 new 创建一个数组并排序”,答题模板是:new 分配、使用、delete 释放,三步缺一不可。漏掉 delete 在实验里可能只扣一两分,但在面试里就是内存泄漏的硬伤。

2.3 类与对象:第 61 到 100 题的核心得分区

这部分题量不大,但分值高。高频考点:构造函数与析构函数、拷贝构造函数、运算符重载、继承与多态。实验题通常会给一个场景,比如“设计一个学生类,包含姓名、学号、成绩,并实现排序”,然后要求你写完整代码。

以“拷贝构造函数”为例,很多同学不理解为什么需要自己写。看下面这段:

#include <iostream> #include <cstring> using namespace std; class Student { private: char* name; int score; public: // 普通构造函数 Student(const char* n, int s) { name = new char[strlen(n) + 1]; strcpy(name, n); score = s; } // 拷贝构造函数,深拷贝 Student(const Student& other) { name = new char[strlen(other.name) + 1]; strcpy(name, other.name); score = other.score; } // 析构函数 ~Student() { delete[] name; } void print() { cout << name << " " << score << endl; } }; int main() { Student a("A同学", 90); Student b = a; // 调用拷贝构造函数 b.print(); return 0; }

如果不写拷贝构造函数,默认的是浅拷贝,两个对象里的name指向同一块内存,析构时就会 double free,程序直接崩。这类题通常这么答:先写普通构造,再写拷贝构造,最后写析构,顺序不能乱。面试官最想听的是“深拷贝和浅拷贝的区别,以及为什么有指针成员时必须自己写拷贝构造”。

运算符重载也常考,尤其是+和<<。答题模板:先写类内声明,再写类外定义,注意返回类型和参数类型。比如重载+返回新对象,重载<<返回ostream&。

3. 高频真题演练:五道题带你摸清答题套路

3.1 指针与数组:用指针遍历二维数组

题目:给定一个 3x3 的二维数组,用指针遍历并输出所有元素。

答题思路:二维数组名是指向第一行的指针,行指针加 1 跳过一整行。用int (*p)[3] = arr;来遍历。

#include <iostream> using namespace std; int main() { int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}}; int (*p)[3] = arr; // p 指向一个有3个int的数组 for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { // *(*(p+i)+j) 等价于 arr[i][j] cout << *(*(p + i) + j) << " "; } cout << endl; } return 0; }

关键在int (*p)[3]这个声明。它表示 p 是一个指针,指向“包含 3 个 int 的数组”。如果写成int* p[3],那就是“包含 3 个 int 指针的数组”,意思完全反了。这类题面试官最想听的是“你能分清数组指针和指针数组”。

3.2 字符串处理:统计单词个数

题目:输入一行字符串,统计其中单词的个数(单词以空格分隔)。

答题思路:用状态标记,遇到非空格且前一个状态是空格时计数加一。

#include <iostream> #include <string> using namespace std; int main() { string s; getline(cin, s); int count = 0; bool inWord = false; // 标记当前是否在单词内部 for (char c : s) { if (c != ' ' && !inWord) { count++; inWord = true; } else if (c == ' ') { inWord = false; } } cout << count << endl; return 0; }

注意getline和cin >>的区别:cin >>遇到空格就停,getline读整行。实验题里如果要求“输入一行”,基本都用getline。参数说明:inWord初始为 false,遇到第一个非空格字符时计数并置 true,遇到空格置 false。

3.3 类与对象:实现一个简单的栈类

题目:设计一个 Stack 类,支持 push、pop、top 操作,用数组实现。

答题思路:私有成员包括数组、栈顶指针;公有成员包括构造函数、push、pop、top、isEmpty。

#include <iostream> using namespace std; class Stack { private: int data[100]; int topIndex; public: Stack() : topIndex(-1) {} // 初始化栈顶为-1 bool isEmpty() { return topIndex == -1; } void push(int val) { if (topIndex >= 99) { cout << "栈满" << endl; return; } data[++topIndex] = val; } void pop() { if (isEmpty()) { cout << "栈空" << endl; return; } topIndex--; } int top() { if (isEmpty()) { cout << "栈空" << endl; return -1; } return data[topIndex]; } }; int main() { Stack s; s.push(1); s.push(2); cout << s.top() << endl; // 输出 2 s.pop(); cout << s.top() << endl; // 输出 1 return 0; }

这道题考的是封装。topIndex是私有成员,外部不能直接改,只能通过 push 和 pop 操作。面试官最想听的是“你理解类的封装性和接口设计”。

3.4 继承与多态:虚函数的使用

题目:定义一个基类 Shape,包含虚函数 area(),派生类 Circle 和 Rectangle 分别实现 area()。

答题思路:基类 area 声明为virtual,派生类用override重写,主函数用基类指针调用。

#include <iostream> using namespace std; class Shape { public: virtual double area() { return 0; } virtual ~Shape() {} // 虚析构,防止内存泄漏 }; class Circle : public Shape { private: double r; public: Circle(double r) : r(r) {} double area() override { return 3.14159 * r * r; } }; class Rectangle : public Shape { private: double w, h; public: Rectangle(double w, double h) : w(w), h(h) {} double area() override { return w * h; } }; int main() { Shape* s1 = new Circle(2); Shape* s2 = new Rectangle(3, 4); cout << s1->area() << endl; // 输出圆面积 cout << s2->area() << endl; // 输出矩形面积 delete s1; delete s2; return 0; }

关键在virtual和虚析构。如果基类析构不是虚的,delete s1时只会调用基类析构,派生类部分不会释放。这类题面试官最想听的是“多态的实现原理和虚析构的必要性”。

3.5 文件操作:读写文本文件

题目:从文件读取整数,排序后写回另一个文件。

答题思路:用ifstream读,ofstream写,注意文件打开失败的处理。

#include <iostream> #include <fstream> #include <vector> #include <algorithm> using namespace std; int main() { ifstream inFile("input.txt"); if (!inFile) { cout << "无法打开输入文件" << endl; return 1; } vector<int> nums; int x; while (inFile >> x) { nums.push_back(x); } inFile.close(); sort(nums.begin(), nums.end()); ofstream outFile("output.txt"); if (!outFile) { cout << "无法打开输出文件" << endl; return 1; } for (int n : nums) { outFile << n << " "; } outFile.close(); return 0; }

注意文件打开后要检查是否成功,用完要 close。实验题里经常忽略这两步,导致程序在文件不存在时直接崩溃。

4. 易错点:这五类题最容易丢分

4.1 指针未初始化或越界

错在哪:声明指针后直接解引用,比如int* p; *p = 10;。为什么错:p 指向随机地址,写入会导致程序崩溃。怎么答对:指针声明时要么初始化为nullptr,要么指向有效内存,使用前检查是否为空。

4.2 数组越界访问

错在哪:循环条件写成i <= n而不是i < n。为什么错:C++ 不检查数组越界,越界读写可能改掉其他变量的值。怎么答对:循环条件用<,或者用vector的at()方法。

4.3 浅拷贝导致 double free

错在哪:类里有指针成员,没写拷贝构造函数。为什么错:默认拷贝构造是浅拷贝,两个对象析构时释放同一块内存。怎么答对:有指针成员就写深拷贝,或者用智能指针。

4.4 虚函数忘记加 virtual

错在哪:基类 area() 没加 virtual,派生类重写后通过基类指针调用,结果调的是基类版本。为什么错:没有 virtual 就没有多态。怎么答对:基类函数加 virtual,派生类加 override。

4.5 文件操作忘记关闭

错在哪:打开文件后直接 return,没 close。为什么错:文件句柄泄漏,数据可能没写进磁盘。怎么答对:用 RAII 思想,或者手动 close,或者用fstream的析构自动关闭。

5. 答题技巧:从 100 道题里提炼一套通用模板

5.1 先判断题型,再套模板

拿到一道题,先看它属于哪类:基础语法、指针、类与对象、文件操作。基础题直接写,指针题先画内存图,类与对象题先写类框架,文件题先写打开和关闭。这套流程走下来,至少不会漏掉关键步骤。

5.2 代码写完后强制检查三件事

第一,边界条件:输入为 0、负数、空字符串时程序会不会崩。第二,内存管理:new 了有没有 delete,指针有没有初始化。第三,输出格式:题目要求换行还是空格,有没有多输出或少输出。

5.3 面试口述题的答题结构

如果面试官让你口述思路,按“定义变量 → 核心逻辑 → 边界处理 → 返回结果”四步说。比如“用指针交换两个数”:先定义临时变量,再解引用交换,最后检查指针是否为空。面试官最想听的是你的思考过程,不是最终答案。

5.4 时间分配建议

100 道题如果按考试时间算,基础题控制在 30 分钟内,指针题 40 分钟,类与对象题 50 分钟,留 20 分钟检查。平时练习时按这个节奏走,考试才不会慌。

5.5 一个血泪教训

我以前做指针题时总觉得自己懂了,结果一上机就翻车。后来强制自己每道指针题都画内存图,标出每个指针指向哪里、值是多少,正确率才上来。从那以后我每次遇到指针和内存相关的题,都强制走一遍“画图 → 写代码 → 检查边界”的流程。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进