原生C风格数组无begin()和end()成员函数,需用std::begin()/std::end()或转为std::array;错误写法arr.begin()会编译失败。

用 begin() 和 end() 遍历 C++ 原生数组必须先转成 std::array 或用 std::begin()/std::end()
原生 C 风格数组(比如 int arr[5];)本身没有 begin() 或 end() 成员函数,直接调用会编译失败。常见错误是写成 arr.begin(),报错:error: 'begin' is not a member of 'int [5]'。
正确做法有两种:
- 把数组封装成
std::array:它有成员函数begin()和end(),类型安全且支持范围 for - 对原生数组用非成员函数
std::begin(arr)和std::end(arr)(C++11 起提供),它们专为内置数组重载
示例:
int arr[] = {1, 2, 3};
// ✅ 正确:用 std::begin/std::end
for (auto it = std::begin(arr); it != std::end(arr); ++it) {
std::cout << *it << " ";
}
<p>// ✅ 正确:转成 std::array
std::array<int, 3> a = {1, 2, 3};
for (auto it = a.begin(); it != a.end(); ++it) {
std::cout << *it << " ";
}
std::begin() 和 std::end() 对原生数组的参数要求很严格
这两个函数只接受“已知大小”的原生数组,不能传指针(哪怕它指向数组首地址)。传参时数组不能退化为指针,否则编译失败。
立即学习“C++免费学习笔记(深入)”;
典型翻车场景:
- 函数参数是
int arr[]或int* arr→ 数组大小丢失,std::begin(arr)无法推导长度,报错 - 用
new int[5]分配的动态数组 → 是指针,不是数组类型,std::begin()不接受 - 声明时没写大小,如
extern int arr[];→ 类型不完整,同样不可用
解决办法:在作用域内保留数组类型信息,或改用 std::vector / std::span(C++20)来承载运行时大小。
用 std::array::begin() 时注意类型和 const 正确性
std::array 的 begin() 返回迭代器类型取决于对象是否为 const:
- 非常量对象 → 返回
iterator(可读可写) - 常量对象或通过 const 引用访问 → 返回
const_iterator(只能读)
混用会导致编译错误,例如:
const std::array<int, 3> a = {1,2,3};
auto it = a.begin(); // it 是 const_iterator
*it = 5; // ❌ error: assignment of read-only location如果只需遍历读取,建议统一用 auto 推导;若需修改,确保源对象非常量,或显式用 std::array::data() 获取原始指针。
性能上,std::begin/end 和下标遍历几乎没差别
对原生数组,std::begin(arr) 就是返回 &arr[0],std::end(arr) 就是返回 &arr[N](N 为元素个数),零开销抽象。生成的汇编和手写 for (int i = 0; i 几乎一致。
但要注意:不要在循环条件里反复调用 std::end(arr)(虽然优化器通常能提出来),更稳妥写法是:
auto b = std::begin(arr), e = std::end(arr);
for (auto it = b; it != e; ++it) { ... }这在调试模式下更清晰,也避免某些老旧编译器的冗余计算。
真正容易被忽略的是数组生命周期——用 std::begin/std::end 遍历时,别让数组提前析构(比如局部数组被 return 出去再遍历),迭代器立刻变成悬垂指针,行为未定义。


















