二維數組傳參問題


先給出問題:
像下面這樣的數組,在函數中如何傳參?也就是說如何保證虛參與實參類型一致。

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][n];
       如果要取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)[n] );
        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)[n] );
        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 );
}

函數原型:要求傳入一個動態二維數組
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[n];
};
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了,實際上我做圖象處理的時候全部這樣做,因為這樣的地址訪問很明了,不會給閱讀帶來不便,而且作為大部分的時候,我們用矩陣比較多,列數不等的情況很少。這只是個人見解。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: