std::fill初始化数组必须显式提供迭代器范围,不可直接传原生数组名;正确写法为std::fill(arr, arr + N, 42)或std::fill(std::begin(arr), std::end(arr), 42),否则易越界或漏填。

用 std::fill 初始化数组时,别直接传原生数组名
原生数组名退化为指针后,std::fill 无法自动推导长度,容易只改前几个元素或越界。必须显式提供迭代器范围。
- 正确写法:
std::fill(arr, arr + N, 42);(C 风格数组)或std::fill(std::begin(arr), std::end(arr), 42);(推荐) - 错误写法:
std::fill(arr, arr + 100, 42);——若N 就越界;若N > 100则漏初始化 - 对
std::vector更安全:v.assign(v.size(), 42);或std::fill(v.begin(), v.end(), 42); -
std::fill不检查类型可赋值性,若元素是不可拷贝类且无合适赋值操作符,编译失败,错误信息里会含no match for operator=
用 std::iota 填充递增序列,注意起始值和迭代器类型匹配
std::iota 每次写入前自增起始值,所以它修改的是“起始值的副本”,但要求目标容器元素类型能接受该值的隐式转换或赋值。
- 常见误用:
int arr[5]; std::iota(arr, arr + 5, 1.5);——1.5是double,赋给int会截断,但编译通过,结果是{1,2,3,4,5},易被忽略 - 安全做法:显式类型一致,如
std::iota(arr, arr + 5, 1);或用long long start = 10000000000LL;配合long long arr[...] - 不适用于
std::array或含const元素的容器——编译报错assignment of read-only location - 对
std::vector填充大索引时,注意size_t无符号特性,避免从负数起始导致回绕
fill 和 iota 在栈数组、堆数组、STL 容器中的调用差异
三者底层都依赖迭代器,但获取方式不同,稍不注意就会传错参数类型。
- 栈数组(
int a[10];):用std::begin(a)/std::end(a)最稳;a和a+10可用但硬编码易错 - 堆数组(
int* p = new int[10];):std::fill(p, p + 10, 0);合法,但必须自己记长度;不能用std::begin(p)(未定义行为) -
std::vector/std::array:优先用.begin()/.end()成员函数,语义清晰且支持 ADL - 性能上无实质差别——都是 O(n) 赋值循环,但
std::iota多一次加法运算,对超大数组可测出微小差距
替代方案:为什么有时不该用 fill/iota?
当初始化逻辑复杂、需条件判断、或涉及构造而非赋值时,std::fill 和 std::iota 就不够用了。
- 对象数组(如
std::string arr[100];):用std::fill只会调用operator=,不会重新构造;若想清空并重置为默认状态,应考虑for (auto& s : arr) s.clear(); - 需要按索引计算值(如
arr[i] = i * i):std::iota无能为力,改用std::transform+ lambda 或简单 for 循环更直白 - 编译期已知值:用
constexpr数组或聚合初始化(int a[] = {1,2,3};)比运行时 fill 更高效,也利于常量传播优化 - 多线程环境批量初始化:
std::fill本身不并发,若需并行,得自己拆分范围 +std::thread或用std::for_each配合执行策略(C++17)
实际写的时候,最容易被忽略的是原生数组与指针边界的混淆,以及 std::iota 对起始值类型的静默截断——这两处不报错但结果不对,调试起来反而更费时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











