这篇文章上次修改于 640 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

前言

各位在写c程序的时候,经常会碰到要给一个函数传递数组的情况,最经典的可能是排序函数。

一维数组的传参很简单

void abc(int array[]);//声明
int main()
{
    int array[10];
    abc(array);//引用
}

下来我们来说说二维数组
先来一个错误示范

void foo(int a[][],int m,int n) 
{
    for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
            printf("%d\n",a[i][j]);
}
int main() {
    int a[2][3] = {
        {1,2,3},
        {4,5,6}
    };
    foo(a,2,3);
}

我猜大家第一次二维数组传参的防范肯定是这样
而编译器肯定会报错

test.c:3:14: error: array type has incomplete element type ‘int[]’
 void foo(int a[][], int m, int n) {
              ^
test.c: In function ‘main’:
test.c:12:9: error: type of formal parameter 1 is incomplete
     foo(a, 2, 3);

报错在处理二维数组参数a的时候,类型不兼容。

C语言里面对二维数组的存储是按照一维数组来处理的,二维数组按照行展开的方式按顺序存储

一维数组 int b[6]={0,1,2,3,4,5};

二维数组 int a2={{0,1,2},{3,4,5}};

他们的空间都是连续的

a: | 0 | 1 | 2 | 3 | 4 | 5 
b: | 0 | 1 | 2 | 3 | 4 | 5 

所以利用二维数组进行传参
必须告诉编译器二维数组的列数 否则无法对二维数组进行处理

将函数声明改为void foo(int a[][3],int m,int n) 时
可以正常编译

三种方法用二维数组进行传参

一、二维数组的列数是个常数

1.

void foo(int a[][3], int m, int n)
{
    for (int i = 0; i < m; i++)
        for (int j = 0; j<n; j++)
            printf("%d\n", a[i][j]);
}
int main()
{
    int a[2][3] = {
        {0, 1, 2},
        {3, 4, 5}};
    foo(a, 2, 3);
}

2.

void foo(int (*a)[3],int m,int n) 
{
    for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
            printf("%d\n",a[i][j]);
}
int main() {
    int a[2][3] = {
        {1,2,3},
        {4,5,6}
    };
    foo(a,2,3);
}

将参数a声明为一个指向数组的指针

声明(*a)是一个数组,等价于int b[3]时,b是一个数组。

又因为本来a就是一个指针,指向一个数组,类似a是指针,指向b的。
注意:此时仍需要指明列长度

二、二维数组的列是一个变量

3.c

显然,如果二维数组的列是一个变量的话,我们没有办法用上面的两种方法进行传参并处理

此时我们不能把数组继续当一个二维数组,而是理解为传进去一个地址,对地址进行操作来达到对二维数组的地址进行抽象的操作

这里引入一个等价转换式

a[i][j]= *((int*)a+lie*i+j)

根据我之前解释的以为数组和二维数组在程序里都是一块连续的空间

如图

一维数组 int b[6]={0,1,2,3,4,5};

二维数组 int a2={{0,1,2},{3,4,5}};

a: | 0 | 1 | 2 | 3 | 4 | 5 
b: | 0 | 1 | 2 | 3 | 4 | 5 

b[4]==a1

4=1*3+1
此时的3为二维数组的列

因此二位数组的任意元素可以解释为

a[i][j]= *((int*)a+lie*i+j)

下面提供实现代码

void foo(int **a,int lie,int m,int n) 
{
    int lie=n;
    for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
            printf("%d\n",*((int*)a+lie*i+j));
}
int main() {
    int a[2][3] = {
        {1,2,3},
        {4,5,6}
    };
    foo(a,2,3);
}

不管怎么样,ai不被允许。也是由编译器的寻址方式决定。

对指针的操作不止上面的一种,如下还有集中参考形式可以供读者进行自由的操作和表达,本文只是抛砖引玉,给大家提供思考的余地,也是笔者一种笔记和备忘

1.*( a[i] + j)  //代表第 i 行 第 j 列

1.  *( *(a+i) + j) //同上

2. *( (int *) a + i*n + j )//同上,n表示第二维数组长度,即列宽