std::fill 直接按字节赋值,不调用构造/析构,仅适用于可赋值类型;对非pod对象易致未定义行为,应优先用 std::fill_n、花括号初始化或 std::span。

std::fill 是最常用也最容易误用的初始化方式之一——它不关心类型是否可平凡复制,也不检查内存对齐,直接按字节逐个赋值;对 std::array、原生数组或 std::vector 的底层数据有效,但对含构造函数的对象数组会跳过构造逻辑,导致未定义行为。
fill 要求迭代器可写,且元素支持赋值操作
比如你写 std::fill(arr, arr + N, 0),编译器会调用每个元素的 operator=。这意味着:
- 内置类型(
int、double)完全安全 -
std::string或自定义类必须有公有、非 explicit 的赋值运算符,否则编译失败 - 若对象已构造,
fill是“再赋值”,不是“重构造”;不会调用析构再构造,只调用= - 对
const数组、std::vector<const t></const>等不可写容器,直接报错:assignment of read-only location
替代方案:memset 仅适用于 POD 类型的全零初始化
memset 比 fill 快,但极度危险——它只管字节,不管语义。只有满足以下全部条件才可用:
- 目标是 POD 类型(如
int、float、struct无虚函数/非静态成员/用户定义构造函数) - 只填
0(memset(ptr, 0, size)),填其他值(如-1)可能破坏浮点数或指针的位模式 - 确认数组地址连续、无 padding 干扰(结构体需
alignas或#pragma pack配合)
错误示例:memset(&obj, 0, sizeof(obj)) 对含 std::string 成员的 struct 会导致内部指针悬空。
立即学习“C++免费学习笔记(深入)”;
更安全的现代写法:std::fill_n、value-initialization、或 span-based 初始化
避免裸指针和长度计算错误,优先用:
-
std::fill_n(std::begin(arr), std::size(arr), 42)—— 自动推导长度,不传arr + N - 初始化时直接用花括号:
int arr[10] = {}; // 全零或std::array<int> a = {1}; // 首元素=1,其余=0</int> - C++20 起可用
std::span封装后操作:auto s = std::span(arr); std::fill(s.begin(), s.end(), 7);
注意:std::fill 和 std::fill_n 都不做类型擦除,不能用于 void* 内存块——那是 std::uninitialized_fill 的职责范围,但后者要求类型可默认构造且内存未初始化。
性能差异其实很小,别为“快”牺牲正确性
在多数场景下,std::fill 和 memset 的实测差距不到 10%,尤其当数组小于几 KB 时。真正拖慢的是缓存不友好访问模式,而不是函数本身。容易被忽略的关键点是:
- 对
std::vector<t></t>,别对&vec[0]用memset——除非你 100% 确认T是 trivially copyable 且填 0 - 多线程环境下,
fill不是原子操作,不要跨线程共享同一块内存并并发调用 - 调试时发现值异常?先查是不是用了
fill去“初始化”一个本该用std::vector<t>(n)</t>构造的对象容器









