logo

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

作者:半吊子全栈工匠2025.10.11 20:05浏览量:250

简介:本文汇总C/C++开发中最常见的50道面试题,涵盖语言基础、内存管理、指针与引用、多线程等核心知识点,提供详细解答与代码示例,助开发者高效备考技术面试。

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

在C/C++技术面试中,考察内容通常围绕语言特性、内存管理、系统编程等核心能力展开。本文精选50道高频面试题,按知识点分类解析,帮助开发者系统梳理知识体系,提升面试通过率。

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

1. C与C++的主要区别

C是面向过程语言,无类、继承等特性;C++支持面向对象编程,引入类、继承、多态等特性。例如:

  1. // C语言结构体示例
  2. struct Point { int x; int y; };
  3. // C++类示例
  4. class Point {
  5. public:
  6. int x, y;
  7. void print() { cout << x << "," << y; }
  8. };

2. static关键字的作用

  • 全局静态变量:限定作用域为当前文件。
  • 局部静态变量:函数内保持持久化。
  • 类静态成员:所有对象共享,通过类名访问。
    1. class Example {
    2. public:
    3. static int count; // 类静态成员
    4. };
    5. int Example::count = 0; // 定义与初始化

3. const#define的区别

  • const是类型安全的常量,有内存分配;#define是预处理宏,无类型检查。
  • const支持调试信息,#define在编译前替换。

4. 指针与引用的区别

  • 指针是变量,可重新赋值;引用是别名,初始化后不可更改。
  • 指针可为NULL,引用必须绑定有效对象。
    1. int a = 10;
    2. int* p = &a; // 指针
    3. int& r = a; // 引用
    4. *p = 20; // 通过指针修改
    5. r = 30; // 通过引用修改

5. sizeofstrlen的区别

  • sizeof是运算符,返回对象或类型的字节大小(包括\0)。
  • strlen是函数,返回字符串长度(不包括\0)。
    1. char str[] = "abc";
    2. cout << sizeof(str); // 输出4('a','b','c','\0')
    3. cout << strlen(str); // 输出3

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

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

  • 原因:未释放动态内存、异常导致delete未执行。
  • 检测工具:Valgrind、AddressSanitizer。
    1. void leak() {
    2. int* p = new int[100]; // 未释放
    3. // 若此处抛出异常,内存泄漏
    4. }

7. newmalloc的区别

  • new是运算符,调用构造函数;malloc是函数,仅分配内存。
  • new失败抛出异常,malloc返回NULL
    1. class MyClass {
    2. public:
    3. MyClass() { cout << "Constructor"; }
    4. };
    5. MyClass* obj1 = new MyClass(); // 调用构造函数
    6. MyClass* obj2 = (MyClass*)malloc(sizeof(MyClass)); // 无构造函数调用

8. 智能指针的种类及适用场景

  • unique_ptr:独占所有权,适用于单一资源管理。
  • shared_ptr:共享所有权,通过引用计数管理生命周期。
  • weak_ptr:解决shared_ptr循环引用问题。
    1. #include <memory>
    2. std::unique_ptr<int> p1(new int(10));
    3. std::shared_ptr<int> p2 = std::make_shared<int>(20);

9. 深拷贝与浅拷贝的区别

  • 浅拷贝:仅复制指针,导致多个对象共享同一内存。
  • 深拷贝:复制指针指向的内容,确保对象独立。
    1. class String {
    2. public:
    3. char* data;
    4. String(const char* str) {
    5. data = new char[strlen(str)+1];
    6. strcpy(data, str);
    7. }
    8. // 深拷贝赋值运算符
    9. String& operator=(const String& other) {
    10. if (this != &other) {
    11. delete[] data;
    12. data = new char[strlen(other.data)+1];
    13. strcpy(data, other.data);
    14. }
    15. return *this;
    16. }
    17. };

10. 内存对齐的原则

  • 结构体成员按最大对齐数对齐(通常为成员大小的倍数)。
  • 可通过#pragma pack修改对齐方式。
    1. struct AlignExample {
    2. char a; // 1字节
    3. int b; // 4字节(对齐到4)
    4. double c; // 8字节(对齐到8)
    5. }; // 总大小:1 + 3(填充) + 4 + 8 = 16字节

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

11. 线程与进程的区别

  • 进程:资源分配的基本单位,拥有独立内存空间。
  • 线程:CPU调度的基本单位,共享进程内存空间。

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

  • 互斥锁:独占访问,任何时刻仅一个线程可持有。
  • 读写锁:分读锁(共享)和写锁(独占),适用于读多写少场景。
    ```cpp

    include

    std::mutex mtx;
    std::shared_timed_mutex rwmtx;

void read() {
rwmtx.lock_shared(); // 读锁
// 读取操作
rwmtx.unlock_shared();
}

void write() {
rwmtx.lock(); // 写锁
// 写入操作
rwmtx.unlock();
}

  1. ### 13. 条件变量的使用场景
  2. - 线程间同步,当某个条件不满足时阻塞线程。
  3. ```cpp
  4. std::mutex mtx;
  5. std::condition_variable cv;
  6. bool ready = false;
  7. void worker() {
  8. std::unique_lock<std::mutex> lock(mtx);
  9. cv.wait(lock, [] { return ready; }); // 等待条件满足
  10. // 执行任务
  11. }
  12. void notifier() {
  13. {
  14. std::lock_guard<std::mutex> lock(mtx);
  15. ready = true;
  16. }
  17. cv.notify_one(); // 唤醒一个等待线程
  18. }

14. 死锁的产生条件及避免方法

  • 条件:互斥、持有并等待、非抢占、循环等待。
  • 避免方法:按固定顺序获取锁、使用std::lock同时获取多个锁。
    1. std::lock(mtx1, mtx2); // 同时获取两个锁,避免死锁
    2. std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    3. std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);

15. 原子操作与CAS(Compare-And-Swap)

  • 原子操作:不可中断的操作,保证线程安全。
  • CAS:比较并交换,用于实现无锁数据结构。
    ```cpp

    include

    std::atomic counter(0);

void increment() {
int expected = counter.load();
int desired = expected + 1;
while (!counter.compare_exchange_weak(expected, desired)) {
desired = expected + 1;
}
}

  1. ## 四、系统编程与底层知识(10题)
  2. ### 16. 虚拟内存的作用
  3. - 每个进程拥有独立的地址空间,防止相互干扰。
  4. - 实现内存换入换出,提高物理内存利用率。
  5. ### 17. 静态链接与动态链接的区别
  6. - **静态链接**:将库代码编译到可执行文件中,体积大但启动快。
  7. - **动态链接**:运行时加载共享库,体积小但依赖外部文件。
  8. ### 18. 文件描述符与文件指针的区别
  9. - **文件描述符**:操作系统层面的整数标识,用于内核操作。
  10. - **文件指针**:C标准库中的`FILE*`,封装描述符并提供缓冲。
  11. ```cpp
  12. #include <unistd.h>
  13. #include <fcntl.h>
  14. #include <cstdio>
  15. int fd = open("file.txt", O_RDONLY); // 文件描述符
  16. FILE* fp = fdopen(fd, "r"); // 转换为文件指针

19. 信号处理机制

  • 通过signalsigaction注册信号处理函数。
  • 异步信号安全函数需谨慎使用(如printf不安全)。
    ```cpp

    include

    include

void handler(int sig) {
std::cout << “Received signal “ << sig << std::endl;
}

int main() {
signal(SIGINT, handler); // 注册Ctrl+C处理函数
while (true) {}
}

  1. ### 20. 进程间通信(IPC)方式
  2. - **管道**:匿名管道用于父子进程,命名管道用于任意进程。
  3. - **共享内存**:最快但需同步机制。
  4. - **消息队列**:结构化数据交换。
  5. ```cpp
  6. #include <sys/mman.h>
  7. int* shared_data = (int*)mmap(NULL, sizeof(int),
  8. PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

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

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

  • 虚函数:有默认实现,子类可重写。
  • 纯虚函数:无实现,强制子类重写(抽象类)。
    1. class Base {
    2. public:
    3. virtual void foo() { cout << "Base"; } // 虚函数
    4. virtual void bar() = 0; // 纯虚函数
    5. };

22. 模板元编程的应用

  • 编译时计算,生成高效代码。
  • 示例:计算阶乘的模板元编程实现。
    ```cpp
    template
    struct Factorial {
    static const int value = N * Factorial::value;
    };

template<>
struct Factorial<0> {
static const int value = 1;
};

cout << Factorial<5>::value; // 输出120

  1. ### 23. RAII(资源获取即初始化)原则
  2. - 通过构造函数获取资源,析构函数释放资源。
  3. - 示例:文件操作封装。
  4. ```cpp
  5. class File {
  6. FILE* fp;
  7. public:
  8. File(const char* name) : fp(fopen(name, "r")) {
  9. if (!fp) throw std::runtime_error("Open failed");
  10. }
  11. ~File() { if (fp) fclose(fp); }
  12. };

24. 单例模式的线程安全实现

  • 使用局部静态变量(C++11后线程安全)。
    1. class Singleton {
    2. public:
    3. static Singleton& getInstance() {
    4. static Singleton instance;
    5. return instance;
    6. }
    7. Singleton(const Singleton&) = delete;
    8. Singleton& operator=(const Singleton&) = delete;
    9. private:
    10. Singleton() {}
    11. };

25. 策略模式的应用

  • 定义算法族,封装变化部分。
    ```cpp
    class SortStrategy {
    public:
    virtual void sort(int* arr, int n) = 0;
    };

class QuickSort : public SortStrategy {
public:
void sort(int arr, int n) override { / 快速排序实现 */ }
};

class Context {
SortStrategy strategy;
public:
Context(SortStrategy
s) : strategy(s) {}
void executeSort(int* arr, int n) { strategy->sort(arr, n); }
};

  1. ## 六、性能优化与调试技巧(10题)
  2. ### 26. 内联函数的使用场景
  3. - 适合短小、频繁调用的函数,减少函数调用开销。
  4. - 避免递归或复杂循环的内联。
  5. ```cpp
  6. inline int max(int a, int b) { return a > b ? a : b; }

27. 编译器优化选项

  • -O1:基本优化,平衡编译时间与性能。
  • -O2:更激进优化,包括内联、循环展开等。
  • -O3:启用更耗时的优化(如自动向量化)。

28. 性能分析工具

  • gprof:函数级调用统计。
  • perf:Linux下硬件事件统计(如缓存命中率)。
  • VTune:Intel提供的深度性能分析工具。

29. 调试内存错误的工具

  • Valgrind:检测内存泄漏、非法访问。
  • AddressSanitizer:GCC/Clang内置的快速内存错误检测。

30. 日志系统的设计要点

  • 分级别记录(DEBUG、INFO、ERROR)。
  • 支持异步写入,避免阻塞主线程。
  • 示例:简单的日志宏。
    1. #define LOG(level, msg) \
    2. std::cout << "[" << level << "] " << __FILE__ << ":" << __LINE__ \
    3. << " " << msg << std::endl

七、进阶主题(10题)

31. C++11/14/17/20新特性概览

  • C++11:自动类型推导(auto)、移动语义、lambda表达式。
  • C++14:泛型lambda、返回类型推导增强。
  • C++17:结构化绑定、if constexpr、并行算法。
  • C++20:概念(Concepts)、协程、范围(Ranges)。

32. 移动语义与右值引用

  • 通过std::move避免不必要的拷贝。
    1. std::string str = "Hello";
    2. std::vector<std::string> vec;
    3. vec.push_back(std::move(str)); // 移动构造,str变为空

33. 异常安全保证

  • 基本保证:不泄漏资源,对象处于有效状态。
  • 强保证:操作成功或回滚到操作前状态。
  • 不抛出保证:析构函数、移动操作不应抛出异常。

34. 自定义内存分配器

  • 重载new/delete或实现Allocator模板类。
    ```cpp
    class PoolAllocator {
    public:
    void allocate(size_t size) { / 从内存池分配 / }
    void deallocate(void
    p, size_t) { / 释放到内存池 / }
    };

std::vector vec;

  1. ### 35. 元组(Tuple)的实现原理
  2. - 基于模板递归和`std::get`实现类型安全的元素访问。
  3. ```cpp
  4. #include <tuple>
  5. std::tuple<int, double, std::string> t(1, 3.14, "hello");
  6. std::cout << std::get<0>(t); // 输出1

八、总结与建议

本文覆盖的50道面试题涉及C/C++开发的多个核心领域。备考时建议:

  1. 理论结合实践:对每个知识点编写示例代码并运行验证。
  2. 深入原理:不仅记住“怎么做”,更要理解“为什么”。
  3. 关注新标准:C++每三年更新一次标准,掌握最新特性是加分项。
  4. 模拟面试:与同伴互相提问,提升临场应变能力。

通过系统复习与实战演练,开发者可显著提升通过C/C++技术面试的概率,为职业发展打下坚实基础。”

相关文章推荐

发表评论

活动