空容器在范围for循环中完全安全,无需提前判空;std::array合法,n须为编译期常量;用size_t反向遍历会因无符号溢出导致死循环。

for 循环遍历 std::vector 时,空容器完全安全
空的 std::vector、std::list、std::array(大小为 0)在范围 for 循环中不会执行任何迭代,也不会触发未定义行为。这是标准保证的行为。
常见错误是误以为需要提前判空,比如:
if (!vec.empty()) {
for (auto& x : vec) { ... }
}
这纯属冗余——for (auto& x : vec) 在 vec 为空时自动跳过循环体,无性能损耗,也无风险。
但要注意:std::array<t></t> 是合法类型,C++17 起支持;而 std::array<t n></t> 的 N 必须是编译期常量,不能是变量。
用 for (size_t i = 0; i 遍历时,<code>size_t 下溢是真坑
当容器为空时,vec.size() 返回 0u(size_t 类型),若写成递减式循环,比如:
for (size_t i = vec.size(); i >= 0; --i) { ... }
会立刻陷入死循环:因为 size_t 是无符号类型,i-- 到 0 后继续减,变成极大正数(如 18446744073709551615),条件 i >= 0 永远成立。
正确做法只有两种:
- 改用有符号整型索引(如
int i = 0; i ),但要确保 size 不超 <code>INT_MAX - 更推荐:用
ptrdiff_t或直接避免递减循环,改写为for (size_t i = 0; i —— 这种写法对空容器天然安全
C 风格数组声明为 T arr[0] 或 T arr[] 时,不能用范围 for
零长数组(如 int arr[0];)不是标准 C++,GCC/Clang 作为扩展支持,但 for (auto x : arr) 会编译失败:编译器无法推导出长度,begin(arr) 和 end(arr) 无定义。
动态分配的“伪空数组”(如 int* p = new int[0];)同样不可遍历 —— 没有长度信息,std::begin(p) 不接受裸指针。
如果你必须处理这种场景:
- 改用
std::vector或std::span(C++20),显式传入数据 + 长度 - 若只能用裸指针,循环必须带长度参数:
for (size_t i = 0; i ,且 <code>len == 0时自然不进循环
迭代器失效和 erase 混合空容器时的错觉
有人觉得“空容器调用 erase 会崩”,其实不会:vec.erase(vec.begin(), vec.end()) 对空容器是合法且无操作的;vec.erase(vec.begin()) 在空容器上调用则触发未定义行为——因为 vec.begin() == vec.end(),不能解引用。
所以真正危险的不是“空”,而是「对 end() 迭代器做非法操作」。常见误写:
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (should_erase(*it)) vec.erase(it); // ❌ 破坏 it 有效性,且 it 可能已是 end()
}
这个 bug 在非空容器里也会崩溃,空容器只是让它“恰好不触发”,容易掩盖问题。
正确擦除方式始终是:
- 用
erase-remove惯用法:vec.erase(std::remove_if(vec.begin(), vec.end(), pred), vec.end()); - 或手动维护迭代器:
it = vec.erase(it);(此时不 ++it)
空容器让某些错误看起来“没出事”,反而更难被发现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











