最稳妥方式是传数组指针加长度、用std::vector或c++20的std::span;模板推导数组长度可零开销实现编译期定长传参,但仅限栈上固定数组。

用指针或引用参数批量修改数组元素
直接传数组名会退化成指针,但无法获知长度,容易越界;最稳妥的方式是把数组长度一并传入,或使用 std::vector 避免手动管理。C++11 起推荐用 std::span(C++20 正式支持),它轻量、不拥有所属数据,又能携带大小信息。
- 原始数组传参必须带长度:函数声明形如
void modify(int* arr, size_t n),调用时写modify(arr, 5) - 用
std::vector更安全:可直接传引用void modify(std::vector<int>& v)</int>,内部用v[i]或范围 for 修改 -
std::span是理想选择(需 C++20):void modify(std::span<int> s)</int>,调用时写modify(std::span(arr)),自动推导长度,且不拷贝 - 避免写
void modify(int arr[])—— 这和int* arr完全等价,编译器不检查边界,极易误用
用模板推导数组长度实现真正“按值传数组”
如果坚持用原生数组且想免去手传长度,可用模板参数推导:让编译器从实参中提取数组大小。这种方式在编译期确定长度,零运行时开销,但只适用于栈上定义的固定大小数组(不能用于 new 或函数返回的数组)。
- 模板声明:
template<size_t n> void modify(int (&arr)[N])</size_t>,其中int (&arr)[N]是对 N 元素数组的引用 - 内部可直接用
N循环:for (size_t i = 0; i - 调用仅限栈数组:
int a[3] = {1,2,3}; modify(a);✅;int* p = new int[3]; modify(p);❌(类型不匹配) - 不适用于
std::array,后者需单独重载或改用泛型:template<typename t size_t n> void modify(std::array<t>& arr)</t></typename>
常见错误:误以为传数组就是传副本
C++ 中数组名作为函数参数时,永远不会复制整个数组内容——它总是以指针形式传递(即“传地址”)。所谓“修改生效”,本质是修改了原内存位置。新手常因误解这点,写出看似修改却无效果的代码。
- 错误写法:
void modify(int arr[5]) { /* ... */ }→ 实际仍是int* arr,sizeof(arr)返回指针大小(通常是 8),不是 5×4 - 若真想复制一份再改,必须显式拷贝:用
std::vector<int> copy = original;</int>或std::array赋值,否则所有“修改”都作用于原始内存 - 多维数组更易出错:
int mat[3][4]传参须写void f(int (*m)[4], int rows),漏掉列数[4]就编译失败
性能与兼容性取舍:vector vs span vs 模板数组引用
三者都能安全批量修改,但适用场景不同。选错会导致冗余拷贝、编译失败或 ABI 不兼容。
-
std::vector:最通用,支持动态大小,但涉及堆分配;若输入是栈数组,传std::vector会触发拷贝构造(除非 move) -
std::span:零成本抽象,兼容原生数组、vector::data()、malloc 内存;但 C++20 才标准,旧项目需用 gsl::span 或自行实现简易版 - 模板数组引用:编译期长度检查严格,无任何开销,但函数实例化数量随数组长度增长(
modify和modify是两个函数),模板膨胀需留意
真正麻烦的不是语法,而是混用场景:比如函数要同时处理 int[10]、std::vector<int></int> 和用户自定义容器。这时候得靠重载或概念(C++20 std::ranges::range)统一接口,而不是硬塞一种传参方式。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











