二維數組作爲函數的參數

問題:已有二維數組a[n][m] (m,n爲常量整數)

int a[n][m];

如果我們要編寫一個函數對這個數組進行處理,我們應該怎樣聲明函數的形參,又怎樣在調用函數時引入二維數組這個參數呢?

  • 首先我們來看對一維數組是怎麼做的

void main()
{
    int vec[n];
    fun1(vec);    
}

對函數傳參時,直接把一維數組的名字作爲參數傳入。

在定義函數的形參時有兩種方式

void fun1(int *vec);

void fun1(int vec[]);

因爲這裏的vec同時也是一個指向整形的指針,指向的是數組第一位的元素,所以以上兩種形參都是符合要求的。

在fun1函數的實現裏面,可以通過*(vec+i)或者vec[i]來訪問數組元素。

  • 那麼二維數組是跟上面的方法一樣嗎

如果是按一維數組的方式,會寫成這樣

void fun2(int **a)
{
    a[i][j];
}
void main()
{
    int a[n][m];
    fun2(a);
}

這是錯誤的寫法。原來我是錯誤的把二維數組的名字a理解成指針的指針,而實際上這裏的a是一個指向整形數組的指針

int (*p)[m];

比如這個p就是一個指向包含m個整形元素的數組的指針,p=a的操作是合法的。

所以fun2的原型可以是以下形式(假設m=10)

void fun2(int (*mat)[10]); //假設m=10
void fun2(int mat[][10]);

這樣我們就可以在函數原型中訪問二維數組的元素mat[i][j]
這裏我們需要注意的是在定義時多維數組的第2個及以後的下標不能爲空,爲什麼是這樣設定呢,拿二維數組舉例,二維數組的元素在內存空間的地址是連續的,從mat[0][0]開始,一直到mat[0][m-1],接着是mat[1][0]到mat[1][n-1],也就是說“每一行”的數據是接在上一“行”數據的後面存放的所以mat[i][j]相對於mat[0][0]的位置是i*(m-1)+j,編譯器就是通過這樣的方式來從一維的存儲地址中找到二維數組的元素,所以第二維的長度m的值是必須的,而第一維的長度n的值是可以省略的。

  • 如果定義形參時不知道數組的大小呢

對於一維數組來說,因爲在定義形參時是不用指明數組大小的,自然沒有這個問題,但是二維數組作爲形參時是至少要寫明第二維及以後的維度的長度,而很多時候數組的大小是不確定的,這時該怎麼辦呢。

這時我們可以想,如果可以像一維數組一樣可以通過第一個元素的地址加上偏移量來訪問數組元素就好了,實際上這是可行的。

上面提到二維數組名字a是“一個指向包含m個整形元素的數組的指針”,同一維數組名字vec指針指向的是數組的第一個元素相似,二維數組名字a指向的是二維數組中第一個“包含m個整形元素的數組”,即以0爲一維座標的這段數據的這個數組a[0],對a這個一維數組再取地址&a,就可以得到a[0][0]的地址,即整個二維數組在存儲空間中的第一個數的地址,通過這個地址,再結合上面提到的求二維數組的地址偏移量的方法就可以實現通過首地址加地址偏移量的方法來訪問數組的內容。所以我們可以用下面這種方式實現

void fun2(int *head,n,m)
{
    *(head+i*(m-1)+j) //訪問mat[i][j]的值
}

void main()
{
    int n,m;
    ...     //給n和m賦值
    int mat[n][m];
    fun2(&mat,n,m);
}

以上理論參考自《c和指針》p159

總結:二維數組的不同主要還是因爲二維數組的名字和一維數組的名字的含義不同,還有二維數組的存儲方式對於語法的影響,這些都跟編譯器的設定有關,更高維的數組又是什麼樣的設計還沒了解,記得以前見過一個題提到java中二維數組必須有第一維的長度,而c中必須有第二維的長度,是因爲java的二維數組是另外一種存儲方式還是因爲java的編譯器是另外的尋址規則,這些還可以進行更加深入的探究

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章