C和C++中对数组的处理大致是相同的,C++多了引用的处理方式。
数组的两个特殊性质决定了我们在函数传递时的基本处理。一是不允许被拷贝,二是数组的操作大多时候可以转化为指针操作。不允许被拷贝是指,初始化的时候不允许用一个数组初始化另一个数组,也不能将一个数组名等于另一个数组名进行整个数组的传递。指针操作是因为数组的首地址本身就是指针概念,一维数组对应一级指针,二维数组对应二级指针,或者说指向一个一维数组的一级指针。
当我们向一个函数传递数组的时候实际上传递的是指向首地址的指针。尽管如此,形式上还是可以有多种形式:
void print(const int*);
void print(const int[]);
void print(const int[10]);
对于编译器来说,这三种定义完全等价,都是对应于const int*的参数类型,也就是一个指向整型常量的指针。而且传递数组的长度并不受形参定义的影响。例如:
int i = 0;
int j[2] = { 0, 1 };
print(&i);
print(j);
这两个函数调用都是完全合法的。
使用数组的最大问题还是在于不能越界,这点在函数传递的时候也是一样。传递过程中,我们没法知道数组的长度,对于这个长度的处理一般有三种方法。
1. 使用特定的标记指定数组长度
这种方法的主要实例时C风格的字符串,也就是字符串最后默认有一个空字符’\0’。所以可以通过检测这个来判断是否越界,但对于int之类的类型就不起作用了。
或者在数组最后加上人为的特殊标记也是可以得。
void print (const char *cp)
{
if (cp) {
while (*cp) {
cout << *cp++;
}
}
}
- 使用标准库规范
也就是传递数组的首元素地址和末尾元素地址。
void print (const int *beg, const int *end)
{
while (beg != end) {
cout << *beg++ << endl;
}
}
- 显示传递一个表示数组大小的变量
这里需要专门定义一个表示数组大小的形参。这在C程序和过去的C++程序中常常使用。
void print (const int ia[], size_t size)
{
for (size_t i = 0; i != size; i++) {
cout << ia[i] << endl;
}
}
不难发现,在上面的代码中我们都把数组形参定义为指向const的指针,这样相对安全,在只需要读取的操作里不会影响原数组的值,除非我们需要对数组进行改变,并带回原作用域才定义为非const。
当然,数组的传递也可以用引用变量。此时数组的维度就是数据类型的一部分。例如:
void print (int (&arr) [10])
{
for (auto elem : arr) {
cout << elem << endl;
}
}
这里注意,类似指针数组和数组指针,这里也有引用数组和数组引用两种区分,如下:
int &arr[10]; //数组内容是10个引用变量
int (&arr)[10]; //一个指向长度为10数组的引用变量
这种引用传递一定程度上限制函数的可用性,因为传递的参数必须是长度为10的一维数组。长度不对不可以。
传递多维数组,其实C或者C++中没有真正意义上的多维数组,多维数组无非是数组的数组。常见的参数形式如下:
void print(int (*matrix)[10], int rowsize);
void print(int matrix[][10], int rowsize);
上述定义等价。