Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing multi-dimensional array to function without righmost size by pointer to incomplete array type

Tags:

c

c11

From what I remember arrays are always passed as pointers. For instance, the declaration:

void foo(int array[2][5]);

means for compiler exactly the same thing as:

void foo(int (*array)[5]);

You can say that these both forms are equivalent. Now, I wonder, why it is allowed then to declare it as:

void foo(int (*array)[]);

while not as:

void foo(int array[][]);

Take an example:

#include <stdio.h>

void foo(int (*p)[3]);
void bar(int (*p)[]);

int main(void)
{
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};

    foo(a);
    bar(a);

    return 0;
}

// The same as int p[][3] or int p[N][3] where N is a constant expression
void foo(int (*p)[3]) 
{

}

// Would it the same as int p[][] or int p[N][] (by analogy)?
void bar(int (*p)[])
{

}

It compiles fine and without warnings, but if I change bar's declaration to:

void bar(int p[][]);

then it's an error.

Why C allows such "obscure" way to pass an array?

like image 482
Grzegorz Szpetkowski Avatar asked Dec 15 '22 13:12

Grzegorz Szpetkowski


2 Answers

Arrays are not pointers, if you declare an array of pointers with unspecified size it's ok because it will store the addresses stored in the poitners contigously and the size of each element is known, but p[][] would require the arrays to be contigous not their addresses and the size of the array is unknown so that's the problem.

To make it clear if you say int p[][] the you don't know how far is p[0] from p[1] whereas in int (*p)[] you know that the distance is the size of a pointer.

Arrays are converted to pointers but the are not pointers.

like image 64
Iharob Al Asimi Avatar answered Jan 23 '23 04:01

Iharob Al Asimi


Arrays are usable as pointers, but they're more than pointers; they point to a number of things which have a size. That way, you can tell the compiler you're interested in the third or fifth element of your array, and it will calculate the location of that element by multiplying the size of one element by three or five to find an offset, and adding that offset to the address of the array.

C doesn't actually have multidimensional arrays. What it does have is arrays of arrays, which is pretty much the same thing. Say you have this:

int a[4][5];

In memory, this would look like this:

  [0]                 |[1]                |[2]                |[3]
  +-------------------|-------------------|-------------------|-------------------+
a |   |   |   |   |   |   |   |   |   |   |   |   | X |   |   |   |   |   |   |   |
  +-------------------|-------------------|-------------------|-------------------+
    0   1   2   3   4 | 0   1   2   3   4 | 0   1   2   3   4 | 0   1   2   3   4 |

and you then try to access the element at index [2][2], then the system needs to perform the following calculation (conceptually):

  • Take the size of an int, and multiply that by five to get the size of the inner array
  • Take the size of that inner array, and multiply by two to get the offset of the second element in the outer array
  • Take the size of an int again, and multiply that by two to get the offset of the second element in the inner array
  • Add the two offsets; that's your memory address

In this case, the calculation is *(a + (2 * 5 * sizeof(int)) + (2 * sizeof(int))), giving *(a + 12*sizeof(int)), which is indeed the correct offset from the starting pointer.

All that means is that the size of the inner array needs to be defined in order for the compiler to be able to do that calculation. If you don't define the size of any dimension (but the leftmost one) of your multidimensional array, you don't have a defined size, and the compiler will balk.

like image 38
Wouter Verhelst Avatar answered Jan 23 '23 03:01

Wouter Verhelst