new int[0]合法但危险:返回非空指针,不可解引用或算术运算,仅可传给delete[];与std::vector{}本质不同,无状态封装和安全扩展能力。
![c++动态分配0大小的数组new int[0]合法吗会有什么问题](https://img.php.cn/upload/article/001/221/864/179005615783851.png)
new int[0] 是合法的,但别当真用
标准明确允许 new int[0],编译器不会报错,返回一个非空指针——但它不指向任何有效元素。这个指针只能传给 delete[],不能解引用、不能做指针算术(比如 p + 1)、不能当成真实数组用。
常见误用场景:
- 在类中写
arr = new int[0];试图“初始化为空”,结果后续resize或push_back逻辑缺失,导致越界或未定义行为 - 把
new int[0]当作“安全占位符”传给 C 接口,而该接口实际会读取size > 0的内存,引发静默崩溃 - 调试时发现指针值不为
nullptr就以为“分配成功了”,忽略它根本不能存数据
为什么编译器允许 new int[0]?它到底返回什么
operator new[] 对 0 字节请求的语义是:返回一个**唯一、非空、可安全传给 delete[] 的指针**。它不是返回 nullptr,也不是复用已有地址——每次调用都可能返回不同地址(MSVC/GCC 都如此)。
这意味着:
立即学习“C++免费学习笔记(深入)”;
-
int* p = new int[0]; int* q = new int[0];中,p != q是完全可能的 -
if (p == nullptr)永远为假,加这种判断毫无意义 - 对
p做sizeof(*p)或p[0]是未定义行为,哪怕只是取地址(&p[0])也不行
new int[0] 和 std::vector{} 的本质区别
std::vector<int></int> 构造为空时,内部指针可能是 nullptr(实现相关),但所有接口(size()、empty()、push_back())都明确定义;而 new int[0] 只是一个裸指针,没有任何状态封装。
关键差异点:
-
std::vector可以随时resize(100),new int[0]之后必须delete[]再new int[100],否则就是内存泄漏 -
std::vector移动后原对象自动变为 valid-but-empty;裸指针移动后容易悬空或重复释放 -
std::vector在异常抛出时能保证强异常安全;裸指针分配失败需手动处理std::bad_alloc或std::nothrow
真正该警惕的不是 new int[0],而是它的上下文
单独写一行 new int[0] 很少出事,问题总出现在它被嵌入到某个生命周期管理逻辑里:
- 类成员初始化列表里写
arr(new int[0]),但没实现拷贝构造——浅拷贝后两个对象共用同一块“空内存”,delete[]两次 - 函数返回
new int[0],调用方误以为可以memcpy或传给std::span,而后者要求size > 0才能构造 - 在嵌入式环境禁用异常(
-fno-exceptions)下,又没用std::nothrow,此时new int[0]虽然合法,但紧随其后的new int[big_size]失败时会直接终止,根本没机会清理前面那个“空数组”的指针
最易被忽略的一点:空数组本身无害,但一旦你开始根据 size == 0 做分支逻辑(比如跳过循环、绕过校验),这些分支往往成为边界 case 漏洞温床——尤其是当 size 后续被外部输入控制时。


















