logo

C/C++面试必知:50道高频题深度解析

作者:Nicky2025.09.19 14:37浏览量:0

简介:本文整理了C/C++面试中最常见的50道核心问题,涵盖基础语法、内存管理、指针与引用、面向对象、多线程等关键领域,提供详细解答思路与代码示例,帮助开发者系统梳理知识体系,提升面试成功率。

C/C++面试必知:50道高频题深度解析

在C/C++技术面试中,基础知识的扎实程度和问题解决能力是考察重点。本文从企业实际面试场景出发,系统梳理了C/C++最常见50道面试题,涵盖语法特性、内存管理、面向对象设计、多线程编程等核心模块,每道题均提供解题思路、代码示例及扩展思考,助力开发者高效备考。

一、基础语法与数据类型(10题)

1. static关键字的作用

static在C/C++中有三种核心用途:

  • 局部变量:延长生命周期至程序结束,仅初始化一次(如计数器)。
    1. void func() {
    2. static int count = 0; // 仅初始化一次
    3. count++;
    4. printf("%d", count);
    5. }
  • 全局变量:限制作用域为当前文件(避免命名冲突)。
  • 成员变量:所有对象共享同一份数据(如单例模式)。

面试官意图:考察对变量作用域和生命周期的理解。

2. const#define的区别

  • 编译阶段处理#define是预处理宏,直接文本替换;const变量有类型检查。
  • 内存分配const变量可能分配存储空间,宏不占用内存。
  • 调试支持const变量可调试,宏无法直接查看值。

建议:优先使用const提升代码安全性。

3. 指针与引用的区别

特性 指针 引用
初始化 可不初始化 必须初始化
可空性 可为NULL 不可为空
重新绑定 可指向其他对象 绑定后不可更改
内存占用 占用存储空间 不占用额外空间

典型场景:函数传参时,引用更简洁且安全。

二、内存管理与动态分配(10题)

4. 内存泄漏的常见原因及检测

  • 原因
    • 动态分配后未释放(如mallocfree)。
    • 异常导致释放路径未执行。
    • 循环引用(智能指针需用weak_ptr)。
  • 检测工具
    • Valgrind(Linux)
    • AddressSanitizer(GCC/Clang)

代码示例

  1. void leak() {
  2. int* p = (int*)malloc(sizeof(int)); // 泄漏!
  3. // 缺少 free(p);
  4. }

5. newmalloc的区别

特性 new malloc
类型检查 是(调用构造函数) 否(仅分配内存)
返回值 对象类型 void*需强制转换
失败处理 抛出异常 返回NULL
内存大小 自动计算 需手动指定字节数

建议:C++中优先使用new/delete

6. 智能指针的类型及适用场景

  • unique_ptr:独占所有权,禁止拷贝。
    1. std::unique_ptr<int> p(new int(10));
  • shared_ptr:共享所有权,引用计数。
  • weak_ptr:解决循环引用问题。

面试高频:如何用weak_ptr避免循环引用?

三、面向对象编程(15题)

7. 虚函数与纯虚函数的区别

特性 虚函数 纯虚函数
定义 可有实现 仅声明(= 0
抽象类 非必须 包含纯虚函数的类为抽象类
实例化 可直接实例化 不可实例化

代码示例

  1. class Base {
  2. public:
  3. virtual void foo() {} // 虚函数
  4. virtual void bar() = 0; // 纯虚函数
  5. };

8. 构造函数能否为虚函数?

不能。构造函数执行时,对象尚未完全构造,虚表(vtable)未初始化,调用虚函数会导致未定义行为。

9. 多态的实现原理

C++通过虚表(vtable)虚表指针(vptr)实现多态:

  1. 编译器为含虚函数的类生成虚表,存储函数指针。
  2. 每个对象包含一个隐藏的vptr,指向类的虚表。
  3. 调用虚函数时,通过vptr查找虚表中的实际函数地址。

性能影响:虚函数调用比普通函数慢约10%-20%。

四、多线程与并发编程(10题)

10. 线程安全的条件

满足以下任意一条:

  • 不可变对象(如const成员)。
  • 线程本地存储(TLS)。
  • 互斥锁保护共享数据。
  • 原子操作(如std::atomic)。

反例

  1. int global = 0;
  2. void thread_func() {
  3. global++; // 非线程安全!
  4. }

11. 死锁的四个必要条件

  1. 互斥条件:资源一次仅能由一个线程占用。
  2. 占有并等待:线程持有资源并等待其他资源。
  3. 非抢占条件:已分配资源不能被强制释放。
  4. 循环等待:存在线程循环等待链。

预防策略:按固定顺序获取锁,或使用std::lock同时获取多个锁。

五、高级特性与扩展(5题)

12. Lambda表达式的捕获方式

捕获方式 说明
[=] 值捕获(拷贝)
[&] 引用捕获(注意生命周期)
[this] 捕获当前类实例
[x, &y] 混合捕获(x值,y引用)

示例

  1. int x = 10;
  2. auto lambda = [=]() { return x + 1; }; // 值捕获

13. 右值引用与移动语义

  • 右值引用:绑定到临时对象(如int&&)。
  • 移动语义:通过std::move转移资源所有权,避免深拷贝。

性能优化案例

  1. std::vector<int> create_data() {
  2. std::vector<int> v(1000000, 42);
  3. return v; // 返回值优化(RVO)
  4. }
  5. auto data = create_data(); // 无拷贝

六、系统级与底层知识(5题)

14. 大小端存储的判断方法

  1. bool is_little_endian() {
  2. int x = 1;
  3. return *(char*)&x == 1; // 小端机返回true
  4. }

面试延伸网络字节序(大端)与主机字节序的转换函数(htonl/ntohl)。

15. 内存对齐的原则

  • 结构体大小是最大成员对齐倍数的整数倍。
  • 可通过#pragma pack(n)修改对齐方式。

示例

  1. struct Example {
  2. char c; // 1字节
  3. int i; // 4字节(通常对齐到4)
  4. }; // 实际大小为8字节(补3字节)

备考建议

  1. 分模块练习:按语法、内存、OOP、多线程等分类突破。
  2. 手写代码验证:对指针操作、虚函数等核心概念,用纸笔模拟执行流程。
  3. 结合项目经验:将面试题答案与实际开发场景关联(如用智能指针解决资源泄漏)。
  4. 关注底层原理:理解虚表、内存对齐等机制,而非死记硬背。

C/C++面试不仅考察语言特性,更重视对底层原理的掌握和工程化思维。通过系统梳理这50道高频题,开发者可建立完整的知识图谱,在面试中展现深度与广度。

相关文章推荐

发表评论