先給出問題:
像下面這樣的數組,在函數中如何傳參?也就是說如何保證虛參與實參類型一致。
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 );
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[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了,實際上我做圖象處理的時候全部這樣做,因爲這樣的地址訪問很明瞭,不會給閱讀帶來不便,而且作爲大部分的時候,我們用矩陣比較多,列數不等的情況很少。這只是個人見解。