std::copy_n定义在<algorithm>中,用于复制恰好n个元素,但不检查源范围有效性,易导致未定义行为;目标空间须预先分配足够容量,且不支持插入迭代器。

std::copy_n 的基本用法和头文件依赖
std::copy_n 是 C++11 引入的算法,定义在 <algorithm> 头文件中,用于从输入迭代器开始复制恰好 n 个元素到输出迭代器。它不检查源范围是否真有 n 个有效元素——这点极易导致未定义行为。
常见错误现象:std::copy_n 在源迭代器越界时不会抛异常,也不会断言,程序可能静默崩溃或读取垃圾值。
- 必须确保
first到first + n之间的所有位置都可解引用(即n == 0是安全的,但n > 0时first必须非空且后续至少n-1步有效) - 目标空间(
result起始地址)必须至少预留n个可写位置,否则写越界 - 对
std::vector使用时,不能直接传v.begin()到一个空vector的begin()—— 得先resize或用back_inserter(但后者不适用于copy_n,因它不支持插入式迭代器)
复制原生数组时的典型写法和指针陷阱
原生数组名退化为指针,容易误传长度或搞错边界。例如:
int src[5] = {1,2,3,4,5};
int dst[3];
std::copy_n(src, 3, dst); // ✅ 正确:复制前3个但下面这行会出问题:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::copy_n(src, 10, dst); // ❌ 源越界:src 只有5个元素
更隐蔽的坑是把数组传给函数后丢失长度信息:
- 函数参数若写
void f(int* arr),就无法在函数内用std::copy_n(arr, n, ...)安全调用,除非额外传入长度 - 推荐改用
std::span<const int>(C++20)或gsl::span来携带长度,或直接传std::array避免裸指针 - 用
std::size(src)替代硬编码数字,能防修改数组大小后的遗漏
与 std::copy 和 std::memcpy 的关键区别
std::copy_n 的核心价值是“只复制 n 个”,而 std::copy 需要明确的结束迭代器(first, last),写起来多两个参数;memcpy 是 C 函数,只支持 trivially copyable 类型,且不调用构造/析构。
- 对
std::string、std::vector<std::string>等含资源管理的对象,必须用std::copy_n(或std::copy),不能用memcpy -
std::copy_n是泛型算法,支持自定义迭代器(如std::istream_iterator),memcpy不支持 - 性能上,编译器通常会对
std::copy_n做优化(如生成memcpy指令),但前提是类型满足 trivially copyable 且优化开启
复制到 std::vector 时必须预先分配空间
std::vector::begin() 返回的是普通随机访问迭代器,不是插入迭代器。std::copy_n 不会自动扩容,也不会调用 push_back。
std::vector<int> src = {10,20,30,40,50};
std::vector<int> dst;
dst.resize(3); // ✅ 必须提前设好大小
std::copy_n(src.begin(), 3, dst.begin());如果漏掉 resize 或写成 dst.clear(),dst.begin() 就等于 dst.end(),写入将越界。
- 不能用
std::back_inserter(dst)配合std::copy_n—— 编译失败,因为back_inserter返回的是输出迭代器,不支持operator+,而std::copy_n内部需要移动迭代器 - 若目标容器大小不确定,改用
std::copy+std::back_inserter更自然 - 对小数组或已知容量场景,
std::copy_n更简洁;对动态增长场景,它不是最佳选择
最容易被忽略的一点:哪怕你确定 n 很小,只要源迭代器本身无效(比如空 vector 的 begin() 传给 n=1),std::copy_n 立刻 UB —— 它不做任何前置校验。

















