C语言中如何将二维数组作为函数的参数传递

今天写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单里,但是最后我也解决了遇到的问题,所以这篇文章主要介绍如何处理二维数组当作参数传递的情况,希望大家不至于再在这上面浪费时间。

正文:

首先,我引用了谭浩强先生编著的《C程序设计》上面的一节原文,它简要介绍了如何

将二维数组作为参数传递,原文如下(略有改变,请原谅):

[原文开始]

可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以指定所有维数的大小,也可以省略第一维的大小说明,如:

 void Func(int array[3][10]);
 void Func(int array[][10]);

二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是不合法的:

 void Func(int array[][]);

因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多 少列,不能只指定一维而不指定第二维,下面写法是错误的:

 void Func(int array[3][]);实参数组维数可以大于形参数组。

例如实参数组定义为:

 void Func(int array[3][10]);

而形参数组定义为:

 int array[5][10];

这时形参数组只取实参数组的一部分,其余部分不起作用。

[原文结束]

大家可以看到,将二维数组当作参数的时候,必须指明所有维数大小或者省略第一维的,但是不能省略第二维或者更高维的大小,这是由编译器原理限制的。大家在学编译原理这么课程的时候知道编译器是这样处理数组的:

   对于数组 int p[m][n];

   如果要取p[i][j]的值(i>=0 && i<m && 0<=j && j < n),编译器是这样寻址的,它的地址为:

   p + i*n + j;

从以上可以看出,如果我们省略了第二维或者更高维的大小,编译器将不知道如何正确的寻址。但是我们在编写程序的时候却需要用到各个维数都不固定的二维数组 作为参数,这就难办了,编译器不能识别阿,怎么办呢?不要着急,编译器虽然不能识别,但是我们完全可以不把它当作一个二维数组,而是把它当作一个普通的指 针,再另外加上两个参数指明各个维数,然后我们为二维数组手工寻址,这样就达到了将二维数组作为函数的参数传递的目的,根据这个思想,我们可以把维数固定 的参数变为维数随即的参数,例如:

 void Func(int array[3][10]);

 void Func(int array[][10]);

变为:

 void Func(int **array, int m, int n);

在转变后的函数中,array[i][j]这样的式子是不对的(不信,大家可以试一下),因为编译器不能正确的为它寻址,所以我们需要模仿编译器的行为把array[i][j]这样的式子手工转变为:

 *((int*)array + n*i + j);

 在调用这样的函数的时候,需要注意一下,如下面的例子:

 int a[3][3] =

 {

   {1, 1, 1},

   {2, 2, 2},

   {3, 3, 3}

 };

 Func(a, 3, 3);

根据不同编译器不同的设置,可能出现warning 或者error,可以进行强制转换如下调用:

 Func((int**)a, 3, 3);

其实多维数组和二维数组原理是一样的,大家可以自己扩充的多维数组,这里不再赘述。写到这里,我先向看了这篇文章后悔的人道歉,浪费你的时间了。下面是一 个完整的例子程序,这个例子程序的主要功能是求一个图中某个顶点到其他顶点的最短路经,图是以邻接矩阵的形式存放的(也就是一个二维数组),其实这个函数 也是挺有用的,但是我们这篇文章的重点在于将二维数组作为函数的参数传递。

/

C语言中函数间传递二维数组的方法

方法一, 形参给出第二维的长度。

例如:

#include <stdio.h>
void func(int n, char   str[ ][5] )
{undefined
int i;
for(i = 0; i < n; i++)
   printf("\nstr[%d] = %s\n", i, str[i]);
}

void main()
{undefined
char* p[3];
char str[][5] = {"abc","def","ghi"};
func(3, str);
}

方法二,形参声明为指向数组的指针。

例如:

#include <stdio.h>
void func(int n, char  (*str)[5] )
{undefined
int i;
for(i = 0; i < n; i++)
   printf("\nstr[%d] = %s\n", i, str[i]);
}

void main()
{undefined
char* p[3];
char str[][5] = {"abc","def","ghi"};
func(3, str);
}

方法三,形参声明为指针的指针。

例如:

#include <stdio.h>
void func(int n, char **str)
{undefined
int i;
for(i = 0; i < n; i++)
   printf("\nstr[%d] = %s\n", i, str[i]);
}
void main()
{undefined
char* p[3];
char str[][5] = {"abc","def","ghi"};
p[0] = &str[0][0];
p[1] = str[1];
p[2] = str[2];
     func(3, p);
}

或者

#include <stdio.h>

void fun(int **ppN, int N1, int N2);

int main(void)
{

int **ppTmp;
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
ppTmp = (int **)a;
fun(ppTmp, 3, 4);
return 0;
}

void fun(int **ppN, int N1, int N2)
{
int i=0, j=0;
for(i=0; i<3; i++)
for(j=0; j<4; j++)
printf("ppTmp[%d][%d]:%d\n", i, j, *((int *)ppN+i*N2+j));
}  

补充

在上述种讲到二级指针指向二维数组是不可以通过array[i][j]方式来进行取值是没有问题的,那么是不是没有办法通过下标的方式来进行取值呢?也不是完全没有方法,这就需要对二级指针的概念有充分的理解。以下通过一个实例来进行讲解:

void func(unsigned int** tmp, unsigned int m, unsigned int n){
    //其中m是二维数组的行,n为列
    for (int i = 0; i < m; i++){
        for (int j = 0; j < n; j++)
        {
            printf("dd:%d\n", *((unsigned int*)tmp + i*n +j));
        }
    }
    printf("----------------------------------\n");
    
    printf("dd-1:%d\n", (unsigned int *)tmp[0]);
    printf("dd-2:%d\n", (unsigned int *)tmp[1]);

    printf("----------------------------------\n");

    printf("dd-1:%d\n", ((unsigned int *)tmp)[0]);
    printf("dd-1:%d\n", ((unsigned int *)tmp)[1]);
    printf("dd-1:%d\n", ((unsigned int *)tmp)[2]);
    printf("dd-1:%d\n", ((unsigned int *)tmp)[3]);
    printf("dd-1:%d\n", ((unsigned int *)tmp)[4]);
    printf("dd-1:%d\n", ((unsigned int *)tmp)[5]);

}

int main(int argc, char const *argv[])
{
    unsigned int tmp_array[2][3] = {
        {1, 2, 3},
        {4, 5, 6},
    };

    func((unsigned int**)tmp_array, 2, 3);

    return 0;
}

结果:

dd:1
dd:2
dd:3
dd:4
dd:5
dd:6
----------------------------------
dd-1:1
dd-2:3
----------------------------------
dd-1:1
dd-1:2
dd-1:3
dd-1:4
dd-1:5
dd-1:6

那么这是为什么呢:首先需要了解两个问题

1、指针什么时候可以用下标的方式可以寻址?

例如:

char  *p = "abcdefg";
printf("p[0]:%c\n", p[0]);
printf("p[1]:%c\n", p[1]);
printf("p[2]:%c\n", p[2]);
printf("p[3]:%c\n", p[3]);
printf("p[4]:%c\n", p[4]);

那么可以通过

p[0] : a
p[0] : b
p[0] : c
p[0] : d

也就是说,在通过二级指针使用下标时需要将其转换位置一级指针时候再通过下标进行寻址,
那么此时又会有人问,既然二级指针是指针的指针,那么将二级指针看作一级指针的嵌套的话不就可以通过下标寻址了么?其实二级指针最上层的指针指向的是一个指针,指针实际上是一个地址,那么地址的大小其实是8字节(64平台)或者4字节(32平台),这也就意味着最下层的指针会跳过中间的许多数值,见例子中的dd-1和dd-2

2、二级指针的两种寻址方式:

typedef struct {
    int a;
    int b;
}test;

void func1(test* tmp[]){
    printf("func1:%d\n", ((test*)tmp + 0)->a);
    printf("func1 ((test*)tmp)[0].c:%d\n", ((test*)tmp)[0].b);
    
    printf("func1:%d\n", ((test*)tmp + 1)->a);
    printf("func1 ((test*)tmp)[1].c:%d\n", ((test*)tmp)[1].b);
}
    
int main(int argc, char const *argv[])
{
    test aa[2];
    aa[0].a = 1;
    aa[0].b = 2;
    
    aa[1].a = 3;
    aa[1].b = 4;
    
    func1((test**)aa);
    return 0;
}

结果:

func1:1
func1 ((test*)tmp)[0].c:2
func1:3
func1 ((test*)tmp)[1].c:4

可以看出其中是通过两种方式进行寻址,具体原理不再进行介绍。

  • 28
    点赞
  • 97
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绛洞花主敏明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值