multiArray and multiArray[0] and &multiArray[0] same? - c

On 6th line instead of multiArray[0], when I write multiArray, program still works. Don't understand why. I was thinking before that multiArray is a pointer to multiArray[0] which is a pointer to multiArray[0][0]. So multiArray alone is a pointer to a pointer. multiArray[0] is a pointer to a 4 element int array. So it seems that multiArray and multiArray[0] must be different. But in below code, both work. Print function I wrote expects a pointer to a 4 element int array. So only multiArray[0] must work and multiArray must not work. But both works. Didn't understand that.
#include <stdio.h>
void printArr(int(*ptr)[4]);
int i, k;
int main(void){
int multiArray[3][4] = { { 1, 5, 2, 4 }, { 0, 6, 3, 14 }, { 132, 4, 22, 5 } };
int(*point)[4] = multiArray[0];
for (k = 0; k < 3; k++)
{
printArr(point++);
}
getchar();
}
void printArr(int(*ptr)[4]){
int *temp = (int *)ptr;
for (i = 0; i < 4; i++)
{
printf("%d ", *temp);
temp++;
}
puts("\n");
}

Someone else wrote "Multi-dimensional arrays are syntactic sugar for 1-D arrays".
This is sort of like saying that int is just syntactic sugar for a unsigned char[4] . You could do away with expressions like 4 + 5 and get the same result by manipulating arrays of 4 bytes.
You could even say that C is just syntactic sugar for a Universal Turing Machine script, if you want to take this concept a bit further.
The reality is that multi-dimensional arrays are a part of the type system in C, and they have syntax associated with them. There's more than one way to skin a cat.
Moving on, the way C arranges what we are calling a multi-dimension array is to say: "Arrays can only have one dimension, but the element type may itself be another array". We say "multi-dimension array" as a matter of convenience, but the syntax and the type system actually reflect the one-dimensional nature of the array.
So, int multiArray[3][4] is an array of 3 elements. Each of those elements is an array of 4 ints.
In memory, an array's elements are stored contiguously -- regardless of what the element type is. So, the memory layout is an array of 4 int, immediately followed by another array of 4 int, and finally another array of 4 int.
There are 12 contiguous int in memory, and in the C type system they are grouped up into 3 groups of 4.
You will note that the first int of the 12 is also the first int of the first group of 4. This is why we find that if we ask "What is the memory location of the first int?", "What is the memory location of the first group of 4 ints?", and "What is the memory location of the entire bloc of 12 ints?", we get the same answer every time. (In C, the memory location of a multi-byte object is considered to start at the location of its first byte).
Now, to talk about the pointer syntax and representation. In C, a pointer tells you where in memory an object can be found. There are two aspects to this: the memory location of the object, and what type of object it is. (The size of the object is a corollary of the type).
Some presentations only focus on the first of those, they will say things like "A pointer is just a number". But that is forgetting about the type information, which is a crucial part of a pointer.
When you print the pointer with %p, you lose the type information. You're just putting out the location in memory of the first byte. So they all look the same, despite the fact that the three pointers are pointing at differently-sized objects (which overlap each other like matruskha dolls).
In most implementations of C, the type information is all computed at compile-time, so if you try to understand C by comparing source code with assembly code (some people do this), you only see the memory-location part of the pointer. This can lead to misunderstanding if you forget that the type information is also crucial.
Footnote: All of this is independent of a couple of syntax quirks that C has; which have caused a lot of confusion over the years (but are also useful sometimes). The expression x is a shortcut for &x[0] if x is an array, except when used as the operand of & or sizeof. (Otherwise this would be a recursive definition!). The second quirk is that if you write what looks like an array declarator in a function formal parameter list, it is actually as if you wrote a pointer declarator. I stress again that these are just syntax oddities, they are not saying anything fundamental about the nature of arrays and pointers, which is actually not that complicated. The language would work just as well without both of these quirks.

Multidiemensional arrays var_t arr[size_y][size_x] provide means of declaring and accessing array elements (memory) in a conveniant manner. But all multidiemensional arrays are internally continuous memory blocks.
You may say that arr[y][x] = arr[y*cols+x].
In terms of pointer-level, the pointers multiArray and multiArray[0] are the same, they're int* - though the formal type for arr will be int (*)[2]. Using that type will allow one to take advantage of all pointer mechanics (++ on such pointer will move the address by 8 bytes, not 4).
Try this:
void t1(int* param)
{
printf("t1: %d\n", *param);
}
void t2(int** param)
{
printf("t2: %d\n", **param);
}
int main(void) {
int arr[2][2] = { { 1, 2 } , { 3, 4 } };
t1(arr); // works ok
t1(arr[0]); // works ok
t2(arr); // seg fault
t2(arr[0]);
}

int(*point)[4] = multiArray[0];
This works because both multiArray[0] and multiArray point to same address, the address of first element of array: multiArray[0][0].
However in this case, you may get a warning from compiler because type of multiArray[0] is int* while of point is int [4]*(pointer to array of 4 integers).

Related

Explanation how pointers and multidimensional arrays work in C

I am trying to understand the following code.
#include <stdio.h>
#include <stdlib.h>
void print2(int (* a)[2]) {
int i, j;
for (i = 0; i < 3; i++ ) {
for (j = 0; j < 2; j++ ) {
printf("%d", a[i][j]);
}
printf("\n");
}
}
void print3(int (* a)[3]) {
int i, j;
for (i = 0; i < 2; i++ ) {
for (j = 0; j < 3; j++ ) {
printf("%d", a[i][j]);
}
printf("\n");
}
}
int main() {
int a[] = { 1, 2, 3, 4, 5, 6 };
print2((int (*)[2]) a);
print3((int (*)[3]) a);
return 0;
}
Running the code returns following output in console:
12
34
56
123
456
My problem is I don't understand where these numbers come from. I have trouble understanding what is actually going on in this code. More specifically, I'm uncertain of what this means:
int( (* a)[2])
I hope someone can explain this code to me, because I really want to understand how pointers and multidimensional arrays work in C.
TL;DR
This code contains incorrect and meaningless hacks. There is not much of value to learn from this code.
Detailed explanation follows.
First of all, this is a plain 1D array that gets printed in different ways.
These lines are strictly speaking bugs:
print2((int (*)[2]) a);
print3((int (*)[3]) a);
In both cases there is an invalid pointer conversion, because a is of type int[6] and a pointer to the array a would have to be int (*)[6]. But the print statements are wrong in another way too, a when used in an expression like this "decays" into a pointer to the first element. So the code is casting from int* to int(*)[2] etc, which is invalid.
These bugs can in theory cause things like misaligned access, trap representations or code getting optimized away. In practice it will very likely "work" on all mainstream computers, even though the code is relying on undefined behavior.
If we ignore that part and assume void print2(int (*a)[2]) gets a valid parameter, then a is a pointer to an array of type int[2].
a[i] is pointer arithmetic on such a type, meaning that each i would correspond to an int[2] and if we had written a++, the pointer would jump forward sizeof(int[2]) in memory (likely 8 bytes).
Therefore the function abuses this pointer arithmetic on a[i] to get array number i, then do [j] on that array to get the item in that array.
If you actually had a 2D array to begin with, then it could make sense to declare the functions as:
void print (size_t x, size_t y, int (*a)[x][y])
Though this would be annoying since we would have to access the array as (*a)[i][j]. Instead we can use a similar trick as in your code:
void print (size_t x, size_t y, int (*a)[x][y])
{
int(*arr)[y] = a[0];
...
arr[i][j] = whatever; // now this syntax is possible
This trick too uses pointer arithmetic on the array pointer arr, then de-references the array pointed at.
Related reading that explains these concepts with examples: Correctly allocating multi-dimensional arrays
void print2(int (*a)[2]) { /*...*/ }
inside the function print2 a is a pointer to arrays of 2 ints
void print3(int (*a)[3]) { /*...*/ }
inside the function print3 a is a pointer to arrays of 3 ints
int a[] = {1, 2, 3, 4, 5, 6};
inside the function main a is an array of 6 ints.
In most contexts (including function call context) a is converted to a pointer to the first element: a value of type "pointer to int".
The types "pointer to int", "pointer to array of 2/3 ints" are not compatible, so calling any of the functions with print2(a) (or print3(a)) forces a diagnostic from the compiler.
But you use a cast to tell the compiler: "do not issue any diagnostic. I know what I'm doing"
print3(a); // type of a (after conversion) and type of argument of print3 are not compatible
// print3((cast)a); // I know what I'm doing
print3((int (*)[3])a); // change type of a to match argument even if it does not make sense
It would be much easier to understand if you break it down and understand things. What if you were to pass the whole array to say a function print4, which iterates over the array and prints the elements? How would you pass the array to such a function.
You can write it something like
print4( (int *) a);
which can be simplified and just written as print4(a);
Now in your case by doing print2((int (*)[2]) a);, you are actually designing a pointer to an array of 2 int elements. So now the a is pointer in array of two elements i.e. every increment to the pointer will increase the offset by 2 ints in the array a
Imagine with the above modeling done, your original array becomes a two dimensional array of 3 rows with 2 elements each. That's how your print2() element iterates over the array a and prints the ints. Imagine a function print2a that works by taking a local pointer to a and increments at each iteration to the point to the next two elements
void print2a(int (* a)[2]) {
int (* tmp)[2] = a;
for( int i = 0; i < 3; i++ ) {
printf("%d%d\n", tmp[0][0], tmp[0][1] );
tmp++;
}
}
The same applies to print3() in which you pass an pointer to array of 3 ints, which is now modeled as a 2-D array of 2 rows with 3 elements in it.
The code seeks to reinterpret the array int a[6] as if it were int a[3][2] or int a[2][3], that is, as if the array of six int in memory were three arrays of two int (in print2) or two arrays of three int (in print3).
While the C standard does not fully define the pointer conversions, this can be expected to work in common C implementations (largely because this sort of pointer conversion is used in existing software, which provides motivation for compilers to support it).
In (int (*)[2]) a, a serves as a pointer to its first element.1 The cast converts this pointer to int to a pointer to an array of two int. This conversion is partially defined C 2018 6.3.2.3 7:
The behavior is undefined if the alignment of a is not suitable for the type int (*)[2]. However, compilers that have stricter alignment for arrays than for their element types are rare, and no practical compiler has a stricter alignment for an array of six int than it does for an array of two or three int, so this will not occur in practice.
When the resulting pointer is converted back to int *, it will compare equal to the original pointer.
The latter property tells us that the resulting pointer contains all the information of the original pointer, since it must contain the information needed to reconstruct the original pointer. It does not tell us that the resulting pointer is actually pointing to the memory where a is.
As noted above, common C implementations allow this. I know that Apple’s versions of GCC and Clang support this reshaping of arrays, although I do not know whether this guarantee was added by Apple or is in the upstream versions.
Given that (int (*)[2]) is passed to print2 as its a, then a[i][j] refers to element j of array i. That is, a points to an array of two int, so a[0] is that array, a[1] is the array of two int that follows it in memory, and a[2] is the array of two int after that. Then a[i][j] is element j of the selected array. In effect, a[i][j] in print2 is a[i*2+j] in main.
Note that no aliasing rules are violated as no arrays are accessed by a[i][j]: a is a pointer, a[i] is an array but is not accessed (it is automatically converted to a pointer, per footnote 1 below), and a[i][j] has type int and accesses an object with effective type int, so C’s aliasing rules in C 2018 6.5 7 are satisfied.
Footnotes
1 This is because when an array is used in an expression, it is automatically converted to a pointer to its first element, except when it is the operand of sizeof, is the operand of unary &, or is a string literal used to initialize an array.

What is the reason for the existence of pointers to the array?

The following code
#include <stdio.h>
char * arr[]={"my","array"};
main()
{
printf("%p %p\n",arr, &arr);
char *(*arr_ptr)[2];
arr_ptr = &arr;
arr_ptr = arr;
}
compiles with the warning
2.c: In function ‘main’:
2.c:15:10: warning: assignment from incompatible pointer type [enabled by default] arr_ptr = arr;
However, being run, the compiled program would print two identical numbers.
The conclusion is that there exists a dissimilar type, namely 'pointer to the array', that points to exactly the first element of the array.
My question is: what is the reason for such a pointer type to exist, isn't just having arr as an pointer to the array not enough?
The reasons to have pointers to arrays are the same as the reasons for types:
It helps reduce errors. If a variable is declared to be a pointer to an array, and you try to assign a different type of pointer to it, the compiler can warn you when your code is compiled instead of letting your program do the wrong thing when it executes. This is the same reason we have pointers to int separate from pointers to char. It is even the reason we have int separate from char—giving types to all variables helps ensure they are used for what they are intended for.
It provides convenience. When you have an array of arrays and want to work with specific rows, you might have a pointer A to one of the rows (which is an array). Then A+1 is the next row, not the next element in the row, and A-2 is two rows back. So pointers to arrays can be used to work with arrays as objects, and the compiler will track the arithmetic for you so you do not have to use A-2*NumberOfColumns to calculate how many elements back is two rows back.
Because why not.Pointer's job is pointing thinks.You can use that type of pointer's (pointer to array) make your job's easier and when you know the 1. element of the array you will know the rest so pointer will be wery usefull here's a simple example
int i = 0;
int arr[4] = { 1, 2, 3, 4};
for(;i<4;i++){
printf("%d \n",arr[i]);
}
the output will be :
1 2 3 4
but using simple pointers
*arr = 5;
*(arr+1)=6;
*(arr+2)=7;
*(arr+3)=8;
the output will be :
5 6 7 8
reached array with pointers easly.

difference between int a[9] and a[3][3]

What is the difference between
#include <stdio.h>
int a[9];
int
main()
{
printf("%d\n", a[1]);
}
and
#include <stdio.h>
int a[3][3];
int
main()
{
printf("%d\n", a[1]);
}
I think both result in placing same 36 byte memory buffer in .bss section, what is the difference? Or is a[3][3] syntactic sugar over a[9] - a[3*3]?
Nope, they are not same, they represent different types. In reference to your code,
In the first case, a is an one dimensional array. Hence, a[1] is of type int.
§) To print the value, %d is fine.
In the second case, however, a is a two-dimensional array. Hence, a[1] is of type int [3].
§) When passed as a function argument, it decays to a pointer to the first element, basically a int *. You'll be needing %p to print that (and cast the pointer to void *, as required by the %p format specifier).
However, if you're bothered about the memory layout for both the variables, you can check the other answer by AnT or another one which details about the memory layout of multi-dimensional arrays.
int a[3][3] is a semantic sugar of sorts over int a[9]. The raw memory layout it the same (i.e. it is a contiguous block of 9 ints), but the language-level access syntax is different. When accessing a[3][3] as a[i][j], the apparent 2D-indexing is converted by the compiler to 1D indexing using i * 3 + j formula. The latter index translation scheme is easily extendable to any number of dimensions.
What is the difference between
int a1[9];
and
int a2[3][3];
(I've changed the names so I can talk about the declarations more easily.)
The difference is that they're of different types. They both have the same underlying memory layout,each consisting of a contiguous region of memory 9 times the size of an int. But a1 is an array of 9 int objects, while a2 is an array of 3 objects each of which is an array of 3 int objects. It's a multidimensional array, which in C is precisely an array of array, nothing more, nothing less.
The difference is not just syntactic sugar. You might get the same generated code for certain operations, for example a1[1] and a2[0][1]. But, for example, a1[3] refers to the 4th element of the array a1, while a2[0][3], though you might think it refers to the same thing, actually has undefined behavior. (Compilers are permitted, but not required, to perform run-time array bound checking, and are permitted to assume that array references do not go past the end of the indexed array object.)
printf("%d\n", a2[1]);
As others have mentioned, this has undefined behavior. a2[1] is an array object of type int[3]. An expression of array type is, in most contexts, converted to an expression of pointer type, so a2[1] ends up being of type int*, and yields a pointer to the initial element of the second row of a2. To print a pointer value, use %p -- which requires an argument of type void*, so you need to cast it:
printf("%p\n", a2[1]);
Recommended reading: Section 6 of the comp.lang.c FAQ.

Questions about pointers and arrays

Sanity-check questions:
I did a bit of googling and discovered the correct way to return a one-dimensional integer array in C is
int * function(args);
If I did this, the function would return a pointer, right? And if the return value is r, I could find the nth element of the array by typing r[n]?
If I had the function return the number "3", would that be interpreted as a pointer to the address "3?"
Say my function was something like
int * function(int * a);
Would this be a legal function body?
int * b;
b = a;
return b;
Are we allowed to just assign arrays to other arrays like that?
If pointers and arrays are actually the same thing, can I just declare a pointer without specifying the size of the array? I feel like
int a[10];
conveys more information than
int * a;
but aren't they both ways of declaring an array? If I use the latter declaration, can I assign values to a[10000000]?
Main question:
How can I return a two-dimensional array in C? I don't think I could just return a pointer to the start of the array, because I don't know what dimensions the array has.
Thanks for all your help!
Yes
Yes but it would require a cast: return (int *)3;
Yes but you are not assigning an array to another array, you are assigning a pointer to a pointer.
Pointers and arrays are not the same thing. int a[10] reserves space for ten ints. int *a is an uninitialized variable pointing to who knows what. Accessing a[10000000] will most likely crash your program as you are trying to access memory you don't have access to or doesn't exist.
To return a 2d array return a pointer-to-pointer: int ** f() {}
Yes; array indexing is done in terms of pointer arithmetic: a[i] is defined as *(a + i); we find the address of the i'th element after a and dereference the result. So a could be declared as either a pointer or an array.
It would be interpreted as an address, yes (most likely an invalid address). You would need to cast the literal 3 as a pointer, because values of type int and int * are not compatible.
Yes, it would be legal. Pointless, but legal.
Pointers and arrays are not the same thing; in most circumstances, an expression of array type will be converted ("decay") to an expression of pointer type and its value will be the address of the first element of the array. Declaring a pointer by itself is not sufficient, because unless you initialize it to point to a block of memory (either the result of a malloc call or another array) its value will be indeterminate, and may not point to valid memory.
You really don't want to return arrays; remember that an array expression is converted to a pointer expression, so you're returning the address of the first element. However, when the function exits, that array no longer exists and the pointer value is no longer valid. It's better to pass the array you want to modify as an argument to the function, such as
void foo (int *a, size_t asize)
{
size_t i;
for (i = 0; i < asize; i++)
a[i] = some_value();
}
Pointers contain no metadata about the number of elements they point to, so you must pass that as a separate parameter.
For a 2D array, you'd do something like
void foo(size_t rows, size_t columns, int (*a)[columns])
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < columns; j++)
a[i][j] = some_value;
}
This assumes you're using a C99 compiler or a C2011 compiler that supports variable length arrays; otherwise the number of columns must be a constant expression (i.e., known at compile time).
These answers certainly call for a bit more depth. The better you understand pointers, the less bad code you will write.
An array and a pointer are not the same, EXCEPT when they are. Off the top of my head:
int a[2][2] = { 1, 2, 3, 4 };
int (* p)[2] = a;
ASSERT (p[1][1] == a[1][1]);
Array "a" functions exactly the same way as pointer "p." And the compiler knows just as much from each, specifically an address, and how to calculate indexed addresses. But note that array a can't take on new values at run time, whereas p can. So the "pointer" aspect of a is gone by the time the program runs, and only the array is left. Conversely, p itself is only a pointer, it can point to anything or nothing at run time.
Note that the syntax for the pointer declaration is complicated. (That is why I came to stackoverflow in the first place today.) But the need is simple. You need to tell the compiler how to calculate addresses for elements past the first column. (I'm using "column" for the rightmost index.) In this case, we might assume it needs to increment the address ((2*1) + 1) to index [1][1].
However, there are a couple of more things the compiler knows (hopefully), that you might not.
The compiler knows two things: 1) whether the elements are stored sequentially in memory, and 2) whether there really are additional arrays of pointers, or just one pointer/address to the start of the array.
In general, a compile time array is stored sequentially, regardless of dimension(s), with no extra pointers. But to be sure, check the compiler documentation. Thus if the compiler allows you to index a[0][2] it is actually a[1][0], etc. A run time array is however you make it. You can make one dimensional arrays of whatever length you choose, and put their addresses into other arrays, also of whatever length you choose.
And, of course, one reason to muck with any of these is because you are choosing from using run time multiplies, or shifts, or pointer dereferences to index the array. If pointer dereferences are the cheapest, you might need to make arrays of pointers so there is no need to do arithmetic to calculate row addresses. One downside is it requires memory to store the addtional pointers. And note that if the column length is a power of two, the address can be calculated with a shift instead of a multiply. So this might be a good reason to pad the length up--and the compiler could, at least theoretically, do this without telling you! And it might depend on whether you select optimization for speed or space.
Any architecture that is described as "modern" and "powerful" probably does multiplies as fast as dereferences, and these issues go away completely--except for whether your code is correct.

Why do we need to specify the column size when passing a 2D array as a parameter?

Why can't my parameter be
void example(int Array[][]){ /*statements*/}
Why do I need to specify the column size of the array? Say for example, 3
void example(int Array[][3]){/*statements*/}
My professor said its mandatory, but I was coding before school started and I remembered that there was no syntactical or semantic error when I made this my parameter? Or did I miss something?
When it comes to describing parameters, arrays always decay into pointers to their first element.
When you pass an array declared as int Array[3] to the function void foo(int array[]), it decays into a pointer to the beginning of the array i.e. int *Array;. Btw, you can describe a parameter as int array[3] or int array[6] or even int *array - all these will be equivalent and you can pass any integer array without problems.
In case of arrays of arrays (2D arrays), it decays to a pointer to its first element as well, which happens to be a single dimensional array i.e. we get int (*Array)[3].
Specifying the size here is important. If it were not mandatory, there won't be any way for compiler to know how to deal with expression Array[2][1], for example.
To dereference that a compiler needs to compute the offset of the item we need in a contiguous block of memory (int Array[2][3] is a contiguous block of integers), which should be easy for pointers. If a is a pointer, then a[N] is expanded as start_address_in_a + N * size_of_item_being_pointed_by_a. In case of expression Array[2][1] inside a function (we want to access this element) the Array is a pointer to a single dimensional array and the same formula applies. The number of bytes in the last square bracket is required to find size_of_item_being_pointed_by_a. If we had just Array[][] it would be impossible to find it out and hence impossible to dereference an array element we need.
Without the size, pointers arithmetics wouldn't work for arrays of arrays. What address would Array + 2 produce: advance the address in Array 2 bytes ahead (wrong) or advance the pointer 3* sizeof(int) * 2 bytes ahead?
In C/C++, even 2-D arrays are stored sequentially, one row after another in memory. So, when you have (in a single function):
int a[5][3];
int *head;
head = &a[0][0];
a[2][1] = 2; // <--
The element you are actually accessing with a[2][1] is *(head + 2*3 + 1), cause sequentially, that element is after 3 elements of the 0 row, and 3 elements of the 1 row, and then one more index further.
If you declare a function like:
void some_function(int array[][]) {...}
syntactically, it should not be an error. But, when you try to access array[2][3] now, you can't tell which element is supposed to be accessed. On the other hand, when you have:
void some_function(int array[][5]) {...}
you know that with array[2][3], it can be determined that you are actually accessing element at the memory address *(&array[0][0] + 2*5 + 3) because the function knows the size of the second dimension.
There is one other option, as previously suggested, you can declare a function like:
void some_function(int *array, int cols) { ... }
because this way, you are calling the function with the same "information" as before -- the number of columns. You access the array elements a bit differently then: you have to write *(array + i*cols + j) where you would usually write array[i][j], cause array is now a pointer to integer (not to a pointer).
When you declare a function like this, you have to be careful to call it with the number of columns that are actually declared for the array, not only used. So, for example:
int main(){
int a[5][5];
int i, j;
for (i = 0; i < 3; ++i){
for (int j=0; j < 3; ++j){
scanf("%d", &a[i][j]);
}
}
some_function(&a[i][j], 5); // <- correct
some_function(&a[i][j], 3); // <- wrong
return 0;
}
C 2018 6.7.6.2 specifies the semantics of array declarators, and paragraph 1 gives constraints for them, including:
The element type shall not be an incomplete or function type.
In a function declaration such as void example(int Array[][]), Array[] is an array declarator. So it must satisfy the constraint that its element type must not be incomplete. Its element type in that declaration is int [], which is incomplete since the size is not specified.
There is no fundamental reason the C standard could not remove that constraint for parameters that are about to be adjusted to pointers. The resulting type int (*Array)[] is a legal declaration, is accepted by compilers, and can be used in the form (*Array)[j].
However, the declaration int Array[][] suggests that Array is at least associated with a two-dimensional array, and hence is to be used in the form Array[i][j]. Even if the declaration int Array[][] were accepted and were adjusted to int (*Array)[], using it as Array[i][j] would not be possible because the subscript operator requires that its pointer operand be a pointer to a complete type, and this requirement is not avoidable as it is needed to calculate the address of the element. Thus, keeping the constraint on the array declarator makes sense, as it is consistent with the intended expression that the argument will be a two-dimensional array, not just a pointer to one one-dimensional array.
Actually whether it is a 2d array or a 1d array, it is stored in the memory in a single line.So to say the compiler where should it break the row indicating the next numbers to be in the next rows we are supposed to provide the column size. And breaking the rows appropriately will give the size of the rows.
Let's see an example:
int a[][3]={ 1,2,3,4,5,6,7,8,9,0 };
This array a is stored in the memory as:
1 2 3 4 5 6 7 8 9 0
But since we have specified the column size as 3 the memory splits after every 3 numbers.
#include<stdio.h>
int main() {
int a[][3]={1,2,3,4,5,6},i,j;
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
OUTPUT:
1 2 3
4 5 6
In the other case,
int a[3][]={1,2,3,4,5,6,7,8,9,0};
The compiler only knows that there are 3 rows but it doesn't know the number of elements in each row so it cannot allocate memory and will show an error.
#include<stdio.h>
int main() {
int a[3][]={1,2,3,4,5,6},i,j;
for(i=0;i<3;i++)
{
for(j=0;j<2;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
OUTPUT:
c: In function 'main':
c:4:8: error: array type has incomplete element type 'int[]'
int a[3][]={1,2,3,4,5,6},i,j;
^
As we know, we can pass a variable as an argument(s) in a function. Similarly, we can pass two-dimensional arrays in C++.
C++ does not allow us to pass an entire array as an argument to a function. However, we can pass a pointer to an array by specifying the array's name without an index.
We can pass a 2D array to a function by specifying the size of the columns of a 2D array. One of the important things to remember here is that the size of rows is optional but the size of the column should not be left empty else the compiler will show an error. A 2D array is stored in the memory in a single line. So, to say the compiler where should it break the row indicating the following numbers to be in the next rows we are supposed to provide the column size. And breaking the rows appropriately will automatically give the size of the rows.
source: https://www.scaler.com/topics/two-dimensional-array-in-cpp/
There is a similar post regarding this. You can refer below link.
Creating Array in C and passing pointer to said array to function
Hope it helps.
On the other hand, compiler needs to the second dimension so that it can move "Array" from one pointer to next since the whole memory is arranged in a linear fashion
I thought this was a cool approach. If you take this as the formula to calculate the address of an element in the array:
a[i][j] = baseArrayAddress + (i + (colSize + elementSize)) + (j * (elementSize))
Then you can see that the only thing the compiler needs to know (which it can't otherwise infer) is the size of the column, thus you need to provide it as the programmer so the algorithm can run to calculate the offset.
The row number only acts as a multiplier and is provided by the programmer when trying to dereference an array location.
When you create a 2D array, anytype a[3][4], in memory what you actually create is 3 contiguous blocks of 4 anytype objects.
a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]
Now the next question is, why is that so? Because, keeping with the spec and structure of the language, anytype a[3][4] actually expands out into anytype (*a)[4], because arrays decay into pointers. And in fact that also expands out into anytype (*(*a)), however, you've now completely lost the size of the 2D array. So, you must help the compiler out a bit.
If you ask the program for a[2], the program can follow the exact same steps that it does for 1D arrays. It simply can return the 3rd element of sizeof(object pointed to), the object pointed to here is of size 4 anytype objects.

Resources