C++ 内存管理与编译原理 (面试复习2)

目录

[一、 内存布局 (Memory Layout)](#一、 内存布局 (Memory Layout))

[1. C++ 程序的内存分区](#1. C++ 程序的内存分区)

[2. 堆 (Heap) 和 栈 (Stack) 的区别](#2. 堆 (Heap) 和 栈 (Stack) 的区别)

[二、 内存分配 (Memory Allocation)](#二、 内存分配 (Memory Allocation))

[1. new / delete 和 malloc / free 的区别](#1. new / delete 和 malloc / free 的区别)

[2. delete 和 delete[] 的区别](#2. delete 和 delete[] 的区别)

[3. 什么是内存泄漏 (Memory Leak)?如何检测?](#3. 什么是内存泄漏 (Memory Leak)?如何检测?)

[4. 什么是内存对齐 (Memory Alignment)?为什么要对齐?](#4. 什么是内存对齐 (Memory Alignment)?为什么要对齐?)

[三、 编译链接 (Compilation & Linking)](#三、 编译链接 (Compilation & Linking))

[1. 从源码到可执行文件的四个步骤](#1. 从源码到可执行文件的四个步骤)

[2. 静态链接和动态链接的区别](#2. 静态链接和动态链接的区别)

后续建议


一、 内存布局 (Memory Layout)

C++ 程序在运行时的内存空间通常被划分为以下几个主要区域(从高地址到低地址通常为栈向堆方向):

1. C++ 程序的内存分区
  1. 栈区 (Stack)

    • 由编译器自动分配和释放。

    • 存放函数的参数值、局部变量、返回值地址等。

  2. 堆区 (Heap)

    • 由程序员手动分配(new/malloc)和释放(delete/free)。

    • 若程序员不释放,程序结束时可能由 OS 回收。

  3. 全局/静态区 (Global/Static)

    • .data:存放已初始化的全局变量和静态变量。

    • .bss:存放未初始化的全局变量和静态变量(程序启动前由内核清零)。

  4. 常量区 (Literal/Constant)

    • 通常对应 .rodata 段。

    • 存放常量字符串、const 修饰的全局变量。此区域只读,修改会导致非法访问(Segmentation Fault)。

  5. 代码区 (Code/Text)

    • 存放函数体的二进制机器指令。
2. 堆 (Heap) 和 栈 (Stack) 的区别

这是面试中最高频的考点之一,建议从以下四个维度对比:

特性 栈 (Stack) 堆 (Heap)
申请方式 系统自动分配 。例如声明 int a; 程序员手动申请 。例如 new int(10);
分配效率 极高。仅需移动栈指针(寄存器操作),且有专门的指令支持。 较低。C 库需遍历空闲链表寻找合适大小的内存块,可能涉及系统调用。
空间大小 较小。通常为几 MB(如 Linux 默认 8MB,Windows 默认 1MB)。 很大。受限于虚拟内存空间,32位系统理论可近 4GB(实际约 2-3GB)。
碎片问题 无碎片。严格遵循 LIFO(后进先出),内存是连续的。 容易产生碎片。频繁的分配释放会导致内存空间不连续。

二、 内存分配 (Memory Allocation)

1. new / deletemalloc / free 的区别
特性 new / delete malloc / free
本质 C++ 运算符 (Operator),可重载。 C 标准库函数
构造/析构 自动调用new 先分配内存再调用构造函数;delete 先调析构再释放内存。 不调用。仅负责分配和释放纯粹的内存字节。
类型安全 类型安全。返回具体类型指针,无需强转。 不安全 。返回 void*,需要强制类型转换。
大小计算 编译器自动计算类型大小。 需要手动传递字节数(如 sizeof(T))。
2. deletedelete[] 的区别
  • 区别

    • delete:用于释放单个对象。

    • delete[]:用于释放数组。它会根据数组前的"cookie"(记录数组大小的信息)知道要调用多少次析构函数。

  • 如果在数组上错用 delete (如 int* p = new int[10]; delete p;)

    • 对于内置类型 (int, char):通常不会出大问题,内存会被释放(因为 allocator 知道块的大小),但这是 Undefined Behavior (UB)。

    • 对于自定义类 (Class)只会调用第一个对象的析构函数 ,剩余 9 个对象的析构函数不会被调用。如果对象内部管理着资源(如打开的文件、动态申请的内存),将导致严重的资源泄漏

3. 什么是内存泄漏 (Memory Leak)?如何检测?
  • 定义:程序动态申请了堆内存,但使用完后没有释放,导致这部分内存既不能被程序再次使用,也不能被操作系统回收(直到进程结束)。

  • 检测手段

    • Linux : 使用 Valgrind (valgrind --leak-check=full ./app)。

    • Windows (VS) : 使用 CRT Debug Library (_CrtDumpMemoryLeaks()) 或 Visual Studio 自带的诊断工具。

    • 代码层面 : 使用智能指针 (std::unique_ptr, std::shared_ptr) 实现 RAII,从根源杜绝泄漏。

4. 什么是内存对齐 (Memory Alignment)?为什么要对齐?
  • 定义 :数据在内存中的起始地址必须是其类型大小(或特定数值)的整数倍。例如,4 字节的 int 通常存储在地址能被 4 整除的地方。

  • 原因

    1. 性能 (Performance):CPU 读取内存通常按块读取(如 4 或 8 字节)。如果数据未对齐(跨越了两个块),CPU 需要做两次内存访问并进行拼接,效率减半。

    2. 平台兼容性 (Portability):某些硬件架构(如某些 ARM 或 RISC 架构)如果访问未对齐的内存,会直接触发硬件异常(Bus Error / Crash)。


三、 编译链接 (Compilation & Linking)

1. 从源码到可执行文件的四个步骤
  1. 预处理 (Preprocessing) (g++ -E):

    • 处理 #include(展开头文件)、#define(宏替换)、#ifdef(条件编译)。

    • 删除注释。

    • 生成 .i 文件。

  2. 编译 (Compilation) (g++ -S):

    • 语法分析、词法分析、语义分析。

    • 代码优化。

    • 将 C++ 代码翻译成汇编代码

    • 生成 .s 文件。

  3. 汇编 (Assembly) (g++ -c):

    • 将汇编代码翻译成机器能识别的机器码 (Machine Code)

    • 生成 .o (Linux) 或 .obj (Windows) 目标文件。

  4. 链接 (Linking) (g++ -o):

    • 合并多个 .o 文件和库文件。

    • 符号解析:找到函数和变量的定义。

    • 地址重定位:确定所有符号的最终内存地址。

    • 生成可执行文件 (.exe / .out)。

2. 静态链接和动态链接的区别
特性 静态链接 (Static Linking) 动态链接 (Dynamic Linking)
文件类型 .a (Linux), .lib (Windows) .so (Linux), .dll (Windows)
链接时机 编译链接阶段,代码被复制进可执行文件。 程序运行阶段,代码仅被引用,不复制。
可执行文件大小 (包含所有依赖库代码)。 (仅包含引用信息)。
依赖性 。发布时不需带库文件,独立运行。 。运行环境必须有对应版本的动态库。
升级维护 。库更新需重新编译发布整个程序。 。只需替换动态库文件即可。

后续建议

这些是理论基础,为了加深印象,您可以尝试以下操作:

  • 写一个会导致内存泄漏的各种场景的小 Demo,然后用 Valgrind 跑一下,看它输出的报告是什么样的。

  • 查看汇编代码 :写一个简单的 main.cpp,使用 g++ -E main.cpp > main.ig++ -S main.cpp,亲自查看预处理和编译后的文件内容。

相关推荐
百***787513 分钟前
Step-Audio-2 轻量化接入全流程详解
android·java·gpt·php·llama
快乐肚皮19 分钟前
MySQL递归CTE
java·数据库·mysql·递归表达式
廋到被风吹走22 分钟前
【Spring】DispatcherServlet解析
java·后端·spring
王琦031822 分钟前
Python 函数详解
开发语言·python
胡伯来了28 分钟前
13. Python打包工具- setuptools
开发语言·python
廋到被风吹走31 分钟前
【Spring】PlatformTransactionManager详解
java·spring·wpf
小鸡吃米…36 分钟前
Python 中的多层继承
开发语言·python
deng-c-f1 小时前
Linux C/C++ 学习日记(53):原子操作(二):实现shared_ptr
开发语言·c++·学习
wanghowie1 小时前
01.07 Java基础篇|函数式编程与语言新特性总览
java·开发语言·面试