在C++编程中,数组作为函数参数传递是日常开发中非常常见的操作,但很多初学者甚至有一定经验的开发者,都对数组传递时的退化现象和指针参数的关联逻辑存在疑惑,本文将深入解析相关机制。

C++数组作为函数参数的常见传递方式
1. 以数组形式声明参数
这种写法看起来是传递了完整的数组,但实际上只是语法糖,编译器会自动将其处理为指针形式。
#include <iostream>
using namespace std;
// 参数看似是数组,实际会被解析为指针
void print_array(int arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
int nums[5] = {1, 2, 3, 4, 5};
print_array(nums, 5);
return 0;
}
2. 以指针形式声明参数
这种方式直接声明参数为指针,和第一种写法在编译后的效果完全一致,是更直观的写法。
#include <iostream>
using namespace std;
// 直接声明为指针参数,和数组形式声明效果相同
void print_array_ptr(int* arr, int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
int nums[5] = {1, 2, 3, 4, 5};
print_array_ptr(nums, 5);
return 0;
}
3. 传递数组的引用
如果希望避免数组退化,可以传递数组的引用,这种方式需要明确指定数组的长度。
#include <iostream>
using namespace std;
// 传递数组的引用,必须指定数组长度
void print_array_ref(int (&arr)[5]) {
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
int nums[5] = {1, 2, 3, 4, 5};
print_array_ref(nums);
// 以下调用会编译报错,因为数组长度不匹配
// int nums2[3] = {1,2,3};
// print_array_ref(nums2);
return 0;
}
数组退化的本质原因
数组退化指的是当数组作为函数参数传递,或者参与表达式运算时,会自动转换为指向数组首元素的指针,丢失数组的长度信息。这个现象的原因是C++为了兼容C语言的特性,同时避免传递大数组时产生大量的内存拷贝开销,所以设计了数组到指针的隐式转换规则。
当数组名出现在表达式(除了作为<sizeof>操作符的操作数、作为取地址符<&>的操作数、作为字符串字面量初始化字符数组的场景)中时,数组名会被自动转换为指向首元素的指针。比如下面的代码可以验证这个特性:
#include <iostream>
using namespace std;
int main() {
int nums[5] = {1,2,3,4,5};
// sizeof作用于数组名时,得到的是整个数组的大小
cout << "数组总大小: " << sizeof(nums) << endl; // 输出20(假设int为4字节)
// 数组名参与赋值表达式,退化为指针
int* p = nums;
cout << "指针大小: " << sizeof(p) << endl; // 输出8(64位系统指针大小)
// 数组作为函数参数传递时,sizeof得到的是指针大小
return 0;
}
数组参数和指针参数的区别与联系
很多人会疑惑函数参数声明为int arr[]和int* arr有什么区别,实际上在C++的函数参数声明中,这两种写法是完全等价的,编译器会将前者自动调整为后者。我们可以通过下面的代码验证:
#include <iostream>
using namespace std;
// 以下两个函数声明是等价的,无法同时存在于同一个作用域
// void func(int arr[]);
// void func(int* arr);
void func(int arr[]) {
cout << "arr是指针,大小: " << sizeof(arr) << endl;
}
int main() {
int nums[5] = {1,2,3,4,5};
func(nums);
return 0;
}
两者的核心差异仅存在于声明层面,实际编译后的函数签名完全一致。如果需要区分参数到底是数组还是指针,只能通过传递引用的方式,因为数组引用和指针是完全不同的类型。
不同传递方式的适用场景
- 如果不需要知道数组的长度,只需要遍历数组元素,使用指针参数或者数组形式的参数即可,同时额外传递长度参数。
- 如果需要保证数组的长度固定,避免传入错误长度的数组,可以使用数组引用的方式传递参数,在编译阶段就检查数组长度是否匹配。
- 如果需要修改原数组的内容,以上三种方式都可以实现,因为传递的都是数组首元素的地址,修改元素会影响原数组。如果不需要修改数组内容,可以在参数前加<const>修饰,比如
void func(const int* arr, int len)。
注意:不要试图在函数内部通过sizeof(arr)/sizeof(arr[0])的方式计算数组长度,因为此时arr已经退化为指针,这个计算得到的只是指针大小和元素大小的比值,和数组实际长度无关。