您的位置:首页 > 其它

二维数组参数传递 指针降级

2013-11-20 23:40 543 查看
先给出问题:

像下面这样的数组,在函数中如何传参?也就是说如何保证虚参与实参类型一致。

char str_arr[3][10] = {"yes","no","uncertain"};

char *str_array[] = {"yes","no","unsure"};

函数原型:

               void func1( char (*a)[10] )

               void func2( char **a )

调用:

              func1( str_arr );

              func2( str_array);

如果向func2()中传入str_arr会怎么样呢?编译器会警告:传递参数 1 (属于 ‘func2’)时在不兼容的指针类型间转换。即虚参与实参类型不一致。

同理,也不能向func1()中传入str_array。

我们给出完整的测试程序:

/********二维数组传参测试程序***************/

#include <stdio.h>

void func1( char (*a)[10])

{

        int i;

        for(i=0;i<3;i++)

                printf("%s/n",a[i]);

}

void func2( char **a )

{

        int i;

        for(i=0;i<3;i++)

                printf("%s/n",*(a+i));

}

int main()

{

        char str_arr[3][10] = {"yes","no","uncertain"};

        char *str_array[] = {"yes","no","unsure"};

        char *str[3] = {"a","b","c"};/*这两种表达效果一样*/

        func1(str_arr);

        func2(str_array);    

        return 0;

}

/******************end*******************/

运行结果:

[root@localhost ansi_c]# gcc test.c 

[root@localhost ansi_c]# ./a.out 

yes

no

uncertain

yes

no

unsure

[root@localhost ansi_c]# 

如果将

        func1(str_arr);

        func2(str_array); 

改成:

        func1(str_array);

        func2(str_arr); 

会怎么呢?

[root@localhost ansi_c]# gcc test.c 

test.c: 在函数 ‘main’ 中:

test.c:22: 警告:传递参数 1 (属于 ‘func1’)时在不兼容的指针类型间转换

test.c:23: 警告:传递参数 1 (属于 ‘func2’)时在不兼容的指针类型间转换

这两种数组的正确赋值应该如下:

        char str_arr[3][10] = {"yes","no","uncertain"};

        char *str_array[] = {"yes","no","unsure"};

        char (*pa)[10] = str_arr;

        char **p = str_array;

pa和p才是和他们相一致的类型。

        

当然,如果不是传参的话,在main()函数中就不会发生这么多烦恼了。

/*************非传参时的情况************************/

#include <stdio.h>

int main()

{

        char str_arr[3][10] = {"yes","no","uncertain"};

        char *str_array[] = {"yes","no","unsure"};

        char *str[3] = {"a","b","c"};

        int i;

        for(i=0;i<3;i++)

              printf("%s/n",str_arr[i]);

        for(i=0;i<3;i++)

              printf("%s/n",str_array[i]);

         return 0;

}

/*************************************/

运行结果:

[root@localhost ansi_c]# gcc test1.c 

[root@localhost ansi_c]# ./a.out 

yes

no

uncertain

yes

no

unsure

[root@localhost ansi_c]# 

这说明了一点,在没传参之前,main()函数清楚它们都是二维数组。对于上面给出的两种函数原型:

函数原型:

               void func1( char (*a)[10] )

               void func2( char **a )

这两种传参方法有什么不同呢?这们对实参有什么要求呢?

上面只是抛出了一个问题,我在这里的主题是想搞清楚二维数组传参有什么奥秘,而非只针对这一个问题提出解决方法。

后面从基础的开始讨论。

我们先看看教材上怎么讲这一块的,

谭浩强的《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 array[3][4],要传参的话,函数原型可以为下面三种的任一种:

      void func(int a[3][4])

     void func(int a[][4])

     void func(int (*a)[4])

调用时为:func(array);

    同时教材里也说了,如果在型参里不说明列数,则编译器无法决定应为多少行多少列。那么能不能把

int array[3][4]的数组名 array 传给 void func(int **a)呢?

看下面:

/**********************************/

#include <stdio.h>

int main()

{

int array[3][4];

int **p = array;

}

**********************************/

root@localhost ansi_c]# gcc test2.c 

test2.c: 在函数 ‘main’ 中:

test2.c:5: 警告:从不兼容的指针类型初始化

[root@localhost ansi_c]# 

虽然从本质上讲int array[3][4] 的数组名相当于二级指针,但它不等同于一般的二级指针,因为它还含有数组相关的信息,所以在main函数中:

char str_arr[3][10] = {"yes","no","uncertain"};

for(i=0;i<3;i++)

              printf( "%s/n",str_arr+i );

它可以通过下标,每次跳过10个字节来寻址。我们再看看编译器是怎样处理数组的:

对于数组 int p[m]
;

       如果要取p[i][j]的值(i>=0 && i<m && 0<=j && j < n),

编译器是这样寻址的:

       p + i*n + j;

我们再看一个例子:

/*********************二维数组传参*****************************/

#include <stdio.h>

void fun( int *a, int m, int n)

{

        int i,j;

        for( i=0; i<m; ++i)

        {

                for(j=0;j<n;++j)

                {

                        printf("%d ", *( a+i*n+j ) );

                }

                putchar('/n');

        }

}

void func( int *a, int m, int n)

{

        int i,j;

        for( i=0;i<m*n;++i)

        {

                        printf("%d ",a[i]);

        }

        putchar('/n');

}

int main()

{

     int a[3][3] = 

    {

      {1, 1, 1},

      {2, 2, 2},

      {3, 3, 3}

    };

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

        func( &a[0][0],3,3);

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

        return 0;

}

********************end******************************/

[root@localhost ansi_c]# gcc test4.c 

[root@localhost ansi_c]# ./a.out 

1 1 1 

2 2 2 

3 3 3 

1 1 1 2 2 2 3 3 3 

1 1 1 2 2 2 3 3 3 

[root@localhost ansi_c]# 

我们来看其中的要点,

数组为:

int a[3][3] = 

    {

      {1, 1, 1},

      {2, 2, 2},

      {3, 3, 3}

    };

函数原型和调用为:

原型:

void fun( int *a, int m, int n)  

{

.............

         printf("%d ", *( a+i*n+j ) );

.............

}    

调用:

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

另一个函数为:

原型:

void func( int *a, int m, int n)

{

.............

         printf("%d ",a[i]);

.............

}    

调用:

    func( &a[0][0],3,3);

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

我们发现这两种方式都能正常执行,我们把一个二级指针,强制转换成了一级指针传了进去,并在函数中模仿编译器数组的寻址方式:*( a+i*n+j )。

我们再看看二维字符数组的例子:

/*******************二维字符数组*******************************/

#include <stdio.h>

void f( char **a, int n)

{

        int i;

        printf("%c/n",*( (char*)a+0 ) );

        printf("%c/n",((char * )a)
);

        puts("------------OK");

        for(i=0;i<3;i++)

                printf("%s/n",(char*)a+i*n );

        }

int main()

{

        char str_arr[3][10] = {"yes","no","uncertain"};

        f( (char **)str_arr, 10);

        return 0;

}

/****************end*************************/

运行结果:

[root@localhost ansi_c]# ./a.out 

y

n

------------OK

yes

no

uncertain

[root@localhost ansi_c]# 

这里也做了强制类型转换,转换成字符指针,

printf("%s/n",(char*)a+i*n ); 每个字符串的地址就是数组中字符'y'、'n'、'u'的地址,

printf("%c/n",*( (char*)a+0 ) );字符在数组中的排列是顺序的,可以用 *( (char*)a+i )或 ((char * )a)[i] 表示。

当然这个程序也可以改成这样,完全不用二级指针:

/*****************************************************************/

#include <stdio.h>

void f( char *a, int n)

{

        int i;

        printf("%c/n",*( a+0 ) );

        printf("%c/n",(a)
);

        puts("------------OK");

        for(i=0;i<3;i++)

                printf("%s/n",a+i*n );

        }

int main()

{

        char str_arr[3][10] = {"yes","no","uncertain"};

        f( (char *)str_arr, 10);

        return 0;

}

/*****************************************************************/

归根结底,还是把它转成一级指针来用。

下面做个小结:

数组传参

数组:

        int array[4][10];

函数原型:

         void func1( int a[][10] );

        void func2( int (*a)[10] );

        void func3( int *p, int col, int row );

函数调用:

        func1( array );

        func2( array );

         func3( (int *)array, 4, 10 );

容易出错的地方:

int arr[][10];

int **p = arr;

这种方式是错误的. 

应该是

int (*p)[10] = arr;

同理,也不能将arr传给fun( int **p)

另外数组传参之后会降级为指针,如:

#include <stdio.h>

void Test(char a[][2])

{

     int size = sizeof( a );//4

}

int main(void)

{

      char a[3][2] = {'a','b','c','d','e','f'};

      int size =sizeof( a );//6

     Test( a );

     return 0;

}

来源:http://hi.baidu.com/d_life/blog/item/912062ef1fd363e9ce1b3e2a.html

 

函数原型:要求传入一个动态二维数组

void func1(int **p,int row, int column)

{

}

调用:

int main()

{

int m,n;

int **b;

cin >> m;

cin >> n;

b = new int *[m];

for(int i=0; i<m; i++)

{

  b[i] = new int
;

};

func1(b,m,n);

return 0;

}

我习惯的做法是不用指针数组,定义一个大块(这种情况主要面向每行行数相同):比如现在有一个W*H的矩阵(H个长度为W的数组),你就直接定义一个float型指针:   float* pfBuffer;然后动态分配大小 pfBuffer = new float[W*H];这个buffer在用完之后要调用 delete pfBuffer;来释放.你传递这个float指针,传递行列数之后,你如果要访问y行x列的话,只要算一下它在哪儿, int addr = y*W+x;就是其"地址"了,你要访问它,直接使用pfBuffer[addr]就OK了,实际上我做图象处理的时候全部这样做,因为这样的地址访问很明了,不会给阅读带来不便,而且作为大部分的时候,我们用矩阵比较多,列数不等的情况很少。这只是个人见解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: