4

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?

3
  • void foo(int array[2][5]); is equivalent to void foo(int *array[5]);, not void foo(int (*array)[5]); Commented Jul 15, 2015 at 18:22
  • 2
    @ooga No, void foo(int array[2][5]); is equivalent to void foo(int (*array)[5]);. int *array[5] is an array of pointers, not a pointer to arrays. Commented Jul 15, 2015 at 19:12
  • @Dmitri You're right, of course. Brain glitch (I was thinking of argv, which is of course actually an array of pointers as you say). Commented Jul 15, 2015 at 19:35

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

13 Comments

Interesting, but can't these things be resolved at runtime when the arguments are passed? Even in the example, it is obvious that the distance from p[0] to p[1] is 12 bytes.
No they must be resolved at compile time, and if it depends on what is passed to the function then you would have a different function for each different call. It could not depend on the parameters.
@Purag With newer versions of the C standard, you can pass the sizes (in other arguments) to do this at runtime... eg. void arrfunc(int x, int y, int arr[y][x])
Incomplete because it doesn't really answer the question, you say this fact about arrays but don't tie it into why it won't compile. While it may be obvious, you're missing the detail regarding the specific problem with memory and the fact that the function allocates the same amount of space on the stack no matter what.
Actually, with int (*p)[] you don't know that the distance is the size of a pointer -- that's a pointer to arrays of int, and you've left out the size of those arrays. Did you mean int *p[] (array of pointers to int)?
|
5

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.

1 Comment

It's a good answer. Just be sure to use the right terminology. Like "an array is converted to a pointer to it's first element, but the pointer ultimately points to ...".
2

You're tripping over the C language support for arrays of 'unspecified' size, which you can declare, but can't generally use directly unless you give it a more specific size somewhere.

In your example, if you actually try to do anything with the array in bar, you'll get an error:

void bar(int (*p)[])
{
    printf("%d\n", p[1][1]);
}

% gcc -Wall t.c
t.c: In function ‘bar’:
t.c:23:5: error: invalid use of array with unspecified bounds
     printf("%d\n", p[1][1]);
     ^

The only thing you can do with p in bar is cast it to some type with an explicit size, or pass it to some other function that takes a pointer to an array (of specified or unspecified size), and if you use the wrong explicit size to try access the array, you get undefined behavior with no warning.

3 Comments

This is okay information but not an answer to the question, so I'm not sure why it's the accepted answer...
@Purag: there isn't really a question in the question, other than "why does C allow this". My answer attempts to answer that question -- its not doing what you think it does.
@Purag: I edited my question, so I hope it's clearer. The other anwers aren't bad, but I think they doesn't address to my (somewhat specific) question. Chris's answer is to closest to it and that's why it is accepted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.