C++fill填充容器指定值

2026-04-11 15:15:32 547阅读 0评论

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);

注意:它压根不接收容器对象,只吃两个迭代器——firstlast。这意味着:

  • 容器必须已存在且有足够空间
  • firstlast必须构成合法、可写入的前向迭代器范围([first, last));
  • 如果last == firstfill什么也不做,安全退出;
  • 如果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!

assignresizefill_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); // 精准、轻量、零副作用

assignresize反而画蛇添足——它们会试图重建整个容器语义,而这里你只是擦掉一部分旧值。

原生数组?fill一样能上手,但得懂指针算术

别以为fill只配STL容器。原生数组照样服帖:

int raw[100];
fill(raw, raw + 100, -1); // ✅ 合法:raw是int*,raw+100是尾后指针

关键点在于:rawraw + 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前,花三秒默念:

  1. 容器空间已就绪?size()够不够?resize()/reserve()是否到位?)
  2. 迭代器范围合法?first <= lastlast是否在[begin(), end()]内?)
  3. 值类型可赋值?(自定义类需有拷贝赋值运算符,const成员或删除了operator=会编译失败)

最后提一句:fill对POD类型极快(通常编译为memset或向量化指令),但对含构造/析构逻辑的类类型,它老老实实调用每个元素的赋值运算符——性能敏感场景,心里要有数。

fill没有魔法,它只是把你的意图,稳稳地、一字不差地,刻进那一段你亲手划定的内存里。用对了,是效率利器;用糙了,就是静默的雪崩起点。写代码如耕田,犁沟要直,下种要准——fill,正是那把最朴素、也最不容敷衍的锄头。

文章版权声明:除非注明,否则均为Dark零点博客原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
验证码
评论列表 (暂无评论,547人围观)

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

目录[+]