std::fill是批量赋相同值最直接的方法,用于连续内存范围,需传入首尾迭代器及值,如std::fill(arr, arr + N, 42);不支持原生数组直接传数组名和长度,且对非零多字节值通常不优化为memset。

用 std::fill 批量赋相同值最直接
如果你只是想把整个数组(或某段连续内存)设成同一个值,std::fill 是语义最清晰、编译器优化最充分的选择。它不关心类型是否可比较或是否支持自增,只要能赋值就行。
常见错误是传错迭代器范围:比如把 end 写成 begin + size + 1 导致越界;或者对 std::array 忘记用 .data() 取指针,直接传数组名会退化为指针但长度信息丢失。
-
std::fill(arr, arr + N, 42)—— 原生数组 -
std::fill(vec.begin(), vec.end(), 0.0f)——std::vector -
std::fill(arr.data(), arr.data() + arr.size(), 'x')——std::array
注意:std::fill 对 POD 类型通常会被编译器自动优化为 memset 或向量化指令(如 AVX),但前提是值是 0 或单字节重复模式(如 0xFF)。非零多字节值(如 int(12345))不一定能内联为 memset,实际走的是循环赋值。
用 std::iota 生成等差序列要小心起始值
std::iota 的作用不是“初始化”,而是“按步长填充递增序列”,默认步长为 1。它要求元素类型支持 operator++(或至少支持 operator+= 和整数加法),且起始值必须能被该类型容纳。
典型误用是以为它能填任意等差数列——它只能填公差为 1 的整数序列(或用户自定义类型的前置++行为所定义的“下一个”)。想填 {10, 20, 30}?std::iota 做不到,得手写循环或用 std::transform。
-
std::iota(arr, arr + N, 100)→ 填{100, 101, 102, ...} - 对
std::vector也能用,但要注意浮点精度累积误差(第N项 ≈ 起始值 +N-1) - 若起始值是
unsigned int且填到溢出边界,行为是未定义的(不会自动回绕)
性能差异主要看编译器和数据规模
小数组(N )基本没差别,现代编译器都会把 std::fill 和 std::iota 展开为几个 mov 指令;真正拉开差距的是大块内存(>4KB)和是否触发向量化。
-
std::fill在填 0 或单字节常量时,GCC/Clang/MSVC 都倾向生成memset,速度接近内存带宽极限 -
std::iota几乎从不向量化(因为依赖前一项结果),即使对int数组也是逐个加 1,L1 缓存友好但吞吐受限于加法指令延迟 - 实测:在 1MB
int数组上,std::fill(..., 0)比std::iota(..., 0)快 3–5 倍(Skylake,O2)
别迷信“STL 函数一定高效”——iota 的语义决定了它无法跳过顺序依赖,这是算法层面的硬限制。
别忽略构造函数初始化这个更早的时机
如果数组生命周期内只初始化一次,且你控制得了定义点,用构造函数比运行时调 fill 或 iota 更轻量:
std::arrayarr = {}; // 全零初始化 std::vectorvec(1000, 42); // 构造时填 42 int arr[1000] = {1}; // 第一个为 1,其余自动零初始化
这些方式在栈/全局区可能直接由 bss/data 段承载,连运行时代码都不生成。而 std::fill 和 std::iota 至少要进函数调用、算地址、跑循环——哪怕只有 3 行汇编,也比零次执行多一次机会出错。
真正容易被忽略的是:很多人先默认堆上 new 一个数组,再用 fill 初始化,却没想过 vector 构造函数或 std::array 的聚合初始化能不能替代它。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











