3

I'm learning about C and pointers in my university class, and I think I've got a pretty good grasp on the concept except for the similarity between multidimensional arrays and pointers.

I thought that since all arrays (even multidimensional) ones are stored in contiguous memory, you could safely convert it to a int* (assuming the given array is an int[].) However, my professor said that the number of stars in the definition depends on the number of dimensions in the array. So, an int[] would become an int*, a int[][] would become an int**, etc.

So I wrote a small program to test this:

void foo(int** ptr)
{    
}

void bar(int* ptr)
{    
}

int main()
{
    int arr[3][4];

    foo(arr);
    bar(arr);
}

To my surprise, the compiler issued warnings on both function calls.

main.c:22:9: warning: incompatible pointer types passing 'int [3][4]' to parameter of type
      'int **' [-Wincompatible-pointer-types]
    foo(arr);
        ^~~
main.c:8:16: note: passing argument to parameter 'ptr' here
void foo(int** ptr)
         ^
main.c:23:9: warning: incompatible pointer types passing 'int [3][4]' to parameter of type
      'int *' [-Wincompatible-pointer-types]
    bar(arr);
        ^~~
main.c:13:15: note: passing argument to parameter 'ptr' here
void bar(int* ptr)
         ^
2 warnings generated.

What's going on here? How could you change the function calls so one of them would accept the array?


My original question got marked as a duplicate, and since I can't figure out how to get the duplicate removed I'm asking it again.

Edit: This is not a duplicate of How to pass a multidimensional array to a function in C and C++ because I'm asking how to change the function calls themselves, not the function signatures. I know one of these functions is correct, I'm just not sure which one or how you would go about calling it.

Edit 2: This is a duplicate of my original question, but the original was wrongly marked as a duplicate and couldn't get answered so I re-asked it.

Community
  • 1
  • 1
Dovahkiin
  • 946
  • 14
  • 25
  • 3
    You get the duplicate closure removed by editing your old question to make it clear that it's not a duplicate (including linking the post that you're referencing) and then waiting for it to get sufficient re-open votes. What you **do not** do is then post the duplicate question again. The [help] has more information about what to do if your question gets closed; it's in the Asking section. – Ken White Feb 24 '17 at 17:57
  • This is *partly* a duplicate of the question you link to. An array of array is *not* the same as a pointer to pointer. See e.g. [this old answer of mine](http://stackoverflow.com/a/18440456/440558) for an explanation why. – Some programmer dude Feb 24 '17 at 17:58
  • The next time a question of yours is closed and you want to have it reopened, do not accept an answer. If you do, you are saying that you are satisfied with it, and therefore there's no reason to reopen it. – Fabio says Reinstate Monica Feb 24 '17 at 18:20

3 Answers3

7

However, my professor said that the number of stars in the definition depends on the number of dimensions in the array. So, an int[] would become an int*, a int[][] would become an int**, etc.

I'm really, really hoping you simply misunderstood what he was saying, because that's not correct.

Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. If T is an array type, you wind up with a pointer to an array, not a pointer to a pointer.

Going through some examples:

int arr[10];
...
foo( arr, 10 ); // need to pass number of rows as a separate parameter

In the call to foo, the expression arr has type "10-element array of int". Since it's not the operand of the sizeof or unary & operators, it "decays" to type int *, and the value of the expression will be the address of the first element. Thus, the prototype for foo would be

void foo( int *arr, size_t rows ); // or int arr[]; it means the same thing in this context

Notice that this is the same type as the result of the expression &arr[0]. arr[0] is an int object, so &arr[0] gives us an int *. IOW, arr == &arr[0].

So far so good. Now let's look at a 2D array:

int arr[10][20];
...
foo( arr, 10 );

In this case, the expression arr has type "10-element array of 20-element array of int"; it "decays" to an expression of type "pointer to 20-element array of int"; thus, the prototype for foo becomes

void foo( int (*arr)[20], size_t rows ); // or int arr[][20]

Remember the part above where it says "except when it is the operand of ... the unary & operator"? If we write &arr[0], arr[0] has type "20-element array of int", but it does not automatically decay to a pointer. So instead of getting an expression of type int **, we get an expression of type int (*)[20]. So again, arr == &arr[0].

Now let's look at a 3D array:

int arr[10][20][30];
...
foo( arr, 10 );

This time, the expression arr has type "10-element array of 20-element array of 30-element array of int". This time, it "decays" to an expression of type "pointer to 20-element array of 30-element array of int", and the prototype is now

void foo( int (*arr)[20][30], size_t rows ); // or int arr[][20][30]

And once again, arr[0] has an array type, so the expression &arr[0] gives us the type int (*)[20][30]; again, arr == &arr[0].

The pattern for higher-dimensioned arrays should be clear from here on out.

Now, this brings up a minor issue. A pointer to an N-element array is a different type from pointer to an M-element array, where N != M. If your function prototype is

void foo( int (*)[20], size_t rows );

then it will only work with Nx20 arrays; you cannot pass it a pointer to an array with a different outer dimension:

void foo( int (*ap)[20], size_t rows );
...
int arr1[10][20];
int arr2[20][20];
int arr3[20][30];

foo( arr1, 10 ); // okay
foo( arr2, 20 ); // okay
foo( arr3, 20 ); // not okay - arr3 has type int (*)[30], which is not *compatible* 
                 // with int (*)[20]

EDIT

In the case where a function is expecting an int * and you have a multi-dimensional array, you'd explicitly pass a pointer to the first element:

void foo( int *, size_t size );
...
int arr2[10][20];
int arr3[20][10][5];

foo( &arr2[0][0], sizeof arr2 / sizeof arr2[0][0] );
foo( &arr3[0][0][0], sizeof arr3 / sizeof arr3[0][0][0] );
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Unfortunately, I specifically remember him saying the line about the number of dimensions being the number of stars in the pointer definition. Thanks for the great explanation though, it's a lot clearer now, +1 – Dovahkiin Feb 24 '17 at 21:37
2

Check the data types carefully, you'll get it.

For the function void bar(int* ptr) {... it expects an int *, and you've got to pass one. Something like

 bar (&(arr[0][0]));

would do it.

That said, in general, an array is not the same as a pointer. For most of the cases, the array decays to a pointer to the first element of the array, but then again, the data types should be same (or at least, compatible).

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
0

Suppose, a = [1,2,3]

Here, a is the pointer to the first element of the array.

*a = 1 , *(a+1) = 2, *(a+2) = 3.

Now , if a is multidimensional array, like a[3][4]

*a = a[0][0] , *(a+1) = a[1][0], *(a+2) = a[2][0]

*(*a + 1) = a[0][1], *(*a + 2) = a[0][2]

*(*(a+1) + 1) = a[1][1], *(*(a+1) + 2) = a[1][2]

*(*(a+2) + 1) = a[1][1], *(*(a+2) + 2) = a[1][2]

*a + any_number changes the row, but *(*a + any_number) changes the column.

In function, you only pass the pointer to array, here i.e, a.

so ,

void bar(*a){
#access elements like told above.
}

Double pointer **b, it means it is pointing to another pointer, means, if

*b = a

b's value is the address of a i.e, *b and a have same values. But **b has points to where a points.

means **b and *a have same values.

Luv33preet
  • 1,686
  • 7
  • 33
  • 66