C++数组作为函数参数:从基础语法到高效传递的完整指南

1次阅读
没有评论

共计 1751 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

为什么数组传参让人头疼?

刚学 C ++ 时,我以为这样传数组很合理:

void printArray(int arr[]) {for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++) {std::cout << arr[i] << ' ';
    }
}

直到运行时发现 sizeof(arr) 得到的永远是指针大小(比如 8 字节),才意识到数组在传参时会 退化为指针。这种隐式转换导致:

C++ 数组作为函数参数:从基础语法到高效传递的完整指南

  • 丢失数组长度信息
  • 无法进行边界检查
  • 必须额外传递 size 参数

三种传统传参方式对比

1. 形参声明为数组语法(最危险)

void func(int arr[5]);  // 实际等价于 int* arr

编译器会静默转换,中括号里的数字甚至会被忽略。我在 VS2022 测试时,传长度 3 的数组也能编译通过!

2. 显式使用指针(最通用)

void func(int* arr, size_t size);

需要手动传递大小,但能处理动态数组。适合 C 风格接口,但缺少类型安全性。

3. 数组引用(最安全但死板)

template<size_t N>
void func(int (&arr)[N]);  // N 会被自动推导

能保留数组类型和长度,但只能接受固定大小的栈数组。在需要灵活性的场景受限。

现代 C ++ 的救星

std::array(C++11 起)

void process(const std::array<int, 5>& arr) {for(auto val : arr) {/* 安全遍历 */}
}

– 自带 size()方法
– 支持迭代器
– 值传递时不会退化为指针

std::span(C++20 起)

void analyze(std::span<int> data) {std::cout << "元素个数:" << data.size();
}

– 可接受原生数组、std::array、vector 等
– 自动记录长度
– 非拥有视图,零成本抽象

避坑实战指南

错误 1:越界访问

错误代码:

int arr[3] = {1,2,3};
for(int i=0; i<=3; i++) {  // 最后一次访问越界
    arr[i] *= 2;
}

修复方案:

// 方法 1:range-for(C++11)for(auto& elem : arr) {elem *= 2;}

// 方法 2:使用 span(C++20)auto view = std::span(arr);
for(size_t i=0; i<view.size(); i++) {view[i] *= 2; }

错误 2:混淆静态 / 动态数组

错误代码:

int* arr = new int[10];
size_t wrongLen = sizeof(arr);  // 返回指针大小而非数组长度

正确做法:

// 动态数组必须自己记录长度
const size_t len = 10;
int* dynArr = new int[len];
delete[] dynArr;  // 注意匹配 new[]/delete[]

错误 3:错误的多维数组传递

错误代码:

void print2D(int arr[][]) {/*...*/}  // 编译错误

正确写法:

// 必须指定第二维大小
void print2D(int arr[][10], int rows);

// 更推荐用 vector 或 array 嵌套
using Matrix = std::vector<std::array<int, 10>>;

性能优化建议

通过 Compiler Explorer 观察不同传参方式的汇编代码:

  1. 传指针 + 大小:生成代码最精简,适合高频调用
  2. std::array 引用:与原生数组性能相当,但更安全
  3. std::span:在开启优化时几乎无额外开销

在热路径代码中,可以这样优化:

// 对已知大小的数组使用模板
template<size_t N>
void fastProcess(const int (&arr)[N]);

// 对性能敏感场景标记为 inline
inline void quickHandle(std::span<int> data);

思考与实践

最后留个思考题:如何设计一个同时接受以下输入的函数?

int cArr[5];
std::array<int, 5> stdArr;
std::vector<int> vec(5);

提示:可以结合模板和 C ++17 的 if constexpr 实现编译期分支。当你掌握这个技巧,就能写出真正通用的容器操作函数了!

希望这篇笔记能帮你避开我踩过的坑。数组传参看似简单,但魔鬼都在细节里。

正文完
 0
评论(没有评论)