一维数组
在C语言中,向函数传递参数只有一种形式——值传递。不管是传递一个整型变量,还是传递一个指针,函数得到的都只是实参的拷贝值。
先定义一个一维整型数组:
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
首先明确一点:数组名和指针是两个不同的概念,尽管数组名在绝大部分情况下被隐式转换成指针使用。
我们没办法真正地把数组传递给函数,我们只能传递一份指向数组起始位置的指针的拷贝。
函数原型:
void func(int *a);
void func(int a[]);
在当前这个上下文环境中,二者等价。你可以使用任何一种声明,如果要说哪一种最准确,应该是第一种。因为实参实际上是个指针,而不是数组。
这样我们就把指向数组首元素的指针的拷贝传递给了函数。在实际应用中,我们通常还会传递另一个参数,用来表示数组的长度:
void func(int *a, int len);
通过第二个参数,我们可以很方便地在函数内遍历这个数组:
void func(int *a, int len) {
int i = 0;
while (i < len) {
printf("%d ", a[i]);
i++;
}
}
func(a, 10); // 打印数组a的所有元素
二维数组
接下来是二维数组,传递二维数组参数有更多需要注意的点。
先定义一个二维整型数组:
int b[2][3] = {
{0, 1, 2},
{3, 4, 5}
};
我们实际上要向数组传递一个指向数组首元素的指针的拷贝,首先必须搞清楚数组名b
的类型应该是:
int (*)[3]
数组名的类型取决于数组元素,数组b
长度是2,每个元素是长度为3的整型数组,所以b
的类型是上面那坨(如果这里有疑问可以看我的另一篇文章《彻底弄懂C语言数组名》)。
如果上面的内容没有疑问,那么就来看函数原型:
void func2(int (*p)[3]); // 两种都可以,第一种更准确
void func2(int p[][3]);
同样地,我们可以传递另外两个参数,分别表示行数和列数:
void func2(int (*p)[3], int len_i, int len_j);
但是请千万注意:写成二级指针是错误的!
void func2(int **p); // 不可以这样~>_<~
为什么不行呢?因为指向指针的指针和指向数组的指针是不同的。如果这样写,有可能你的程序能正确运行,但也有可能因为寻址错误导致程序崩溃哦!
下面给出完整的例子:
#include <stdio.h>
void func2(int (*p)[3], int len_i, int len_j);
int main() {
int a[2][3] = {
{0, 1, 2},
{3, 4, 5}
};
func2(a, 2, 3);
return 0;
}
void func2(int (*p)[3], int len_i, int len_j) {
int i, j;
for (i = 0; i < len_i; i++) {
for (j = 0; j < len_j; j++) {
printf("%d ", p[i][j]);
}
printf("\n");
}
}
但是,不知道你有没有发现一个问题,我们必须在函数原型中指定第二维的大小,这意味着函数不能复用。为了向函数传递一个大小未知的数组,我将介绍两种技巧。
第一种是通过void *
指针接收一个数组,同时传递两个参数标记行和列,然后在函数内部将void *
指针强制转换成二维数组。
还有一种技巧叫做压扁数组(flattening the array),是通过一级指针遍历二维数组的方法。只要数组在内存中连续,这个技巧就可以用。我将在下一篇文章中详细介绍这个技巧。