std::fill 和 std::iota 语义不同:fill 填相同值,iota 填严格+1递增整数序列;误用会导致编译失败或未定义行为,且性能差异源于编译器优化能力而非算法复杂度。

直接说结论:std::fill 和 std::iota 不是性能竞赛,而是语义分界线——填相同值就用 std::fill,填严格 +1 递增整数序列才用 std::iota;选错一个,轻则编译失败,重则未定义行为。
std::fill 填相同值,但传错范围会直接越界崩溃
std::fill 的作用就是把 [first, last) 范围内每个位置都赋成同一个值。它不做任何边界检查,也不管你传的是不是合法内存。
- 对 C 风格数组
int arr[10],必须写std::fill(arr, arr + 10, 42);写成arr + 9漏最后一个,arr + 11就越界 UB - 用
std::begin(arr)和std::end(arr)更安全,C++11 起原生支持,且数组大小改了不用动代码 - 对
std::vector,别在空容器上调用std::fill(v.begin(), v.end(), x)——它什么也不做;要扩容+赋值,直接用v.assign(n, x) - 对
std::array,优先调用成员函数a.fill(x)(C++11),不依赖<algorithm></algorithm>,也更短
std::iota 填递增序列,但不是“等差数列通用工具”
std::iota 的行为是:第 0 个位置赋 value,第 1 个赋 ++value(前置递增),第 2 个再 ++value……它不接受步长参数,也不支持倒序或浮点精确步进。
- 想填
{10, 20, 30}?std::iota做不到,得用std::generate配 lambda:[n = 10]() mutable { return (n += 10); } - 对
unsigned char buf[5]调用std::iota(buf, buf + 5, 254),第二个元素就溢出,结果未定义 - 对
std::vector<double></double>用std::iota,第 100 万个元素可能偏离理论值超 0.1,因浮点累加误差不可控 - 对
std::string数组或没重载operator++的自定义类,编译直接失败
性能差异本质是编译器能否跳过循环
两者都是 O(n) 线性遍历,真实差距不在算法,而在编译器能否识别模式并替换为底层指令。
-
std::fill(ptr, ptr + n, 0)在 GCC/Clang -O2 下大概率被优化为memset,尤其对int或char数组,速度接近内存带宽极限 -
std::fill(ptr, ptr + n, 42)对非零多字节值不一定触发memset,但现代编译器常能向量化为 AVX store -
std::iota几乎无法 SIMD 化,因为每个值依赖前一个;实测 10Mint数组上,它比std::fill(, 0)慢约 30%~50% - 小数组(
真正该优先考虑的初始化方式,往往不是 fill 或 iota
它们只是运行时填充手段。若值在编译期已知,声明即初始化才是零开销首选:
-
int arr[1024] = {};—— 全零,由编译器生成最优机器码(如 .data 段置零) -
std::array<int> a = {0};</int>—— 首元素为 0,其余默认初始化 - 局部大数组(如
int arr[100000])可能栈溢出,应改用std::vector或new[] - 对齐到 cache line 边界(
alignas(64))对后续遍历有帮助,但对初始化本身影响微弱
最容易被忽略的一点:所有这些函数都只写入**已分配空间**,不负责构造对象。对未初始化内存(如 malloc 分配区域)、未 resize 的 vector、或 vector<bool></bool> 这类特化容器,行为要么 UB,要么编译失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











