tags:

views:

194

answers:

4

Hello. I have the function with following signature:

void box_sort(int**, int, int)

and variable of following type:

int boxes[MAX_BOXES][MAX_DIMENSIONALITY+1]

When I am calling the function

box_sort(boxes, a, b)

GCC gives me two warnings:

103.c:79: warning: passing argument 1 of ‘box_sort’ from incompatible pointer type (string where i am calling the function)
103.c:42: note: expected ‘int **’ but argument is of type ‘int (*)[11] (string where the function is defined)

The question is why? Whether int x[][] and int** x (and actually int* x[]) are not the same types in C?

+1  A: 

You never constructed an array of pointers as the signature requires.

There are two ways to do 2D arrays in C. In one case, you just have a lot of something and the compiler is told what the dimensions are. It calculates the beginning of the row by multiplying the row index by the number of columns and then it adds the column index to find the element within that row.

The other way is with a vector of pointers, where the compiler just dereferences the vector to find the beginning of the row, but the compiler won't make these for you automatically, you have to do it yourself.

Your actual object is one of the first kind, but your function prototype is asking for the second kind.

So you should either change the prototype to match the object or construct a vector of row pointers to pass to the function.

DigitalRoss
Well explained, DigitalRoss. For more info, see also http://c-faq.com/aryptr/index.html
Denilson Sá
+5  A: 

I know there was a question almost exactly like this a couple days ago... can't find it now though.

The answer is, int[size][] (see note at the bottom) and int** are definitely not the same type. You can use int[] and int* interchangeably in many cases, in particular in cases like this because the array decays to a pointer to the first element when you pass it into a function. But for a two-dimensional array, these are very different methods of storing.

Here's what they'd look like in memory for a 2x2 array:

int a[2][2]:

__a[0][0]__|__a[0][1]__|__a[1][0]__|__a[1][1]__
  (int)       (int)       (int)       (int)

int **a (e.g. dynamically allocated with nested mallocs)

__a__
(int**)
  |
  v
__a[0]__|__a[1]__
  (int*)  (int*)
    |        |
    |        |
    v        ------------------>
__a[0][0]__|__a[0][1]__        __a[1][0]__|__a[1][1]__
  (int)       (int)              (int)       (int)

You could construct the second one like this:

int **a = malloc(2 * sizeof(int*));
a[0] = malloc(2 * sizeof(int));
a[1] = malloc(2 * sizeof(int));

Note: As others have noted, int[][] isn't a real type; only one of the sizes can be unspecified. But the core of the question here is whether a two-dimensional array and a double pointer are the same thing.

Jefromi
Cool ASCII-art, but I think you have the first index wrong for the boxes pointed by the arrow.
RaphaelSP
@RaphaelISP: Thanks, just a copy paste error, obviously.
Jefromi
I like the visual explanation. I wonder though, if they have made it possible for * and [] to work interchangebly why not for *{n} and []{n}? Was it simply too difficult?
NomeN
NomeN: It's because it just doesn't make sense - you can sensibly convert an array to a pointer to its first element, because an array is just a sequence of contiguous elements; but you *can't* sensibly convert an array-of-arrays to a pointer-to-a-pointer, because a pointer-to-a-pointer has to be pointing *at* pointer(s), and there are no actual pointers there for it to point at. Ahem.
caf
@caf: Thanks for covering that. I saw NomeN's comment and thought, wait, doesn't the visual explanation demonstrate why they can't work interchangeably? Words are good too, though!
Jefromi
@caf if you *really* wanted to have that feature in a language couldn't you do a *deep* conversion? i.e. converting a 2d array to a pointer to an array of pointers to the subarrays. It requires an additional array or at least something that can be iterated, but it should work right? Or am I missing something fundamental?
NomeN
A: 

There is no such type in C as int[][], only the first part of a multidimensional array can be unspecified. So int[][5] is okay.

In addition to the other answers posted here, if you can use C99, you can use variable arrays to accomplish what you want:

void box_sort(int N, int M, int x[M][N]);

This will work on most platforms except Windows.

Dietrich Epp
"Except on Windows" ? I've not tried with GCC on Cygwin or MinGW, but I'm pretty sure it works. I guess you meant "Visual C++".
RaphaelSP
Yes, I am aware that Windows is not a compiler. Forgive my shorthand.
Dietrich Epp
A: 

When an array expression appears in most contexts, its type is implicitly converted from "N-element array of T" to "pointer to T", and its value is set to the address of the first element in the array. The exceptions to this rule are when the array expression is an operand of the sizeof or address-of (&) operators, or if the array expression is a string literal being used to initialize another array in a declaration.

What this means in the context of your code is that in your call to box_sort, the type of the expression boxes is implicitly converted from M-element array of N-element array of int to pointer to N-element array of int, or int (*)[MAX_DIMENSIONALITY+1], so your function should be expecting parameter types like:

void box_sort(int (*arr)[MAX_DIMENSIONALITY+1], int x, int y)
{
   ...
}

Since int *a and int a[] are synonymous in a function parameter declaration, it follows that int (*a)[N] is synonymous with int a[][N], so you could write the above as

void box_sort(int arr[][MAX_DIMENSIONALITY+1], int x, int y)
{
}

although I personally prefer the pointer notation, as it more accurately reflects what's going on. Note that in your function, you would subscript arr as normal:

arr[x][y] = ...;

since the expression arr[x] is equivalent to *(arr + x), the pointer is implicitly dereferenced.

If you want box_sort to work on arbitrarily-sized arrays (i.e., arrays where the second dimension isn't necessarily MAX_DIMENSIONALITY+1), then one approach is to do the following:

int boxes[X][Y];
...
box_sort (&boxes[0], X, Y, x, y);
...
void box_sort(int *arr, size_t rows, size_t cols, int x, int y)
{
  ...
  arr[x*cols + y] = ...;
}

Basically, you're treating boxes as a 1-d array of int and calculating the offsets manually.

John Bode