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

作者:da吃一鲸8862025.10.15 14:34浏览量:1

简介:本文汇总了C/C++开发领域最常见的50道面试题,涵盖语言特性、内存管理、指针与引用、多线程、设计模式等核心知识点,提供详细解答与代码示例,帮助开发者系统梳理知识体系,提升面试成功率。

C/C++ 最常见50道面试题深度解析

在C/C++开发岗位的面试中,技术问题往往围绕语言核心特性、内存管理、并发编程、系统设计等维度展开。本文精选50道高频面试题,从基础到进阶,结合代码示例与理论分析,帮助开发者高效备考。

一、语言基础与语法(10题)

1. static关键字的作用

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

  • 局部变量:延长生命周期至程序结束,仅初始化一次
    1. void counter() {
    2. static int count = 0; // 只初始化一次
    3. count++;
    4. printf("%d\n", count);
    5. }
  • 全局变量:限制作用域为当前文件(内部链接)
  • 成员变量/函数:属于类而非对象,所有实例共享

2. const#define的区别

  • 编译阶段处理#define是预处理指令,直接文本替换;const是类型安全的常量
  • 内存分配const变量可能分配存储空间,#define不占内存
  • 调试支持const变量可在调试器中查看,宏无法直接调试

3. 指针与引用的区别

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

4. 内存对齐规则

结构体内存对齐遵循:

  1. 成员按声明顺序排列
  2. 每个成员对齐到自身大小的整数倍
  3. 结构体整体对齐到最大成员大小的整数倍
  1. struct Example {
  2. char a; // 1字节
  3. int b; // 4字节(对齐到4)
  4. double c; // 8字节(对齐到8)
  5. }; // 总大小:1(padding3) + 4 + 8 = 16字节

5. volatile的作用

强制编译器不优化对变量的访问,常用于:

  • 硬件寄存器访问
  • 多线程共享变量
  • 中断服务程序中的变量

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

6. mallocnew的区别

特性 malloc new
调用方式 函数 运算符
初始化 不初始化内存 可调用构造函数
失败处理 返回NULL 抛出std::bad_alloc
大小计算 需显式指定字节数 自动计算对象大小

7. 内存泄漏检测方法

  • 工具检测:Valgrind、Dr. Memory
  • 代码审查:检查所有new/malloc是否有对应delete/free
  • 智能指针:优先使用std::unique_ptr/std::shared_ptr

8. 野指针产生原因与避免

产生原因

  • 指针未初始化
  • 释放后继续使用
  • 返回局部变量地址

避免方法

  • 初始化指针为nullptr
  • 释放后立即置空
  • 使用智能指针

9. 浅拷贝与深拷贝

  • 浅拷贝:仅复制指针值,不复制指向的内容(导致双重释放)
  • 深拷贝:复制指针指向的完整数据
  1. // 浅拷贝示例(错误)
  2. typedef struct {
  3. char* data;
  4. } String;
  5. String shallow_copy(String src) {
  6. String dest;
  7. dest.data = src.data; // 危险!共享同一内存
  8. return dest;
  9. }

10. 定位内存泄漏的步骤

  1. 使用Valgrind等工具运行程序
  2. 分析泄漏堆栈跟踪
  3. 检查对应代码段的资源释放逻辑
  4. 添加日志确认释放点是否执行

三、多线程与并发编程(12题)

11. 互斥锁与读写锁的区别

特性 互斥锁 读写锁
读写权限 独占访问 读共享,写独占
适用场景 高竞争写操作 读多写少场景
性能 高竞争时性能下降 读操作并发提升性能

12. 死锁产生的必要条件

  1. 互斥条件:资源一次只能由一个线程占用
  2. 占有并等待:持有资源同时等待其他资源
  3. 非抢占条件:已分配资源不能强制剥夺
  4. 循环等待:存在等待环路

13. 条件变量使用模式

  1. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  2. pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  3. bool ready = false;
  4. // 等待线程
  5. pthread_mutex_lock(&mutex);
  6. while (!ready) {
  7. pthread_cond_wait(&cond, &mutex);
  8. }
  9. // 临界区操作
  10. pthread_mutex_unlock(&mutex);
  11. // 通知线程
  12. pthread_mutex_lock(&mutex);
  13. ready = true;
  14. pthread_cond_signal(&cond);
  15. pthread_mutex_unlock(&mutex);

14. 原子操作与CAS

  • 原子操作:不可分割的操作,如std::atomic
  • CAS(Compare-And-Swap):无锁编程基础操作
    1. bool compare_and_swap(int* ptr, int oldval, int newval) {
    2. if (*ptr == oldval) {
    3. *ptr = newval;
    4. return true;
    5. }
    6. return false;
    7. }

15. 线程局部存储(TLS)

使用__thread(GCC)或thread_local(C++11)定义线程私有变量:

  1. __thread int thread_local_var = 0;
  2. void* thread_func(void* arg) {
  3. thread_local_var = 42; // 每个线程独立副本
  4. return NULL;
  5. }

四、高级特性与设计模式(10题)

16. RAII设计模式

通过对象生命周期管理资源,典型应用:

  1. class FileHandler {
  2. FILE* fp;
  3. public:
  4. FileHandler(const char* filename) : fp(fopen(filename, "r")) {
  5. if (!fp) throw std::runtime_error("Open failed");
  6. }
  7. ~FileHandler() {
  8. if (fp) fclose(fp);
  9. }
  10. // 禁用拷贝
  11. FileHandler(const FileHandler&) = delete;
  12. FileHandler& operator=(const FileHandler&) = delete;
  13. };

17. 虚函数与多态实现

  • 虚表机制:编译器为类生成虚函数表(vtable)
  • 动态绑定:通过对象指针调用虚函数时,根据实际类型查找vtable
  1. class Base {
  2. public:
  3. virtual void foo() { printf("Base\n"); }
  4. };
  5. class Derived : public Base {
  6. public:
  7. void foo() override { printf("Derived\n"); }
  8. };
  9. Base* obj = new Derived();
  10. obj->foo(); // 输出"Derived"

18. 智能指针类型比较

类型 所有权语义 适用场景
unique_ptr 独占所有权 明确单一所有者
shared_ptr 共享所有权 多个所有者需要共存
weak_ptr 非拥有观察者 打破循环引用

19. 模板元编程应用

编译时计算斐波那契数列:

  1. template<int N>
  2. struct Fibonacci {
  3. static const int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
  4. };
  5. template<>
  6. struct Fibonacci<0> { static const int value = 0; };
  7. template<>
  8. struct Fibonacci<1> { static const int value = 1; };
  9. int main() {
  10. std::cout << Fibonacci<10>::value; // 输出55
  11. }

20. C++11新增特性

  • auto:类型推导
  • lambda表达式:匿名函数
  • 移动语义std::move
  • 范围for循环:简化容器遍历

五、系统级编程与底层知识(10题)

21. 大小端判断方法

  1. bool is_little_endian() {
  2. int num = 1;
  3. return *(char*)&num == 1;
  4. }

22. 函数调用约定

约定 参数传递 栈清理 命名修饰
__cdecl 右到左 调用方 _func
__stdcall 右到左 被调方 _func@n
__fastcall 寄存器+栈 被调方 @func@n

23. 内存映射文件实现

  1. #include <sys/mman.h>
  2. int fd = open("file.bin", O_RDWR);
  3. void* addr = mmap(NULL, file_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  4. // 使用addr访问文件内容
  5. munmap(addr, file_size);
  6. close(fd);

24. 信号处理机制

  1. #include <signal.h>
  2. void handler(int sig) {
  3. write(STDOUT_FILENO, "Signal caught\n", 15);
  4. }
  5. int main() {
  6. signal(SIGINT, handler);
  7. while (1) pause();
  8. }

25. 进程间通信方式

方式 特点 典型应用
管道 单向字节流 命令行管道
共享内存 最高效 大型数据交换
消息队列 结构化数据 进程间消息传递
套接字 网络支持 分布式系统

六、面试策略与建议

  1. 系统复习:按模块整理知识图谱
  2. 代码实践:每日编写3-5个代码片段
  3. 模拟面试:与同伴进行问答练习
  4. 项目复盘:准备3个核心项目的技术细节
  5. 工具准备:熟悉GDB、Valgrind等调试工具

掌握这50道面试题不仅能帮助通过技术面试,更能深化对C/C++语言本质的理解。建议结合《Effective C++》、《C++ Primer》等经典书籍系统学习,同时通过LeetCode等平台练习算法题,全面提升编程能力。