2

我有這樣的功能:傳遞多個維陣列的功能用C

void myfunc(int** arr, int n) { 
    int i, j; 
    for(i=0; i<n; ++i) { 
    for(j=0; j<n; ++j) { 
     printf("%d,", *(arr + i*n + j)); // Print numbers with commas 
    } 
    printf("\n");      // Just breakline 
    } 
} 
在其他功能

我有一個二維陣列是這樣的:

int main() { 
    int seqs[8][8] = { 
    {0, 32, 36, 52, 48, 16, 20, 4}, 
    {0, 16, 20, 52, 48, 32, 36, 4}, 
    {0, 32, 36, 44, 40, 8, 12, 4}, 
    {0, 8, 12, 44, 40, 32, 36, 4}, 
    {0, 32, 36, 38, 34, 2, 6, 4}, 
    {0, 2, 6, 38, 34, 32, 36, 4}, 
    {0, 32, 36, 37, 33, 1, 5, 4}, 
    {0, 1, 5, 37, 33, 32, 36, 4} 
    }; 

    // Call to myfunc 
    myfunc(seqs, 8); // This is the idea 
} 

但是編譯器擲我這個錯誤:

什麼是傳遞這個數組(seqs),以功能的正確方法(myfunc)?

+0

http://www-ee.eng.hawaii.edu/~dyun/ee160/Book/chap7/section2。 1.2.html – Jeyaram

+0

它應該看起來像'void myfunc(int arr [] [8],int n){'。 – 2014-02-17 06:44:19

+0

從函數代碼的外觀來看,實際上你需要一個'int *'而不是''int **'作爲你的'arr'參數。然後傳入第一個元素的地址('&seqs [0] [0]')。 – Dmitri

回答

2

數組和指針是不一樣的。同樣,二維數組和指向指針的指針也不是一回事。類型不匹配seqs函數的參數,你可以修改的myfunc到簽名:

void myfunc(int arr[][8], int n) 

或者,您可以修改seqs到一個真正的指針的指針。

+0

如何靜音警告並仍然接受任何大小的數組?這是真正的問題,我認爲 – slezica

+1

@uʍopǝpısdn你是對的,OP有參數'n'的原因。一個真正的指針指針是一個選項。 –

+0

@YuHao,我無法更改'myfunc'簽名,我可以將'seqs'從數組更改爲指針,但有沒有辦法將'seqs'作爲'int [] []'傳遞'或者我必須改變它? – Israel

1

您聲明int**爲靜態大小的數組,因此其簽名爲int *[8]。當您撥打myfunc時,您可以通過轉換陣列來擺脫編譯器錯誤。

myfunc((int **)seqs, 8); 

在一個實際的程序中,這樣的數組很可能會動態生成,而且您不必這樣做。

+1

如果你分配一個動態二維數組[正確](http://stackoverflow.com/questions/12462615/how-do-i-correctly-set-up-access-and-free-a-multidimensional-array-in- c),你會遇到和這裏一樣的問題。如果你把它分配到一個基於指針的查找表中,那麼你就不需要這個表演了。 – Lundin

+0

很高興知道,我不知道'sizeof'處理過這樣的多維數組。看起來它甚至可能會回到C90。 – OregonTrail

+0

不幸的是,它不會(請參閱接受的答案中的註釋),因爲您不能在C90中聲明具有可變長度的數組指針a,它必須是編譯時常量。 – Lundin

0

您在函數中訪問數組中值的方式會使用簡單的int *而不是int **爲您提供正確的元素。用*(arr + i*n + j)i*n讓你到右邊一排,j讓你成爲專欄。所以,你的函數標題行更改爲:

void myfunc(int* arr, int n) { 

...(有一個「*」),並通過了第一個元素的地址,而不是裸露的數組名,像這樣:

myfunc(&seqs[0][0], 8); 

..或者第一行:

myfunc(seqs[0], 8); 
0

當定義的2維數組作爲

int seqs[8][8]; 

seqs的類型是8 elements的數組,其中每個element的類型爲array of 8 integers。當您通過seqsmyfunc時,seqs將隱式轉換爲指向其第一個元素(當將數組傳遞給函數時發生的情況)的指針,即鍵入* elementelement類型爲int [8]。因此seqs隱式轉換爲int *[8]類型 - 指向8個整數數組的指針。

在你的函數myfunc,參數arr的類型是int **明確了不同類型比你通過它,當你在main調用它。它們是不同的類型並且具有不同的指針算術。編譯器抱怨結果。

您應該將arr的類型更改爲int *[8]

void myfunc(int arr[][8], int n); 

arr是不是在這裏一個數組,但一個指向8個整型數組,你還不如申報myfunc

void myfunc(int *arr[8], int n); 

他們是完全一樣的。

編輯

關於你的評論here,你不能做以下,並希望事情能夠正常工作。

// in main 
myfunc((int **)seqs, 8); 

// the above call is essentially doing this 
int **arr = (int **)seqs; 

seqsarr是不兼容的類型和類型轉換seqs抑制由編譯器,因爲類型轉換不正確的事情,更糟糕的是,否則發出警告,並使它看起來好像事情是好的。

讓我們來看看爲什麼。 (函數調用後)

seqs (type: array of 8 int[8] type; value: address of the location seqs[0][0]) 
*seqs (type: int[8] type, value: address of the location seqs[0][0]) 

arr (type: pointer to a pointer to an int; value: address of the location seqs[0][0]) 
*arr (type: pointer to an int; value: seqs[0][0]) 
**arr (type: int; value: value at the address seqs[0][0]) 

上述斷言可以用assert宏來檢查。所以我們看到,當我們做**arr我們實際上正在做的是將值seqs[0][0]作爲內存地址並嘗試訪問該位置的值。 這顯然是錯的!它可能導致未定義的行爲或最可能的分段錯誤。

即使通過類型轉換,也無法使seqs的值(初始值爲arr時的值)作爲int **。這也表明,我們應該小心類型化值,除非我們知道並確定我們正在做什麼,否則不應該這樣做。

因此,您必須更改您的myfunc函數簽名才能執行您想要的操作。

+0

關於編輯:轉換爲'int **'並且用*(*(arr + i * n)+ j)兩次取消引用將不起作用,因爲'arr'中的地址仍然是數組數據 - 不是指向它的指針的地址。 – Dmitri

+0

...並且該地址不包含指向行的指針,而是包含行本身。 – Dmitri

+0

@Dmitri對。我刪除了編輯。 – ajay

5

在C99或C11,你會做這樣的:

void myfunc(int n, int arr[n][n]) 
{ 
    for (int i = 0; i < n; ++i) 
    { 
     for (int j = 0; j < n; ++j) 
      printf("%d,", arr[i][j]); 
     printf("\n"); 
    } 
} 

注意大小之前,不如下,該陣列。該功能將與正常工作:

int main(void) 
{ 
    int seqs[8][8] = 
    { 
     { 0, 32, 36, 52, 48, 16, 20, 4 }, 
     { 0, 16, 20, 52, 48, 32, 36, 4 }, 
     { 0, 32, 36, 44, 40, 8, 12, 4 }, 
     { 0, 8, 12, 44, 40, 32, 36, 4 }, 
     { 0, 32, 36, 38, 34, 2, 6, 4 }, 
     { 0, 2, 6, 38, 34, 32, 36, 4 }, 
     { 0, 32, 36, 37, 33, 1, 5, 4 }, 
     { 0, 1, 5, 37, 33, 32, 36, 4 }, 
    }; 
    myfunc(8, seqs); 

    int matrix3x3[3][3] = { { 1, 2, 3 }, { 2, 4, 6 }, { 3, 6, 9 } }; 
    myfunc(3, matrix3x3); 
} 

有人問我:

Your example does look much better indeed, but is it well-defined? Is n really guaranteed to be evaluated before int arr[n][n] ? Wouldn't the order of evaluation of function parameters be unspecified behavior?

舊標準(ISO/IEC 9899:1999)說,在§6.7.5.2* 數組聲明 *:

¶5 If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by * ; otherwise, each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.

它給出了一個例子(這是非規範性的文本,因爲它是一個例子,但強ly指示預期內容):

EXAMPLE 4 All declarations of variably modified (VM) types have to be at either block scope or function prototype scope. Array objects declared with the static or extern storage-class specifier cannot have a variable length array (VLA) type. However, an object declared with the static storage class specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.

extern int n; 
int A[n];      // invalid: file scope VLA 
extern int (*p2)[n];   // invalid: file scope VM 
int B[100];      // valid: file scope but not VM 
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope 
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA 
{ 
    typedef int VLA[m][m];  // valid: block scope typedef VLA 
    struct tag { 
     int (*y)[n];   // invalid: y not ordinary identifier 
     int z[n];    // invalid: z not ordinary identifier 
    }; 
    int D[m];     // valid: auto VLA 
    static int E[m];   // invalid: static block scope VLA 
    extern int F[m];   // invalid: F has linkage and is VLA 
    int (*s)[m];    // valid: auto pointer to VLA 
    extern int (*r)[m];   // invalid: r has linkage and points to VLA 
    static int (*q)[m] = &B; // valid: q is a static block pointer to VLA 
} 

還有其他示例顯示可變修改的功能參數。

此外,在§6.9.10函數定義,它說:

¶10 On entry to the function, the size expressions of each variably modified parameter are evaluated and the value of each argument expression is converted to the type of the corresponding parameter as if by assignment. (Array expressions and function designators as arguments were converted to pointers before the call.)

+2

Uhms,這是你的答案編號2^13,很有詩意:) – fedorqui

+0

'seq'初始化的最後一個逗號不會引起任何警告。我不知道它是否應該,但它讓我想起了Python的列表,元組和字典類型,尾隨逗號是可以接受的。 – ajay

+1

@ajay:自從K&R第一版(1978年左右)之前,C在初始化程序中允許使用尾隨逗號。定義枚舉時,C89不允許尾隨逗號,但C99添加了明確允許的規則。它使得生成C初始化器變得更容易,添加尾隨初始化器意味着修改不太可能失敗(因爲在添加額外的數據行時沒有添加額外的逗號)。 –