5

As per the post,

Passing a 2D array to a C++ function

int array[10][10];
void passFunc(int a[][10]) //  <---Notice 10 here
{
    // ...
}
passFunc(array);

Why is this higher dimension required from compilers internal point of view.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Mahesh Attarde
  • 378
  • 3
  • 16
  • 3
    If you think of a 2D array as a flat 1D array. How do you think it might be indexed. – Mohamad Elghawi Feb 09 '16 at 10:47
  • as long as Indexing is issue implementing n-d array "ptr+ d1*size+d2*size+.....+index" will also work – Mahesh Attarde Feb 09 '16 at 10:49
  • 5
    because when you try to access `a[2][0]` the second dimension is required to know at which memory location to find this element (it is at `a+(2*10)+0`) – 463035818_is_not_an_ai Feb 09 '16 at 10:49
  • @tobi303; Why it is necessary to provide length in the declaration `int a[10];`? – haccks Feb 09 '16 at 10:56
  • @haccks Thats a different question, and the answer is: To tell the compiler how much memory it should allocate. – 463035818_is_not_an_ai Feb 09 '16 at 11:00
  • @tobi303; In other words to let the compiler know the size of array, agreed? – haccks Feb 09 '16 at 11:02
  • @haccks yep, but when passing a 1D array to a function there is no need tell the compiler its size while with 2D arrays the size of the 2nd dimension has to be specified and hence (i guess) the OPs question. – 463035818_is_not_an_ai Feb 09 '16 at 11:38
  • @tobi303; Yes. that;s because you need to tell the compiler to what object pointer points to and for that you must have to specify the size of array the pointer points to. There is no involvement of 2D array in the function parameter. – haccks Feb 09 '16 at 12:16
  • @haccks Conceptually, he is passing a 2D array. And conceptually, to do anything meaningful with a 2D array you need to know at least the size of one dimension. The rest (and how this is realized in C++) is details, and honestly I dont get your point (but I already get warnings to avoid discussions ;) – 463035818_is_not_an_ai Feb 09 '16 at 12:27
  • @tobi303; No. He is not passing 2D array. – haccks Feb 09 '16 at 12:32

6 Answers6

4

An alternative explanation (to array-to-pointer decay):

Let's say we have a one-dimensional array, and we use it like this:

int array[10];
int i = array[3];

The compiler has to know where to find array[3]. It knows it needs to skip 3 ints before it can get to the one in array[3]. So it works.

But if we have a two-dimensional array,

int array[2][5];
int i = array[1][1];

To get i here, how many ints does the compiler need to skip? It needs to skip an entire row, plus one. To skip one is easy, since we know the size of one int. But we also need to know the size of the row in the array—and the size of the row is determined by the size of the type * number of columns per row. This is one way of looking at it, which explains why you need the latter dimension.

Let's make this a small brain teaser by taking it one dimension further, to

int array[2][2][2];
int i = array[1][1][1];

and let's call the dimensions X, Y, Z.

Here, we can say we have a finite 3D space of ints. The unit is of course the size of one int. The number of rows is defined by Y, the number of planes is defined by Z. That leaves X as the basic unit, which is the size of one int, as we said. The combination of the three yields a "point."

To be able to get to any point in that 3D space, we need to know where each dimension "stops" and the next one begins. So we need:

  1. The size of the unit (int), to traverse the X dimension
  2. The size of each plane (Y), to traverse the Y dimension
  3. The number of planes, to traverse the Z dimension

So again, X is already given to us, because we're using int. But we don't know the size of each plane, nor do we know how many planes there are. So we need to specify all but the first dimension. And that's the general rule.

This also explains why this issue invites a bit more elaborate explanation than mere pointer decay, because once you get to more than 2 dimensions, you still need to know how this works. In other words, you need the overall size (product of dimensions) to not overflow, and you need the dimension of each size to be able to use successive [] indices.

Yam Marcovic
  • 7,953
  • 1
  • 28
  • 38
  • 1
    But note that this misses the point that the function parameter isn't a 2D array at. – juanchopanza Feb 09 '16 at 10:59
  • 2
    To say "a 3D array is a pointer to a 2D array which is a pointer to an array which is a pointer," is just plain wrong. **Arrays are not pointers.** – Peter - Reinstate Monica Feb 09 '16 at 11:54
  • @PeterA.Schneider Note I covered this: what I meant by "_partially_ true" is that the decaying process can work. You can traverse a 3D array with a simple pointer to an `int`. – Yam Marcovic Feb 09 '16 at 12:04
  • Your statement about pointers is very missleading. That is exactly the reason we get questions like "how to pass 2D array `int **`" every day. When it comes to pointers and array, you should be very precise and use the standard terminology to avoid confusion. – too honest for this site Feb 09 '16 at 12:57
  • @Olaf I didn't say "pointer to pointer", I said they were all decayable to just one pointer. Also, it seems too easy to miss that the reason I said this at all, along with "**partially** true", was because I saw it being left to the imagination in other answers. However, I'll fix the phrasing to make it less confusing. – Yam Marcovic Feb 09 '16 at 13:11
  • "which is a pointer to an array which is a pointer" implies that. At least to a newbie. **I** do understand what you mean, but only because I know what's going on. But still this is wrong, an arrary does not always decay to a pointer. First you should always think of is that `[]` does not work on arrays. It is **only** a pointer-operator (well, it requires one pointer argument, which one does not matter - try `int a[4] = {0}; (void)4[a];`). – too honest for this site Feb 09 '16 at 13:15
  • @Olaf I'm in the midst of working out a less confusing phrasing. But when is an array ever not *decyable* to a pointer? – Yam Marcovic Feb 09 '16 at 13:17
  • Keep that under your pillow: http://port70.net/~nsz/c/c11/n1570.html (And it is not about an ability, but either it decays (the standard term is "converted to ...") or not). – too honest for this site Feb 09 '16 at 13:18
  • @Olaf 1. Thanks for linking the entire standard (C standard, at that) as a supposed answer; 2. I intentionally said, and emphasized, decay*able*; 3. I'll ask to end here because your attitude is really ungraceful. – Yam Marcovic Feb 09 '16 at 13:35
  • I read that vey well. And I also stated decaying is not an option, as "-able" implies, but clearly defined by the standard when it occurs and when not. Also note that's why I wrote the standard does not use this term. And no, I did not give a "supposed answer". Please read the comment carefully again. – too honest for this site Feb 09 '16 at 13:40
3

Arrays in C/C++ are a type, but not a first-class object and they "decay" into a pointer to the first element when passed to functions.

An int[10][10] is an array of 10 int[10] arrays... the function declarations:

void foo(int x[][10]);

typedef int IntArray10[10];
void bar(IntArray10 *x);

are for the compiler identical.

When passing a 2d array to a function therefore you're passing a pointer to the first element (and the first dimension is ignored) but the element itself is an array and the compiler needs to know its size.

6502
  • 112,025
  • 15
  • 165
  • 265
  • Note that in C++ arrays _are_ first-class objects. They don't decay if passed by reference. (One of those dangers in assuming C/C++ is one language) – MSalters Feb 09 '16 at 11:13
  • Interestingly enough, when passing a multi-dimensional array as a parameter, we have both array-decay and "array-type-preservation": The *elements* to which the decay-resulting pointer points are still full-fledged arrays. Akin to why one cannot write `int arr[2][3]; int **p = arr;`, even though one *can* write `**arr`. – Peter - Reinstate Monica Feb 09 '16 at 11:30
  • Well, what happens in the function parameters is type *adjustment*, i.e. something that looks like an array is adjusted to pointer. The decay happens when you pass an object of a suitable type to the function. – juanchopanza Feb 09 '16 at 12:13
  • @MSalters: you don't pass an array "by reference", you pass a reference. – 6502 Feb 09 '16 at 13:36
3

Contrary to what you might think from the "[]" in the parameter int a[][10], the function doesn't take a two-dimensional array but a pointer to a one-dimensional array - its prototype is equivalent to

void passFunc(int (*a)[10])

array can decay into a pointer to its first element, like all arrays.
That pointer is, as usual, &array[0], and in this case it is a pointer to an array with ten ints - an int (*)[10].

So it's not that you need to specify the "higher dimension", it's that the parameter is a pointer to an array of ten elements and not a two-dimensional array at all.

(You can't have a be a pointer to an array of unspecified size because the compiler needs to know where a[1] is located in relation to a[0], i.e. it needs to know sizeof(*a).)

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

It is required because in C doesn't exist the concept of multidimensional array.
We can define an array of anything, even another array. The last could be seen as a multidimensional array.
Now consider a bidimensional array as:

int arrray[5][4];

This is to be interpreted as an array of 5 elements, each one being an array of 4 int.
In memory the layout is sequential: first the 4 elements of the first array, then the second and so on up to the 5th array of 4 elements.
To access the 2nd element of 3rd array array[2][1] the compiler have to skip the first 2 arrays, but to do so needs to know how many elements to skip.
I.e. mathematically the address of the 2nd element of the 3rd array is:

//pElement2Array3  points the 2nd element of the 3rd array.
//Note that 4 is the number of elements of the base dimension
int *pElement2Array3 = &array[0] + (2 * 4) + 1;

(Note: the term (2 * 4) + 1 is not multiplied by the sizeof(int) because this is done automatically by pointer arithmetic.)
The math above is automatically performed by the compiler when using the addressing array[2][1], but to execute it the compiler have to know how many elements are in the base array (that could be ana array of array of array...).

Frankie_C
  • 4,764
  • 1
  • 13
  • 30
  • yes that is true. Consider this (we can bcz i am implementing) i have prototype int passFunc(int[][]); int a[2][2]; passFunc(a); call will only be resolved with correct arguments, and caller scope knows "a" and its dimensions,still why we need dimensions in callee scope? – Mahesh Attarde Feb 09 '16 at 11:28
  • We need dimensions wherever the compiler have to generate code to address an element of the array. – Frankie_C Feb 09 '16 at 11:46
0

Simply put, because multi-dimensional arrays "grow" from right to left. Think of it like this:

int arr [a]; is an array of a integers.

int arr [b][a] is an array of b arrays of a integers.

And so on.

As for why you can omit the left-most dimension when passing an array to a function, it is because of "array decay". 6.7.6.3/7 says:

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’

That is, if you declare a function parameter as int param[10] or int param[], it gets silently/invisibly replaced by the compiler with a pointer to int: int* param, which points at the first element of the array. The array itself remains allocated by the caller.

It works like this to prevent arrays from getting passed to functions by value, which would be very ineffective and in most cases doesn't make any sense.

Now for the case of multi-dimensional arrays, the same rule above applies. So if you have int param[10][10] it decays into a pointer to the first element. The first element is an array of 10 integers, so you get an array pointer to an array of 10 integers: int (*param)[10].

The very same would happen if you have int param[][10], you would still get a int (*param)[10]. So the left-most dimension could be anything - it doesn't matter since it is ignored anyhow.

But the other dimensions following the left-most are required, or otherwise the compiler wouldn't know which pointer type that the array would decay into. Outside the special case of function declarations, there is no such thing as int(*param)[] which would mean "an array pointer to an array of unknown size".

Lundin
  • 195,001
  • 40
  • 254
  • 396
-1

As a function parameter int a[][10] is equivalent to int (*a)[10], means : a is a pointer to an array of 10 int. It doesn't represent a 2D array in this case.

If the higher dimension left blank then it is not possible for the compiler to know the length of array the pointer a is pointing to and pointer arithmetic can't be performed.

haccks
  • 104,019
  • 25
  • 176
  • 264