三、内存管理、指针与堆栈(共16项)
1.C/C++中的new和malloc有什么区别?
2.C++中new、delete、malloc、free关系
3.C++中介绍一下delete与 delete\[\]区别
4.介绍一下newoperator和operatornew的区别
5.main函数执行之前会执行什么? 执行之后还能执行代码吗?
6.堆和栈的区别有什么?
7.程序的局部变量、全局变量、及动态申请的数据分别存储在哪里?
8.编程实现memcopy
9.C/C++中struct的内存对齐与内存占用计算?
10.C/C++中智能指针的定义与作用?
11.C/C++中内存泄漏以及解决方法?
12.空指针和悬空指针的区别?
13.什么是智能指针,它的作用有哪些,智能指针有什么缺点?
14.应用程序在运行时的内存包括代码区和数据区,其中数据区又包括哪些部分?
15.介绍一下c++内存泄漏?
16.C++智能指针在模型服务中如何选择?
结论先行
这16个问题覆盖了 C++ 内存生命周期三阶段:分配(new/malloc/operator new)→ 使用(栈/堆/数据区)→ 回收(delete/free/智能指针) 。
核心主线是:理解堆栈差异、掌握手动分配规则、用智能指针替代裸指针、避免内存泄漏与悬空指针 🔐
逐条简明解释
1. new 和 malloc 的区别
| 维度 | new / delete |
malloc / free |
|---|---|---|
| 语言 | C++ 操作符 | C 库函数 |
| 分配类型 | 按对象/数组分配,自动计算大小 | 按字节分配,需手动指定 sizeof |
| 初始化 | 会调用构造函数 | 只分配内存,不构造 |
| 释放配套 | delete / delete[] |
free |
| 失败行为 | 抛出 std::bad_alloc |
返回 NULL |
| 可重载 | 可重载 operator new |
不可重载 |
2. new、delete、malloc、free 的关系
new=operator new(分配内存) + 调用构造函数delete= 调用析构函数 +operator delete(释放内存)malloc/free只是纯内存分配/释放,不处理对象生命周期 ⚠️
不能混用 :
new出来的用delete,malloc出来的用free。
3. delete 与 delete[] 的区别
-
delete:释放单个对象,调用一次析构函数。 -
delete[]:释放对象数组,对每个元素调用析构函数,并读取数组长度信息。int* a = new int[10];
delete[] a; // ✅ 正确
// delete a; // ❌ 只析构第一个,未定义行为
4. new operator 与 operator new 的区别
-
new operator:语法new T,先分配内存再构造对象,不可重载。 -
operator new:底层内存分配函数,只负责"挖内存",可重载。void* p = operator new(sizeof(T)); // 只分配
new (p) T(); // 在已有内存上构造(placement new)
5. main 函数执行前会执行什么?执行后还能执行代码吗?
执行前:
- 加载可执行文件,初始化全局/静态变量。
- 调用全局对象的构造函数。
- 初始化 C 运行时环境、命令行参数
argc/argv。
执行后:
main返回后还可以执行代码,比如全局对象的析构函数、通过atexit注册的函数。- C++11 起可用
std::atexit注册退出处理。
6. 堆和栈的区别
| 特性 | 栈(Stack) | 堆(Heap) |
|---|---|---|
| 分配速度 | 快(SP 移动即可) | 慢(需查找合适内存块) |
| 大小 | 有限(通常几 MB) | 较大(受系统内存限制) |
| 生命周期 | 随函数调用/返回自动管理 | 手动管理,需 delete/free |
| 碎片 | 无 | 容易产生内存碎片 |
| 用途 | 局部变量、函数参数 | 动态分配的大对象、生命周期跨函数的对象 |
7. 局部变量、全局变量、动态申请数据存储在哪里?
| 变量类型 | 存储区域 |
|---|---|
| 局部变量 | 栈区 |
| 全局变量 / 静态变量 | 数据区(全局/静态存储区) |
动态申请(new/malloc) |
堆区 |
| 常量字符串 / 字面量 | 常量区(代码区附近) |
8. 编程实现 memcpy
void* my_memcpy(void* dest, const void* src, size_t n) {
char* d = static_cast<char*>(dest);
const char* s = static_cast<const char*>(src);
if (d == s) return dest;
// 未处理内存重叠,若重叠请用 memmove
while (n--) {
*d++ = *s++;
}
return dest;
}
⚠️ 真实
memcpy会按字长拷贝、处理对齐,且不处理内存重叠。
9. struct 的内存对齐与占用计算
编译器按成员最大对齐数对齐,填充空白字节。
struct S {
char a; // 1
// 3 bytes padding
int b; // 4
char c; // 1
// 3 bytes padding
}; // 总大小 12
规则:
- 每个成员的偏移量是其对齐值的倍数。
- 结构体总大小是最大对齐值的倍数。
- 可用
#pragma pack(n)或alignas修改对齐。
10. 智能指针的定义与作用
C++11 起提供三种智能指针:
std::unique_ptr:独占所有权,不可复制,可移动。std::shared_ptr:共享所有权,引用计数。std::weak_ptr:弱引用,不增加引用计数,用于打破循环引用。
作用:自动管理内存生命周期,避免内存泄漏和悬空指针 🎯
11. 内存泄漏及解决方法
内存泄漏:动态分配的内存未被释放,程序长期运行后内存持续增长。
解决方法:
- 遵循"谁申请谁释放"原则。
- 使用 RAII 和智能指针。
- 避免异常路径下遗漏
delete。 - 使用 Valgrind、AddressSanitizer 等工具检测。
12. 空指针和悬空指针的区别
| 类型 | 含义 | 示例 |
|---|---|---|
| 空指针 | 不指向任何有效对象的指针,值为 nullptr / NULL |
int* p = nullptr; |
| 悬空指针 | 指向曾经有效、但已被释放或越界的内存 | int* p = new int; delete p; // p 悬空 |
空指针可判断,悬空指针难以察觉,危害更大 💥
13. 智能指针的作用与缺点
作用:
- 自动释放资源。
- 明确所有权语义。
- 异常安全。
缺点:
shared_ptr有引用计数开销和循环引用风险。- 不能替代所有裸指针场景(如非所有权指针、C 接口)。
- 误用(如用多个
shared_ptr管理同一裸指针)会导致重复释放。
14. 程序运行时内存的数据区包括哪些部分?
代码区
数据区
├── 已初始化全局/静态变量区(.data)
├── 未初始化全局/静态变量区(.bss)
├── 堆区(Heap)
└── 栈区(Stack)
常量区(.rodata,只读数据)
15. C++ 内存泄漏介绍
常见类型:
- 未释放 :
new后没有delete。 - 异常泄漏 :异常抛出时跳过
delete。 - 循环引用 :两个
shared_ptr互相引用。 - 资源泄漏:文件句柄、锁、网络连接未释放。
解决思路:RAII + 智能指针 + 工具检测。
16. C++ 智能指针在模型服务中如何选择?
| 场景 | 推荐 |
|---|---|
| 模型实例、推理引擎等唯一所有者 | std::unique_ptr |
| 多个组件共享同一个模型/上下文 | std::shared_ptr |
| 缓存、回调、避免循环引用 | std::weak_ptr |
| 插件返回给框架的接口对象 | std::shared_ptr<Base> 或 std::unique_ptr<Base> |
| 临时借用,不拥有所有权 | 裸指针或引用 |
模型服务中常用
shared_ptr管理会话和模型句柄,unique_ptr管理一次性资源,尽量避免裸指针持有所有权 🧠
整体联系
这16题串起了 C++ 内存管理的完整链条:
分配 new/malloc
使用 栈/堆/数据区
回收 delete/free
智能指针自动管理
避免泄漏/悬空指针
模型服务中的资源生命周期管理

理解这套机制,是写出高性能、稳定、可维护 C++ 服务(尤其是推理、模型部署类服务)的基础 🚀