2017-06-15 154 views
0

比方說,我有兩個數組,我將它們傳遞給函數:爲什麼我們在傳遞動態二維數組時不需要列數?

void func(int arr1[][4], int **arr2) { // <- I need to give n in only one, why? 
... 
} 
int main() { 
    int n = 5, m = 4; 
    int arr1[n][m]; 
    int **arr2 = (int**)malloc(n * sizeof(int*)); 
    for(int i = 0;i < n;i++) 
     arr2[i] = (int*)malloc(m * sizeof(int)); 
    func(arr1, arr2); 
    return 0; 
} 

爲什麼我們不能把經過都以類似的方式排列?

編輯:代碼中有錯誤。

+3

'arr2'不是一個數組,而是一個指針,實際上是一個指向指針的指針。 – alk

+0

在任何一種情況下,函數都需要以某種方式知道指向數據的大小。不管語法如何,都沒有辦法。所以理想情況下,你會寫'void func(size_t x,size_t y,int arr [x] [y])''。 – Lundin

+1

另請參閱[正確分配多維數組](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays),以消除對動態二維數組的誤解。 – Lundin

回答

1

與你所說的相反,情況是這樣的:你不必傳遞行數。當您通過int arr[][MAX_COL]編譯器知道在哪裏,當你解決諸如arr[row][col]例如下一行開始

int arr[MAX_ROW][MAX_COL]; /* with both 3 */ 

      col 
    ---------------> 
    | 0,0 0,1 0,2 
row | 1,0 1,1 1,2 
    V 2,0 2,1 2,2 

:假設數組的索引像這樣工作。

如果您想使用指針手動執行該操作,它看起來像這樣:&arr[0][0] + row * MAX_COL + col。在那個例子中,你也必須知道數組的列大小MAX_COL來計算下一行。

原因是數組在內存中是連續的。上述陣列中,如內存表示:

|  row = 0  |  row = 1  |  row = 2  | 
| 0,0 0,1 0,2 | 1,0 1,1 1,2 | 2,0 2,1 2,2 | 

編譯程序還必須知道,當你通過聲明爲int arr[MAX_SIZE]的功能void foo (int arr[])數組行,因爲偏移,其衰變成一個指針的開始數組int* arr。在數組數組(二維數組)的情況下,它衰減到一個指向其第一個元素的指針,這是一個指向單個數組的指針int (*arr)[MAX_COL]

簡而言之:int arr[][MAX_COL]編譯器具有所需的所有信息來尋址數組arr[row][col]

+0

我明白你的答案,它肯定有幫助。總之,如果我可以這樣建議,將會更好:「以更簡單的方式,int ** arr2是使用指針形成的整數的2-d網格,而int arr1 [] []是整數的一維數據結構,有關行結束的信息「。如果我理解錯了,請糾正我。 –

+0

@AbhishekAgrawal:不知道你的意思是二維網格和一維數據結構。實際上所有的陣列,1D,2D,3D等都連續存儲在內存中。 –

+0

是指向連續指針的指針,因爲每一行都是在每次迭代中使用malloc分配的,所以第一行的最後一個元素的地址和第二行的第一個元素之間應該有一個跳轉? –

0

實際上恰恰相反,您只能省略其中一個索引(在多維數組的情況下),即最內層索引。

這是因爲,當作爲函數參數傳遞數組時,數組衰減到指向第一個元素的指針。引用C11,章§6.3.2.1

當它是sizeof操作者的操作數時,操作者_Alignof或 一元&操作者,或是用於初始化數組文本字符串,具有 類型「」類型的陣列「」表達轉換爲表達式與類型「」指針爲類型「」指向 到陣列對象的初始元素,不是左值。 [...]

因此,像

void func(int arr1[][5], int **arr2) //an array of array of 5 ints 

void func(int (*arr1) [5], int **arr2) //pointer to the starting element of an array of 5 ints 

的符號是等同的。

+0

請再詳細解釋一下:'衰減到指向第一個元素的指針'。 –

+0

是不是所謂的*最外層*(可忽略的)?我相信它被稱爲內存佈局,而不是代幣之間的代碼位置。 –

+1

@AbhishekAgrawal實際上,函數array *參數*被「調整」爲指向數組元素類型的指針。所以'int [42']被*調整爲'int *'。 Array * decay *是當你用一個數組作爲*參數*調用這樣一個函數時發生的情況。而「二維數組」是一組數組。 – juanchopanza

0

實際上您只有一個整數數組(即int arr1[][5])和一個指向int的指針的指針,即int **arr2。即使像arr1[10][5]這樣的數組在作爲參數傳遞給函數時衰減到指向元素所在內存開始的指針,在內存佈局和編譯器如何處理訪問這些指針。

順便說一句,主要應該是int n=5,m=4;int arr1[m][n],而不是int n=5,m=4;int arr1[n][m]

關於存儲器佈局:

形式int [10][5]的2D整數數組表示爲10個連續的「行」,每個包括5「列」(即積分值)。這個數組的大小是10 * 5 * sizeof(int),一個「行」的大小是5 * sizeof(int)

指向int int **p的指針只是一個指針;它的大小是sizeof(int**),即使你有「malloced」一系列積分指針lile p = malloc(10 * sizeof (int*));請注意0​​中的「*」,因爲您創建了一個指向整數的指針序列,而不是整數序列。這是內存佈局的主要區別:它不是一個整數的二維數組,而是一個指向整數的一維數組。如果實際上爲「10」整數分配了10個「行」,則每行可以位於內存的不同部分。管理10×5積分值所需的空間是「10 * sizeof(int *)+ 10 * 5 * sizeof(int)」。

關於訪問:

假設int arr[][5]類型,這是整數的2D陣列,一個變量,其中一列的大小是5和沒有被確定的行數。非正式地,像int x = arr[3][4]那樣的訪問被轉換成訪問數組的第012個元素,即「行時間行加列」;注意 - 基於這個公式 - 編譯器不需要知道數組實際具有多少行。

相比之下,我們假設一個int **p類型的變量。您可以將x = p[3][4]這樣的訪問視爲等同於int *r = p[3]; int x = r[4];請注意,r類型爲int *,即它是一個指針,然後r[4]取消引用此指針並返回一個整數值。

這是非常非正式的描述。 然而,主要問題是arr[][5]的內存佈局僅包含連續的整數值,而int **arrr可能是指針的序列(或者甚至只是一個這樣的指針),它們中的每一個可能指向一系列整數值(或者僅僅是一個整數值)。

相關問題