Pass multiple-dimensional array to a function in C

瘦欲@ 提交于 2019-12-20 03:32:07

问题


I have a function like this:

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
  }
}

in other function I have an two-dimensional array like this:

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
}

But compiler throw me this error:

lab.c: In function 'main':
lab.c:75:5: warning: passing argument 1 of 'myfunc' from incompatible pointer type [enabled by default]
lab.c:4:6: note: expected 'int **' but argument is of type 'int (*)[8]'

What is the right way to pass this array (seqs) to function (myfunc)?


回答1:


In C99 or C11, you would do it like this:

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");
    }
}

Note that the size precedes, not follows, the array. This function will work correctly with:

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);
}

I was asked:

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?

The old standard (ISO/IEC 9899:1999) says in §6.7.5.2*Array declarators*:

¶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.

And it gives an example (it is non-normative text because it is an example, but strongly indicative of what is expected):

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
}

There are other examples showing variably modified function parameters.

Also, in §6.9.10 Function definitions, it says:

¶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:


Arrays and pointers are not the same. Similarly, two-dimensional array and pointer to pointer are not the same thing. The type of seqs mismatches function argument, you can modify the signature of myfunc to:

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

or, you can modify seqs to a real pointer to pointer.




回答3:


You're declaring the int** as an array with a static size, so its signature is int *[8]. You can get rid of the compiler error by casting the array when you call myfunc.

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

In an actual program such an array would most likely be generated dynamically, and you wouldn't have to do this.




回答4:


The way you're accessing the value from the array in your function, would give you the right element with a simple int * rather than an int **. With *(arr + i*n + j). The i*n gets you to the right row, and the j gets you the column. So change your function header line to:

void myfunc(int* arr, int n) {

...(with one '*'), and pass the address of the first element rather than the bare array name, like this:

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

..or the first row:

myfunc(seqs[0], 8);



回答5:


When you define a 2-dimensional array as

int seqs[8][8];

the type of seqs is array of 8 elements where each element is of type array of 8 integers. When you pass seqs to myfunc, seqs is implicitly converted to a pointer to its first element (which is what happens when you pass an array to a function), i.e., to type * element. element is of type int [8]. Hence seqs is implicitly converted to type int *[8] - a pointer to an array of 8 integers.

In your function myfunc, the type of the parameter arr is int ** clearly a different type than what you pass it when you call it in main. They are different types and have different pointer arithmetic. The compiler complains as a result.

You should change the type of arr to int *[8].

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

arr is not an array here but a pointer to an array of 8 integers and you might as well declare myfunc as

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

They are exactly the same.

Edit

Regarding your comment here, you cannot do the following and expect things to work correctly.

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

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

seqs and arr are incompatible types and typecasting seqs suppresses the warning emitted otherwise by the compiler which is worse because typecasting doesn't correct things, and make it seem as if things are alright.

Let us see why. (After the function call.)

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])

The above assertions can be checked with the assert macro. So we see that when we do **arr what we are actually doing is treating the value seqs[0][0] as a memory address and trying to access the value at that location. This is obviously wrong! It can lead to undefined behaviour or most likely segmentation fault.

There's no way to make the value of seqs (value it evaluates to in the initialization of arr) act like a int ** even by typecasting. This also shows that we should be careful with typecasting values and shouldn't do it unless we know and are sure of what we are doing.

Therefore, you must change your myfunc function signature to do what you want.



来源:https://stackoverflow.com/questions/21822382/pass-multiple-dimensional-array-to-a-function-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!