共计 2460 个字符,预计需要花费 7 分钟才能阅读完成。
最近在指导新人学习 C ++ 时,发现数组作为函数参数传递这个看似简单的操作,居然能难倒不少初学者。今天我们就来彻底搞懂其中的门道,顺便分享些工程中的实用技巧。

从 sizeof 的陷阱说起
先看这段新手容易翻车的代码:
void printSize(int arr[]) {std::cout << "Array size:" << sizeof(arr) << std::endl; // 输出指针大小而非数组长度!}
int main() {int data[5] = {1,2,3,4,5};
std::cout << "Actual size:" << sizeof(data) << std::endl; // 正确输出 20(假设 int 为 4 字节)
printSize(data); // 通常输出 8(64 位系统指针大小)
}
这个经典问题暴露了 C ++ 数组的第一个特性: 数组作为参数时会退化为指针 。接下来我们看看如何正确处理。
三种传参方式对比
1. 传统指针模式
// 最基础的传递方式,但丢失了长度信息
void processWithPointer(int* arr, size_t len) {for(size_t i=0; i<len; ++i) {arr[i] *= 2; // 安全访问需要依赖外部传入的 len
}
}
特点 :
– 最接近底层的方式
– 完全依赖外部传入长度参数
– 容易发生越界访问
2. 显式长度传递
// 语法糖形式,实际仍是指针
void processWithArraySyntax(int arr[], size_t len) {for(size_t i=0; i<len; ++i) {arr[i] += 1; // 同样需要外部长度
}
}
虽然用了数组语法,但编译器仍然会将其视为指针。这种写法更直观但本质没变。
3. 现代 C ++ 方案
使用 std::array(C++11 起)
#include <array>
void processWithStdArray(std::array<int, 5>& arr) {for(auto& item : arr) { // 支持范围 for 循环
item -= 1;
}
// 自带 size() 方法
std::cout << "Array size:" << arr.size() << std::endl;}
使用 std::span(C++20 起)
#include <span>
void processWithSpan(std::span<int> arr) {for(auto& item : arr) {item = 0;}
// 自动记录长度
std::cout << "Span size:" << arr.size() << std::endl;}
现代方案的优势:
– 自带长度信息
– 支持迭代器
– 更安全的访问控制
完整示例代码
#include <iostream>
#include <array>
#include <span>
// 传统指针方式
void pointerStyle(int* arr, size_t len) {
std::cout << "Pointer style:\n";
for(size_t i=0; i<len; ++i) {std::cout << arr[i] << " ";
}
std::cout << "\n";
}
// std::array 方式
template<size_t N>
void arrayStyle(std::array<int, N>& arr) {std::cout << "std::array style (size=" << arr.size() << "):\n";
for(const auto& item : arr) {std::cout << item << " ";}
std::cout << "\n";
}
// std::span 方式
void spanStyle(std::span<int> arr) {std::cout << "std::span style (size=" << arr.size() << "):\n";
for(const auto& item : arr) {std::cout << item << " ";}
std::cout << "\n";
}
int main() {int cArray[5] = {1,2,3,4,5};
std::array<int, 5> stdArray = {6,7,8,9,10};
pointerStyle(cArray, 5);
arrayStyle(stdArray);
spanStyle(cArray); // 兼容 C 风格数组
spanStyle(stdArray); // 也兼容 STL 容器
}
性能考量
- 栈分配差异
- C 风格数组和 std::array 都在栈上分配
-
std::span 只是包装指针和长度,无额外开销
-
访问效率
- 指针解引用和数组索引访问效率相当
- 现代编译器都能优化迭代器访问
实测在 -O2 优化下,几种方式的性能差异可以忽略不计。
生产环境建议
多线程安全
// 使用 const 保证线程安全
void safeProcess(const std::span<const int> arr) {// arr[0] = 1; // 编译错误,防止意外修改
}
静态检查
使用 clang-tidy 检测潜在问题:
clang-tidy -checks="-*,modernize-avoid-c-arrays" example.cpp
与 STL 算法结合
#include <algorithm>
#include <numeric>
void stlBestPractice(std::span<int> arr) {
// 使用标准算法
std::sort(arr.begin(), arr.end());
auto sum = std::accumulate(arr.begin(), arr.end(), 0);
}
思考题延伸
如何设计同时支持 C 数组和 STL 容器的接口?可以考虑:
1. 使用模板 + 迭代器
2. 统一用 std::span 作为参数
3. 提供重载版本
你会选择哪种方案呢?在实际项目中,我倾向于方案 2,因为它在 C ++20 后提供了最好的兼容性和安全性。
通过这次梳理,希望大家能彻底理解 C ++ 数组参数传递的各种姿势。记住:在现代 C ++ 中,除非有特殊需求,否则优先考虑 std::array 和 std::span,它们能让你的代码更安全、更易维护。
正文完
