1

Why aren't these function prototypes equivalent?

void print_matrix(char *name, int SX, int SY, int m[SX][SY])

void print_matrix(char *name, int SX, int SY, int **m)
Brad Larson
  • 170,088
  • 45
  • 397
  • 571
richard
  • 309
  • 2
  • 8
  • Hey guys, let's not pick on @richard too much - he's probably not doing the downvoting. I myself had forgotten that C has different rules for local variables as it does for parameter types. There's also the issue of VLAs. You can't expect richard to appreciate all these subtleties. – Aaron McDaid Sep 08 '12 at 11:30
  • possible duplicate of [What is difference between types int** and int\[\]\[\]?](http://stackoverflow.com/questions/8395255/what-is-difference-between-types-int-and-int) – Bo Persson Sep 08 '12 at 12:06
  • Sorry, I thought at first that you had changed so much about this question that you wanted it rolled back to the original. I've removed the comments that were made obsolete, but you have a good answer here to the question as asked, so it all worked out. In the future, rather than making a dramatic change to a question that totally alters its meaning, simply leave it be and ask a new question on the new topic. – Brad Larson Sep 08 '12 at 19:33

1 Answers1

6

Even though the two function arguments can be consumed in the same way, namely via m[i][j], they're quite different:

  • int m[M][N] is an array of M arrays of N ints.

  • int **m is a pointer to a pointer to an int.

You cannot pass arrays as function arguments, so an "array of K elements of type T" decays to a "poin­ter-to-T", pointing to the first element of the array. Thus it is permissible and equivalent to write the first form as int m[][N] in a function argument, since the value M is lost. However, the value N is not lost; it is part of the type!

So the following are admissible/erroneous for the first form:

void f(int arr[M][N]);

int a[M][N];
int b[2*M][N];
int c[M][N + 1];

f(a);   // OK
f(b);   // OK; slowest extent is forgotten in the decay
//f(c); // Error! 'c' is not an array of {array of N ints}.

For the second form, the meaning is rather different:

void g(int **p);

int a;
int * pa = &a;

g(&pa);          // fine, pointer to 'pa'

int arr[M][N];

// g(arr);  // Error, makes no sense

The expression arr designates the pointer to the first element of an array of arrays of N integers, i.e. its type is int (*)[N]. Dereferencing it gives an array of N integers, and not a pointer to an integer.

There is no way to convert the expression arr into a pointer to a pointer: If you said,

int ** fool = (int**)arr;

then *fool would point to the first element of the first array (arr[0]), and not to an int pointer. So you cannot dereference the value further, because the value is not a pointer.

The only correct way to pass a two-dimensional array as a double pointer is to construct an intermediate helper array:

int * helper[M];   // array of pointers

for (size_t i = 0; i != M; ++i)
{
    helper[i] = arr[i]; // implicit decay
}

g(helper);  // or "g(&helper[0])"
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084