C++fill填充容器指定值
C++里fill不是“填坑”,是精准灌注——手把手理清容器值填充的边界与陷阱
刚学C++那会儿,我写了个小工具处理传感器数据,想把一个vector<double>全设成-999.0表示无效值。顺手敲下fill(v.begin(), v.end(), -999.0),结果程序跑着跑着崩溃了——调试半天才发现,v根本没分配空间,begin()和end()指向的是空范围,fill照常执行,但没内存可写。那一刻才明白:fill从不负责“准备容器”,它只干一件事:往已有有效迭代器区间里灌值。
fill不是万能橡皮擦,也不是自动扩容键。它是标准库里的“灌注工”,只认地址,不问地基牢不牢。
fill真正干活的地方:迭代器区间,不是容器本身
std::fill定义在<algorithm>头文件中,原型就一行:
template<class ForwardIterator, class T>
void fill(ForwardIterator first, ForwardIterator last, const T& value);
注意:它压根不接收容器对象,只吃两个迭代器——first和last。这意味着:
- 容器必须已存在且有足够空间;
first和last必须构成合法、可写入的前向迭代器范围([first, last));- 如果
last == first,fill什么也不做,安全退出; - 如果
last落在first之前?行为未定义——别赌运气,先校验。
常见误用场景:
vector<int> v;
fill(v.begin(), v.end(), 42); // ✅ 合法!空范围,无操作
v.resize(5);
fill(v.begin(), v.end(), 42); // ✅ 正确:空间已就位
// 但下面这句是典型踩坑:
vector<int> u;
fill(u.begin(), u.begin() + 10, 42); // ❌ 危险!u.begin()+10越界,UB!
和assign、resize、fill_n划清界限
新手容易混淆这几个“填”字辈函数:
vec.assign(n, val):重置整个容器,等价于先清空再插入n个val;会改变size()和capacity();vec.resize(n, val):调整大小,若扩大,新增元素用val填充;若缩小,直接截断;fill_n(first, n, val):从first开始填n个,不依赖last,但要求first + n必须有效;fill(first, last, val):最纯粹——只动指定区间,不碰容器结构,不申请内存,不改size()。
举个实在例子:你有个固定长度的环形缓冲区array<int, 1024> buf,只需刷新有效数据段(比如索引0~63),那就该用:
fill(buf.begin(), buf.begin() + 64, 0); // 精准、轻量、零副作用
用assign或resize反而画蛇添足——它们会试图重建整个容器语义,而这里你只是擦掉一部分旧值。
原生数组?fill一样能上手,但得懂指针算术
别以为fill只配STL容器。原生数组照样服帖:
int raw[100];
fill(raw, raw + 100, -1); // ✅ 合法:raw是int*,raw+100是尾后指针
关键点在于:raw和raw + 100必须指向同一块连续内存的合法边界。如果数组是栈上局部变量,没问题;如果是new int[100],也OK;但若raw是某个结构体成员且后面没预留空间,raw + 100就可能越界——fill可不管这些,它只信你给的指针。
迭代器失效?fill自己不惹事,但可能帮凶
fill本身不导致迭代器失效(它不增删元素),但它常出现在“先修改容器,再填充”的流程里。比如:
vector<string> words = {"a", "bb", "ccc"};
words.erase(words.begin()); // size变为2,原有迭代器可能失效
fill(words.begin(), words.end(), "X"); // ✅ 此时begin()/end()已更新,安全
重点来了:fill用的迭代器必须是调用时刻有效的。如果你在erase后还拿着旧end()迭代器传给fill,那就是拿过期票进站——编译器不拦,运行时可能崩。
实战建议:三步检查法
写fill前,花三秒默念:
- 容器空间已就绪?(
size()够不够?resize()/reserve()是否到位?) - 迭代器范围合法?(
first <= last?last是否在[begin(), end()]内?) - 值类型可赋值?(自定义类需有拷贝赋值运算符,
const成员或删除了operator=会编译失败)
最后提一句:fill对POD类型极快(通常编译为memset或向量化指令),但对含构造/析构逻辑的类类型,它老老实实调用每个元素的赋值运算符——性能敏感场景,心里要有数。
fill没有魔法,它只是把你的意图,稳稳地、一字不差地,刻进那一段你亲手划定的内存里。用对了,是效率利器;用糙了,就是静默的雪崩起点。写代码如耕田,犁沟要直,下种要准——fill,正是那把最朴素、也最不容敷衍的锄头。


还没有评论,来说两句吧...