在一般情形之下,一維陣列當作參數(parameter)傳遞給副程式作運算是很常見的事情...
但有時候會需要用到二維甚至多維的陣列當參數,此時原本的傳遞方法就會無法編譯
以下提供多維陣列的傳遞方法,以三維陣列為例.
首先,陣列的傳遞是使用call by reference的方式,而陣列無論維度高低,在記憶體中都是一個連續的區塊.
傳到副函式裡面的是陣列的起始位址,在給予陣列維度大小即可存取陣列.
舉例:
void sub(int ary[] , int size)
{
//array operation.
}
main( )
{
int tmp[5]={1,2,3,4,5}; //建立一個陣列,初始內容值為1,2,3,4,5
sub(tmp , 5);
}
在上面的例子中,main()的部份呼叫副函式sub()
傳進去的tmp,其實等同於tmp[0] , 由於維度只有一維
相當於傳進去陣列的最起始位址.搭配上維度大小,在sub()部份即可對陣列作運算.
而副函式sub()對引數ary[]所做的內容修改,會直接修改 tmp[ ]內容,因為他是直接使用call by reference
而在二維以上的陣列部份,則有所不同
範例如下:
void sub(int ary[] , int d1 , int d2 , int d3)
{
//array operation.
int val=0;
for(int i=0 ; i<d1 ; i++)
{
for(int j=0 ; j<d2 ; j++)
{
for(int k=0 ; k<d3 ; k++)
{
/*
i *d2*d3 = 算出目前的i值位於d1的哪一個區塊
j = 算出目前的j位於d2的哪一個區塊
*/
ary[(i*d2*d3)+(j*d3)+k]=val;
val++;
}
}
}
}
main( )
{
int tmp[3][3][3]={{0},{0},{0}}; //建立一個陣列,初始內容值為0
sub(tmp[0][0] , 3,3,3);
}
上述為三維陣列傳送至副程式作運算的方法,有幾個重點需要注意
main()主程式方面:
由於之前有說過,主程式要傳送整塊陣列在記憶體中的起始位置,一維陣列可以直接傳陣列名稱
即可代表一維陣列起始位址,但多維以上就不能套用原有規則,原因是只傳陣列名稱進去的話,只有指定第一維的位置
但實際上還有更多的維度,無法清楚代表一整塊記憶體的起始位置,所以我們必須指定在第一維的開頭中,第二維也是開頭 [0] ,甚至到第三維
也就是說假如是三維陣列,主程式就必須傳送
tmp[0][0]
若是二維陣列就必須
tmp[0]
如此才能精確表達傳進去的是起始位址,下圖是多維陣列在記憶體中的表示法
上圖為tmp[3][3][3] 的三維矩陣在記憶體中的表示法
d1,d2,d3分別表示三個維度在記憶體中的相對關係
從圖可以發現,多維矩陣就是在每個矩陣元素裡面在插入第二維度數量的矩陣
而圖中綠色字體則是表示每個元素在記憶體中的位置.
接下來要說明如何在副程式存取多維矩陣:
由於副程式的引數只是個一維指標,則我們必須利用多維陣列在記憶體中是一塊連續的記憶體這個特性來存取
從圖中我們可以發現黃色字部份是一連串的數字
也就是在sub function 裡要存取tmp[2][2][2]相當於存取 tmp[26] 的概念.
在上面的範例程式中,副程式利用
ary[(i*d2*d3)+(j*d3)+k]=val;
這行程式碼可以存取三維陣列的值.
i,j,k 的部份分別是巢狀迴圈,控制要讀取陣列元素之每一個維度的 變數
而 i*d2*d3 則是算出,第一個維度變數 i 是位在記憶體中的那一個大區塊.
因為每一個d1 中 有d2*d3個 個數,以此來找到要控制的元素位在哪個d1大區塊
在 +(j*d3) 意思跟上面雷同,也就是第二個維度變數j 是在固定d1區塊中裡面的那一個d2區塊
j 是第二維度變數,乘以d3 原因是因為每一個d2當中有 d3個 個數
最後在加上第三維度變數 k.
用巢狀迴圈則可以依維度順序存取矩陣元素.
而當然,在圖中黃色部份也可以用另外一個方式在主程式存取
例如:在主程式要存取tmp[2][2][2]
第一種方式:
tmp[2][2][2] = val ; //直接存取,快又省事!
第二種方式:
*(*(*(tmp))+26) = val; 這樣也可以.
但這樣在資料存取控制上非常不直覺而且容易寫錯...
因此...如果有好的方法就別拿石頭砸自己腳了:)
留言列表