參數傳遞二維數組


轉自http://blog.csdn.net/yunyun1886358/article/details/5659851

  • 數組名作為形參
 1 void func1(int iArray[][10])
 2 {
 3 
 4 }
 5 
 6 
 7 
 8 int main()
 9 {
10     int array[10][10];
11     func1(array);
12 
13 }

編譯通過,注意形參聲明一定要給出第二個維度的大小,要不編譯不過。

  • 一維數組指針作為形參 
  1.  1 void func2(int (*pArray)[10])  
     2 {  
     3   
     4 }  
     5   
     6 void func2_1(int (*pArray)[]) //編譯通過,無法調用  
     7 {  
     8   
     9 }  
    10   
    11 int main()  
    12 {  
    13     int array[10][10];  
    14     func2(array);  
    15 }  

     

其實二維數組名就是一個指向一維數組的指針,所以這種聲明方式OK。必須指定一維數組的長度,如果沒有指定的話,函數聲明編譯通過。但是如果一旦有調用代碼,就有編譯不通過,因為沒有實參類型能匹配int[].

  • 二維數組引用作為形參

 

 1 void func3(int (&pArray)[10][10])  
 2 {  
 3   
 4 }  
 5   
 6 int main()  
 7 {  
 8     int array[10][10];  
 9     func3(array);  
10 }  

 

 

必須指定兩個維度的長度。

  • 二維數組指針作為形參

 

 1 void func4(int (*pArray)[10][10])  
 2 {  
 3   
 4 }  
 5   
 6 int main()  
 7 {  
 8     int array[10][10];  
 9     func4(&array);  
10   
11 }  

 

 

必須指定兩個維度的長度。

 

以上方法都可以等價使用,對數組來說,沒有值傳遞。

      還不滿足,還有其他的聲明方式嗎?回到本文開始提到的問題:可以用雙重指針int**作為形參,接受二維數組實參嗎?答案是肯定的,但是又局限性。用雙重指針作為形參,那么相應的實參也要是一個雙重指針。事實上,這個雙重指針其實指向一個元素是指針的數組,雙重指針的聲明方式,很適合傳遞動態創建的二維數組。怎么動態創建一個二維數組?如下程序:

 

1 int main()  
2 {  
3     int m = 10;  
4     int n = 10;  
5     int** p = new int[m][n];  
6 }  

 

 

會發現編譯不通過,第二個維度長度必須為常量。那么怎么聲明一個兩個維度都能動態指定的二維數組呢?看下面:

 

 1 void func5(int** pArray, int m, int n)  
 2 {  
 3   
 4 }  
 5   
 6 #include <ctime>  
 7 int main()  
 8 {  
 9     int m = 10;  
10     int n = 10;  
11   
12     int** pArray = new int* [m];  
13     pArray[0] = new int[m * n]; // 分配連續內存  
14   
15     // 用pArray[1][0]無法尋址,還需指定下標尋址方式  
16     for(int i = 1; i < m; i++)  
17     {  
18         pArray[i] = pArray[i-1] + n;  
19     }  
20   
21     func5(pArray, m, n);  
22 }  

這里為二維數組申請了一段連續的內存,然后給每一個元素指定尋址方式(也可以為每一個元素分別申請內存,就不必指定尋址方式了),最后將雙重指針作為實參傳遞給func5。這里func5多了兩個形參,是二維數組的維度,也可以不聲明這兩個形參,但是為了安全嘛,還是指定的好。最后編譯,運行,一切OK。總結一下,上面的代碼其實是實現了參數傳遞動態創建的二維數組。

 

二、轉自:http://www.cnblogs.com/wuyuegb2312/archive/2013/06/14/3135277.html

1.基本形式:二維數組在棧上分配,各行地址空間連續,函數參數使用文首提到的3種形式

  最初接觸二維數組時,可能只是在main()或某個函數里進行聲明,然后直接使用:

1 ...
2     int array[M][N];
3 
4   //array[][N] ={{...},...,{...}}; is ok
5   //array[][] = {{...},...,{...}}; is wrong
6 
7 
8    ...
9    //使用array[m][n]

 這種分配是在棧上進行的,能夠保證所有元素的地址空間連續。這樣,array[i][j] 和 *(*(array +i) +j)是一樣的,程序是知道array+i的i實際上偏移了i*N個單位,這也導致了在二維數組array[3][3]中,使用下標array[2][1]和array[1][4]是訪問的同一個元素,盡管后者的下標對於一個3*3矩陣來說是非法的,但這並不影響訪問。

  這種形式,無論是數組定義還是函數都不夠泛用,兩個維度在編譯前就定好了,唯一可以做的就是把維度M、N聲明為宏或者枚舉類型,但這仍不能避免每次修改后都要重新編譯。


 

2.數組傳參形式:二維數組在棧上分配,各行地址空間連續,函數參數使用指針形式

  當把這種二維數組的指針直接作為參數傳遞時,數組名退化為指針,函數並不知道數組的列數,N對它來說是不可見的,即使使用*(*(array +i) +j),第一層解引用失敗。這時,編譯器會報warning,運行生成的文件會發生segment fault。那么,為了指導這個函數如何解引用,也就是人為地解引用,需要把這個二維數組的首元素地址傳給函數,於是就變成了下面的形式:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <assert.h>
 4 int func(int *array, int m, int n) {
 5     int i,j;
 6     for(i=0;i<m;i++) {
 7         for(j=0;j<n;j++)
 8             printf("\t%d", *(array +i*n +j));
 9         printf("\n");
10     }
11     return 0;
12 }
13 
14 int main(int argc,char** argv) {
15     int m=3,n=3,i;
16     int array[][3] = {{1,2,3},{4,5,6},{7,8,9}};
17     func(*array,m,n);
18     return 0;
19 }

你可能會問,為什么下面的不行?原因其實和上面提到的一樣,第一次解引用時,函數並不知道數組的列數,從而導致失敗。准確的說,是因為數組實際類型是int [3][3],在作為右值時可以被轉化為int (*)[3],它們都和int **不同,自然不可用。

 1 int func(int **array, int m, int n) {
 2     ...
 3     printf("\t%d", *(*array +i*n +j));
 4     ...
 5 }
 6 
 7 int main() {
 8   int array[3][3] = {
 9         {1,2,3},
10         {4,5,6},
11         {7,8,9}
12     };
13     ... 
14     func(array,3,3);
15   ... 
16 }

3.動態數組形式:二維數組在堆上分配,各行地址空間不一定連續,函數參數使用指針形式

  第2種雖然函數參數的限定降低了,但仍需要在棧上預先分配一定大小的二維數組,程序整體並不是完全的泛用。為了進一步提高泛用性,把二維數組空間的分配也動態化,使用malloc()在堆上分配空間,重復一下前言中的方式如下:

1 int **array;
2 array = (int **)malloc(m *sizeof(int *));
3 for(i=0;i<M;i++)
4     array[i] = (int *)malloc(n *sizeof(int));

這時,在分配空間的作用域里,對0<=i<M,0<=j<N,array[i][j]的訪問完全沒有問題。那么,對應地,函數寫作

1 int func(int **array,int m,int n) {
2     ...
3     printf("%d ", *(*(array+i)+j));
4     ...
5 }

值得注意的是,雖然malloc()每次分配的空間在地址上是連續的,但是多次malloc()分配的空間之間並不一定是連續的,這與在棧上分配的二維矩陣有着根本的不同,對於二維數組array[3][3],不能再用array[1][4]來訪問array[2][1]了,前者地址越界。


 

4.折中形式:用堆上分配的一維數組表示二維數組,函數參數使用指針形式

  用一維數組來實現二維數組,是一種折中方案,但是很好理解,也不易出錯。這樣分配的數組空間是連續的。使用時需要把兩維下標轉化為一維下標。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <assert.h>
 4 int func(int *array, int m, int n) {
 5     int i,j;
 6     for(i=0;i<m;i++) {
 7         for(j=0;j<n;j++)
 8             printf("\t%d",*(array+i*n+j));
 9         printf("\n");
10     }
11     return 0;
12 }
13 
14 int main(int argc,char** argv) {
15     int m,n,i;
16     int *array;
17     assert(argc == 3);
18     m = atoi(argv[1]);
19     n = atoi(argv[2]);
20     array = (int*)malloc(m*n*sizeof(int));
21     for(i=0;i<m*n;i++)
22         array[i] = i;
23     func(array,m,n);
24     return 0;
25 }

5.較新的編譯器:用棧上分配的直到執行時才確定大小的二維數組

  C90不支持這種形式,C99支持,因此一些較新的編譯器可以對下面的代碼進行執行。注意print()的參數順序不能改變。

 1 void print(int x, int y, int a[x][y]){
 2     printf("\n");
 3     int i, j;
 4     for(i = 0; i < x; i++){
 5         for(j = 0; j < y; j++)
 6             printf("%d     ", a[i][j]);
 7         printf("\n");
 8     }
 9 }
10 
11 // Function to initialize the two-dimensional array
12 void init_2d(int *a, int x, int y){
13     int i, j;
14     for(i = 0; i < x; i++){
15         for(j = 0; j < y; j++){
16             a[i*y + j] = i + j;
17         }
18         printf("\n");
19     }
20 }
21 
22 int main(){
23     int m , n ;
24     scanf("%d %d",&m,&n);
25     int a[m][n];  // a two dimensional whose size has been defined using variables
26     init_2d(a, m, n);
27     print(m, n, a);
28 }

這段代碼出自http://stackoverflow.com/questions/17181577/two-dimensional-arrays-in-c

  (2013.7.28更新)

   另外,這種分配方式仍然是在棧上,相關討論可見於http://bbs.csdn.net/topics/90350681

  

小結

  • 其實所謂的二維數組,在K&R上只是指預先分配好大小的形如int a[M][M]這樣的數組,它存在於棧上;而實際使用的在堆空間利用malloc動態分配空間的並不是這種,只是用的人多了,把后者叫成二維數組了(我不認為把后者也稱為二維數組是標准的說法)。再加上我們經常用它來處理矩陣,“標准的”二維數組、“動態的”“二維數組”、矩陣這三個概念就混在了一起。矩陣是可以用這兩種二維數組表示的,而對於這兩種不同的二維數組,函數傳參的方式也不完全相同,不能隨意混用。
  • C99對於多維數組的描述:

    If E is an n -dimensional array ( n ≥ 2) with dimensions i × j × ... × k , then E (used as other than an lvalue) is converted to a pointer to an ( n − 1)-dimensional array with dimensions j × ... × k . If the unary * operator is applied to this pointer explicitly, or implicitly as a result of subscripting, the result is the pointed-to ( n − 1)-dimensional array which itself is converted into a pointer if used as other than an lvalue. It follows from this
    that arrays are stored in row-major order (last subscript varies fastest).

  • 棧上分配的二維數組數組名int array[3][3]的真實類型是int [ ][ ],在作為右值時才被轉化為(int *array)[N] (感謝 garbageMan指出),和int **是不同的。把前者進行強制轉換為后者,在函數中對元素操作也會導致段錯誤,下面用圖來說明二者區別:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM