C/C++面试必知:50道高频题深度解析
2025.09.19 14:37浏览量:0简介:本文整理了C/C++面试中最常见的50道核心问题,涵盖基础语法、内存管理、指针与引用、面向对象、多线程等关键领域,提供详细解答思路与代码示例,帮助开发者系统梳理知识体系,提升面试成功率。
C/C++面试必知:50道高频题深度解析
在C/C++技术面试中,基础知识的扎实程度和问题解决能力是考察重点。本文从企业实际面试场景出发,系统梳理了C/C++最常见50道面试题,涵盖语法特性、内存管理、面向对象设计、多线程编程等核心模块,每道题均提供解题思路、代码示例及扩展思考,助力开发者高效备考。
一、基础语法与数据类型(10题)
1. static
关键字的作用
static
在C/C++中有三种核心用途:
- 局部变量:延长生命周期至程序结束,仅初始化一次(如计数器)。
void func() {
static int count = 0; // 仅初始化一次
count++;
printf("%d", count);
}
- 全局变量:限制作用域为当前文件(避免命名冲突)。
- 成员变量:所有对象共享同一份数据(如单例模式)。
面试官意图:考察对变量作用域和生命周期的理解。
2. const
与#define
的区别
- 编译阶段处理:
#define
是预处理宏,直接文本替换;const
变量有类型检查。 - 内存分配:
const
变量可能分配存储空间,宏不占用内存。 - 调试支持:
const
变量可调试,宏无法直接查看值。
建议:优先使用const
提升代码安全性。
3. 指针与引用的区别
特性 | 指针 | 引用 |
---|---|---|
初始化 | 可不初始化 | 必须初始化 |
可空性 | 可为NULL |
不可为空 |
重新绑定 | 可指向其他对象 | 绑定后不可更改 |
内存占用 | 占用存储空间 | 不占用额外空间 |
典型场景:函数传参时,引用更简洁且安全。
二、内存管理与动态分配(10题)
4. 内存泄漏的常见原因及检测
- 原因:
- 动态分配后未释放(如
malloc
未free
)。 - 异常导致释放路径未执行。
- 循环引用(智能指针需用
weak_ptr
)。
- 动态分配后未释放(如
- 检测工具:
- Valgrind(Linux)
- AddressSanitizer(GCC/Clang)
代码示例:
void leak() {
int* p = (int*)malloc(sizeof(int)); // 泄漏!
// 缺少 free(p);
}
5. new
与malloc
的区别
特性 | new |
malloc |
---|---|---|
类型检查 | 是(调用构造函数) | 否(仅分配内存) |
返回值 | 对象类型 | void* 需强制转换 |
失败处理 | 抛出异常 | 返回NULL |
内存大小 | 自动计算 | 需手动指定字节数 |
建议:C++中优先使用new
/delete
。
6. 智能指针的类型及适用场景
unique_ptr
:独占所有权,禁止拷贝。std::unique_ptr<int> p(new int(10));
shared_ptr
:共享所有权,引用计数。weak_ptr
:解决循环引用问题。
面试高频:如何用weak_ptr
避免循环引用?
三、面向对象编程(15题)
7. 虚函数与纯虚函数的区别
特性 | 虚函数 | 纯虚函数 |
---|---|---|
定义 | 可有实现 | 仅声明(= 0 ) |
抽象类 | 非必须 | 包含纯虚函数的类为抽象类 |
实例化 | 可直接实例化 | 不可实例化 |
代码示例:
class Base {
public:
virtual void foo() {} // 虚函数
virtual void bar() = 0; // 纯虚函数
};
8. 构造函数能否为虚函数?
不能。构造函数执行时,对象尚未完全构造,虚表(vtable)未初始化,调用虚函数会导致未定义行为。
9. 多态的实现原理
C++通过虚表(vtable)和虚表指针(vptr)实现多态:
- 编译器为含虚函数的类生成虚表,存储函数指针。
- 每个对象包含一个隐藏的vptr,指向类的虚表。
- 调用虚函数时,通过vptr查找虚表中的实际函数地址。
性能影响:虚函数调用比普通函数慢约10%-20%。
四、多线程与并发编程(10题)
10. 线程安全的条件
满足以下任意一条:
- 不可变对象(如
const
成员)。 - 线程本地存储(TLS)。
- 互斥锁保护共享数据。
- 原子操作(如
std::atomic
)。
反例:
int global = 0;
void thread_func() {
global++; // 非线程安全!
}
11. 死锁的四个必要条件
- 互斥条件:资源一次仅能由一个线程占用。
- 占有并等待:线程持有资源并等待其他资源。
- 非抢占条件:已分配资源不能被强制释放。
- 循环等待:存在线程循环等待链。
预防策略:按固定顺序获取锁,或使用std::lock
同时获取多个锁。
五、高级特性与扩展(5题)
12. Lambda表达式的捕获方式
捕获方式 | 说明 |
---|---|
[=] |
值捕获(拷贝) |
[&] |
引用捕获(注意生命周期) |
[this] |
捕获当前类实例 |
[x, &y] |
混合捕获(x值,y引用) |
示例:
int x = 10;
auto lambda = [=]() { return x + 1; }; // 值捕获
13. 右值引用与移动语义
- 右值引用:绑定到临时对象(如
int&&
)。 - 移动语义:通过
std::move
转移资源所有权,避免深拷贝。
性能优化案例:
std::vector<int> create_data() {
std::vector<int> v(1000000, 42);
return v; // 返回值优化(RVO)
}
auto data = create_data(); // 无拷贝
六、系统级与底层知识(5题)
14. 大小端存储的判断方法
bool is_little_endian() {
int x = 1;
return *(char*)&x == 1; // 小端机返回true
}
面试延伸:网络字节序(大端)与主机字节序的转换函数(htonl
/ntohl
)。
15. 内存对齐的原则
- 结构体大小是最大成员对齐倍数的整数倍。
- 可通过
#pragma pack(n)
修改对齐方式。
示例:
struct Example {
char c; // 1字节
int i; // 4字节(通常对齐到4)
}; // 实际大小为8字节(补3字节)
备考建议
- 分模块练习:按语法、内存、OOP、多线程等分类突破。
- 手写代码验证:对指针操作、虚函数等核心概念,用纸笔模拟执行流程。
- 结合项目经验:将面试题答案与实际开发场景关联(如用智能指针解决资源泄漏)。
- 关注底层原理:理解虚表、内存对齐等机制,而非死记硬背。
C/C++面试不仅考察语言特性,更重视对底层原理的掌握和工程化思维。通过系统梳理这50道高频题,开发者可建立完整的知识图谱,在面试中展现深度与广度。
发表评论
登录后可评论,请前往 登录 或 注册