Related
I am having a tough time understanding the type and use of the name of the array in C. It might seems a long post but please bear with me.
I understand that the following statement declares a to be of type int [] i.e array of integers.
int a[30];
While a also points the first element of array and things like *(a+2) are valid. Thus, making a look like a pointer to an integer. But actually the types int [] and int* are different; while the former is an array type and later is a pointer to an integer.
Also a variable of type int [] gets converted into a variable of type int* when passing it to functions; as in C arrays are passed by reference (with the exception of the sizeof operator).
Here comes the point which makes me dangle. Have a look at the following piece of code:
int main()
{
int (*p)[3];
int a[3] = { 5, 4, 6 };
p = &a;
printf("a:%d\t&a:%d\n",a,&a);
printf("%d",*(*p + 2));
}
OUTPUT:
a:2686720 &a:2686720
6
So, how does the above code work? I have two questions:
a and &a have the same values. Why?
What exactly does int (*p)[3]; do? It declares a pointer to an array, I know this. But how is a pointer to an array different from the pointer to the first element of the array and name of the array?
Can anyone clarify things up? I am having a hell of a lot of confusions.
I know that I should use %p as a placeholder instead of using %d for printing the value of pointer variables. As using the integer placeholder might print truncated addresses. But I just want to keep things simple.
Other answers already explained the issue. I am trying to explain it with some diagram. Hope this will help.
When you declare an array
int a[3] = {5, 4, 6}
the memory arrangement looks like
Now answering your question:
a and &a have the same values.How?
As you already know that a is of array type and array name a becomes a pointer to first element of array a (after decay),i.e it points to the address 0x100. Note that 0x100 also is the starting address of the memory block (array a). And you should know that, in general, the address of the first byte is said to be the address of the variable. That is, if a variable is of 100 bytes, then its address is equal to the address of its first byte.
&a is address of the entire memory block, i.e it is an address of array a. See the diagram:
Now you can understand why a and &a both have same address value although both are of different type.
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
See the above figure, it is explained clearly how pointer to an array is different from the pointer to an array element.
When you assign &a to p, then p points to the entire array having starting address 0x100.
NOTE: Regarding to the line
... as in C arrays are passed by references (with exception of sizeof function).
In C, arguments are passed by value. No pass by reference in C. When an ordinary variable is passed to a function, its value is copied; any changes to corresponding parameter do not affect the variable.
Arrays are also passed by value, but difference is that the array name decays to pointer to first element and this pointer assigned to the parameter (here, pointer value is copied) of the function; the array itself isn't copied.
In contrast to ordinary variable, an array used as an argument is not protected against any change, since no copy is made of the array itself, instead copy of pointer to first element is made.
You should also note that sizeof is not a function and array name does not act as an argument in this case. sizeof is an operator and array name serves as an operand. Same holds true when array name is an operand of the unary & operator.
a and &a have the same values.How?
They have the same value but different types. Array objects have no padding between elements (before or after) so the address of the array and the address of the first element of the array are the same.
That is:
(void *) a == (void *) &a
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
These are two different pointer types. Take for example, pointer arithmetic:
a + 1 /* address of the second element of the array */
&a + 1 /* address one past the last element of the array */
EDIT: due to popular demand I added below some information about conversion of arrays.
With three exceptions, in an expression an object of type array of T is converted to a value of type pointer to T pointing to the first element of the array. The exceptions are if the object is the operand of sizeof or & unary operator or if the object is a string literal initializing an array.
For example this statement:
printf("a:%d\t&a:%d\n", a, &a);
is actually equivalent to:
printf("a:%d\t&a:%d\n", &a[0], &a);
Also please note that d conversion specifier can only be use to print a signed integer; to print a pointer value you have to use p specifier (and the argument must be void *). So to do things correctly use:
printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);
respectively:
printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
a corresponds to the pointer pointing at 0th element of the array. Whereas,the same is the case with &a.It just gives the starting address of the array.
As,a --> pointer pointing to starting element of array a[],it does not know about other element's location..
&a --->address location for storing array a[] which stores first element location,but knows every element's location.
Similarly,other elements location will be (a+2),(a+4) and so upto the end of the array.
Hence,you got such result.
int (*p)[3] is a pointer to the array. had it been int *p[3],it would been meant entirely different. It'd have meant an array of pointers which would have been totally different from this context.
Pointer to an array will automatically take care of all the other
elements in the array.In this case,your's is (p);
Whereas,the pointer to the first element of the array,i.e., a will
only know about first element of the array.You'll have to manually
give pointer arithmetic directions to access next elements.See,in this
case---we can get second element from a by adding 2 to a,i.e.
a+2,third element by adding 4 to a,i.e., a+4 and so on. // mind the
difference of two as it is an integer array!
In answer to question 1, this is simply an aspect of the C language as designed, unlike most other modern languages C/C++ allows direct manipulation of addresses in memory and has built in facilities to 'understand' that. There are many articles online that explain this better than I could in this small space. Here is one and I am sure there are many others: http://www.cprogramming.com/tutorial/c/lesson8.html
From C99 Standard n1124 6.3.2.1 p3
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
a and &a have the same value because a long time ago you were required to use the address operator & on arrays to get the array's address, but it is no longer necessary. The name of the array (a in this case) these days just represents the memory address of the array itself, which is also what you get from &a. It's a shorthand that the compiler handles for you.
I am having a tough time understanding the type and use of the name of the array in C. It might seems a long post but please bear with me.
I understand that the following statement declares a to be of type int [] i.e array of integers.
int a[30];
While a also points the first element of array and things like *(a+2) are valid. Thus, making a look like a pointer to an integer. But actually the types int [] and int* are different; while the former is an array type and later is a pointer to an integer.
Also a variable of type int [] gets converted into a variable of type int* when passing it to functions; as in C arrays are passed by reference (with the exception of the sizeof operator).
Here comes the point which makes me dangle. Have a look at the following piece of code:
int main()
{
int (*p)[3];
int a[3] = { 5, 4, 6 };
p = &a;
printf("a:%d\t&a:%d\n",a,&a);
printf("%d",*(*p + 2));
}
OUTPUT:
a:2686720 &a:2686720
6
So, how does the above code work? I have two questions:
a and &a have the same values. Why?
What exactly does int (*p)[3]; do? It declares a pointer to an array, I know this. But how is a pointer to an array different from the pointer to the first element of the array and name of the array?
Can anyone clarify things up? I am having a hell of a lot of confusions.
I know that I should use %p as a placeholder instead of using %d for printing the value of pointer variables. As using the integer placeholder might print truncated addresses. But I just want to keep things simple.
Other answers already explained the issue. I am trying to explain it with some diagram. Hope this will help.
When you declare an array
int a[3] = {5, 4, 6}
the memory arrangement looks like
Now answering your question:
a and &a have the same values.How?
As you already know that a is of array type and array name a becomes a pointer to first element of array a (after decay),i.e it points to the address 0x100. Note that 0x100 also is the starting address of the memory block (array a). And you should know that, in general, the address of the first byte is said to be the address of the variable. That is, if a variable is of 100 bytes, then its address is equal to the address of its first byte.
&a is address of the entire memory block, i.e it is an address of array a. See the diagram:
Now you can understand why a and &a both have same address value although both are of different type.
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
See the above figure, it is explained clearly how pointer to an array is different from the pointer to an array element.
When you assign &a to p, then p points to the entire array having starting address 0x100.
NOTE: Regarding to the line
... as in C arrays are passed by references (with exception of sizeof function).
In C, arguments are passed by value. No pass by reference in C. When an ordinary variable is passed to a function, its value is copied; any changes to corresponding parameter do not affect the variable.
Arrays are also passed by value, but difference is that the array name decays to pointer to first element and this pointer assigned to the parameter (here, pointer value is copied) of the function; the array itself isn't copied.
In contrast to ordinary variable, an array used as an argument is not protected against any change, since no copy is made of the array itself, instead copy of pointer to first element is made.
You should also note that sizeof is not a function and array name does not act as an argument in this case. sizeof is an operator and array name serves as an operand. Same holds true when array name is an operand of the unary & operator.
a and &a have the same values.How?
They have the same value but different types. Array objects have no padding between elements (before or after) so the address of the array and the address of the first element of the array are the same.
That is:
(void *) a == (void *) &a
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
These are two different pointer types. Take for example, pointer arithmetic:
a + 1 /* address of the second element of the array */
&a + 1 /* address one past the last element of the array */
EDIT: due to popular demand I added below some information about conversion of arrays.
With three exceptions, in an expression an object of type array of T is converted to a value of type pointer to T pointing to the first element of the array. The exceptions are if the object is the operand of sizeof or & unary operator or if the object is a string literal initializing an array.
For example this statement:
printf("a:%d\t&a:%d\n", a, &a);
is actually equivalent to:
printf("a:%d\t&a:%d\n", &a[0], &a);
Also please note that d conversion specifier can only be use to print a signed integer; to print a pointer value you have to use p specifier (and the argument must be void *). So to do things correctly use:
printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);
respectively:
printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
a corresponds to the pointer pointing at 0th element of the array. Whereas,the same is the case with &a.It just gives the starting address of the array.
As,a --> pointer pointing to starting element of array a[],it does not know about other element's location..
&a --->address location for storing array a[] which stores first element location,but knows every element's location.
Similarly,other elements location will be (a+2),(a+4) and so upto the end of the array.
Hence,you got such result.
int (*p)[3] is a pointer to the array. had it been int *p[3],it would been meant entirely different. It'd have meant an array of pointers which would have been totally different from this context.
Pointer to an array will automatically take care of all the other
elements in the array.In this case,your's is (p);
Whereas,the pointer to the first element of the array,i.e., a will
only know about first element of the array.You'll have to manually
give pointer arithmetic directions to access next elements.See,in this
case---we can get second element from a by adding 2 to a,i.e.
a+2,third element by adding 4 to a,i.e., a+4 and so on. // mind the
difference of two as it is an integer array!
In answer to question 1, this is simply an aspect of the C language as designed, unlike most other modern languages C/C++ allows direct manipulation of addresses in memory and has built in facilities to 'understand' that. There are many articles online that explain this better than I could in this small space. Here is one and I am sure there are many others: http://www.cprogramming.com/tutorial/c/lesson8.html
From C99 Standard n1124 6.3.2.1 p3
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
a and &a have the same value because a long time ago you were required to use the address operator & on arrays to get the array's address, but it is no longer necessary. The name of the array (a in this case) these days just represents the memory address of the array itself, which is also what you get from &a. It's a shorthand that the compiler handles for you.
I am having a tough time understanding the type and use of the name of the array in C. It might seems a long post but please bear with me.
I understand that the following statement declares a to be of type int [] i.e array of integers.
int a[30];
While a also points the first element of array and things like *(a+2) are valid. Thus, making a look like a pointer to an integer. But actually the types int [] and int* are different; while the former is an array type and later is a pointer to an integer.
Also a variable of type int [] gets converted into a variable of type int* when passing it to functions; as in C arrays are passed by reference (with the exception of the sizeof operator).
Here comes the point which makes me dangle. Have a look at the following piece of code:
int main()
{
int (*p)[3];
int a[3] = { 5, 4, 6 };
p = &a;
printf("a:%d\t&a:%d\n",a,&a);
printf("%d",*(*p + 2));
}
OUTPUT:
a:2686720 &a:2686720
6
So, how does the above code work? I have two questions:
a and &a have the same values. Why?
What exactly does int (*p)[3]; do? It declares a pointer to an array, I know this. But how is a pointer to an array different from the pointer to the first element of the array and name of the array?
Can anyone clarify things up? I am having a hell of a lot of confusions.
I know that I should use %p as a placeholder instead of using %d for printing the value of pointer variables. As using the integer placeholder might print truncated addresses. But I just want to keep things simple.
Other answers already explained the issue. I am trying to explain it with some diagram. Hope this will help.
When you declare an array
int a[3] = {5, 4, 6}
the memory arrangement looks like
Now answering your question:
a and &a have the same values.How?
As you already know that a is of array type and array name a becomes a pointer to first element of array a (after decay),i.e it points to the address 0x100. Note that 0x100 also is the starting address of the memory block (array a). And you should know that, in general, the address of the first byte is said to be the address of the variable. That is, if a variable is of 100 bytes, then its address is equal to the address of its first byte.
&a is address of the entire memory block, i.e it is an address of array a. See the diagram:
Now you can understand why a and &a both have same address value although both are of different type.
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
See the above figure, it is explained clearly how pointer to an array is different from the pointer to an array element.
When you assign &a to p, then p points to the entire array having starting address 0x100.
NOTE: Regarding to the line
... as in C arrays are passed by references (with exception of sizeof function).
In C, arguments are passed by value. No pass by reference in C. When an ordinary variable is passed to a function, its value is copied; any changes to corresponding parameter do not affect the variable.
Arrays are also passed by value, but difference is that the array name decays to pointer to first element and this pointer assigned to the parameter (here, pointer value is copied) of the function; the array itself isn't copied.
In contrast to ordinary variable, an array used as an argument is not protected against any change, since no copy is made of the array itself, instead copy of pointer to first element is made.
You should also note that sizeof is not a function and array name does not act as an argument in this case. sizeof is an operator and array name serves as an operand. Same holds true when array name is an operand of the unary & operator.
a and &a have the same values.How?
They have the same value but different types. Array objects have no padding between elements (before or after) so the address of the array and the address of the first element of the array are the same.
That is:
(void *) a == (void *) &a
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
These are two different pointer types. Take for example, pointer arithmetic:
a + 1 /* address of the second element of the array */
&a + 1 /* address one past the last element of the array */
EDIT: due to popular demand I added below some information about conversion of arrays.
With three exceptions, in an expression an object of type array of T is converted to a value of type pointer to T pointing to the first element of the array. The exceptions are if the object is the operand of sizeof or & unary operator or if the object is a string literal initializing an array.
For example this statement:
printf("a:%d\t&a:%d\n", a, &a);
is actually equivalent to:
printf("a:%d\t&a:%d\n", &a[0], &a);
Also please note that d conversion specifier can only be use to print a signed integer; to print a pointer value you have to use p specifier (and the argument must be void *). So to do things correctly use:
printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);
respectively:
printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
a corresponds to the pointer pointing at 0th element of the array. Whereas,the same is the case with &a.It just gives the starting address of the array.
As,a --> pointer pointing to starting element of array a[],it does not know about other element's location..
&a --->address location for storing array a[] which stores first element location,but knows every element's location.
Similarly,other elements location will be (a+2),(a+4) and so upto the end of the array.
Hence,you got such result.
int (*p)[3] is a pointer to the array. had it been int *p[3],it would been meant entirely different. It'd have meant an array of pointers which would have been totally different from this context.
Pointer to an array will automatically take care of all the other
elements in the array.In this case,your's is (p);
Whereas,the pointer to the first element of the array,i.e., a will
only know about first element of the array.You'll have to manually
give pointer arithmetic directions to access next elements.See,in this
case---we can get second element from a by adding 2 to a,i.e.
a+2,third element by adding 4 to a,i.e., a+4 and so on. // mind the
difference of two as it is an integer array!
In answer to question 1, this is simply an aspect of the C language as designed, unlike most other modern languages C/C++ allows direct manipulation of addresses in memory and has built in facilities to 'understand' that. There are many articles online that explain this better than I could in this small space. Here is one and I am sure there are many others: http://www.cprogramming.com/tutorial/c/lesson8.html
From C99 Standard n1124 6.3.2.1 p3
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
a and &a have the same value because a long time ago you were required to use the address operator & on arrays to get the array's address, but it is no longer necessary. The name of the array (a in this case) these days just represents the memory address of the array itself, which is also what you get from &a. It's a shorthand that the compiler handles for you.
I am having a tough time understanding the type and use of the name of the array in C. It might seems a long post but please bear with me.
I understand that the following statement declares a to be of type int [] i.e array of integers.
int a[30];
While a also points the first element of array and things like *(a+2) are valid. Thus, making a look like a pointer to an integer. But actually the types int [] and int* are different; while the former is an array type and later is a pointer to an integer.
Also a variable of type int [] gets converted into a variable of type int* when passing it to functions; as in C arrays are passed by reference (with the exception of the sizeof operator).
Here comes the point which makes me dangle. Have a look at the following piece of code:
int main()
{
int (*p)[3];
int a[3] = { 5, 4, 6 };
p = &a;
printf("a:%d\t&a:%d\n",a,&a);
printf("%d",*(*p + 2));
}
OUTPUT:
a:2686720 &a:2686720
6
So, how does the above code work? I have two questions:
a and &a have the same values. Why?
What exactly does int (*p)[3]; do? It declares a pointer to an array, I know this. But how is a pointer to an array different from the pointer to the first element of the array and name of the array?
Can anyone clarify things up? I am having a hell of a lot of confusions.
I know that I should use %p as a placeholder instead of using %d for printing the value of pointer variables. As using the integer placeholder might print truncated addresses. But I just want to keep things simple.
Other answers already explained the issue. I am trying to explain it with some diagram. Hope this will help.
When you declare an array
int a[3] = {5, 4, 6}
the memory arrangement looks like
Now answering your question:
a and &a have the same values.How?
As you already know that a is of array type and array name a becomes a pointer to first element of array a (after decay),i.e it points to the address 0x100. Note that 0x100 also is the starting address of the memory block (array a). And you should know that, in general, the address of the first byte is said to be the address of the variable. That is, if a variable is of 100 bytes, then its address is equal to the address of its first byte.
&a is address of the entire memory block, i.e it is an address of array a. See the diagram:
Now you can understand why a and &a both have same address value although both are of different type.
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
See the above figure, it is explained clearly how pointer to an array is different from the pointer to an array element.
When you assign &a to p, then p points to the entire array having starting address 0x100.
NOTE: Regarding to the line
... as in C arrays are passed by references (with exception of sizeof function).
In C, arguments are passed by value. No pass by reference in C. When an ordinary variable is passed to a function, its value is copied; any changes to corresponding parameter do not affect the variable.
Arrays are also passed by value, but difference is that the array name decays to pointer to first element and this pointer assigned to the parameter (here, pointer value is copied) of the function; the array itself isn't copied.
In contrast to ordinary variable, an array used as an argument is not protected against any change, since no copy is made of the array itself, instead copy of pointer to first element is made.
You should also note that sizeof is not a function and array name does not act as an argument in this case. sizeof is an operator and array name serves as an operand. Same holds true when array name is an operand of the unary & operator.
a and &a have the same values.How?
They have the same value but different types. Array objects have no padding between elements (before or after) so the address of the array and the address of the first element of the array are the same.
That is:
(void *) a == (void *) &a
What exactly it does int (*p)[3]; Declares a pointer to an array,i know this.But,how a pointer to an array is different from the pointer to the first element of the array and name of the array?
These are two different pointer types. Take for example, pointer arithmetic:
a + 1 /* address of the second element of the array */
&a + 1 /* address one past the last element of the array */
EDIT: due to popular demand I added below some information about conversion of arrays.
With three exceptions, in an expression an object of type array of T is converted to a value of type pointer to T pointing to the first element of the array. The exceptions are if the object is the operand of sizeof or & unary operator or if the object is a string literal initializing an array.
For example this statement:
printf("a:%d\t&a:%d\n", a, &a);
is actually equivalent to:
printf("a:%d\t&a:%d\n", &a[0], &a);
Also please note that d conversion specifier can only be use to print a signed integer; to print a pointer value you have to use p specifier (and the argument must be void *). So to do things correctly use:
printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);
respectively:
printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);
a corresponds to the pointer pointing at 0th element of the array. Whereas,the same is the case with &a.It just gives the starting address of the array.
As,a --> pointer pointing to starting element of array a[],it does not know about other element's location..
&a --->address location for storing array a[] which stores first element location,but knows every element's location.
Similarly,other elements location will be (a+2),(a+4) and so upto the end of the array.
Hence,you got such result.
int (*p)[3] is a pointer to the array. had it been int *p[3],it would been meant entirely different. It'd have meant an array of pointers which would have been totally different from this context.
Pointer to an array will automatically take care of all the other
elements in the array.In this case,your's is (p);
Whereas,the pointer to the first element of the array,i.e., a will
only know about first element of the array.You'll have to manually
give pointer arithmetic directions to access next elements.See,in this
case---we can get second element from a by adding 2 to a,i.e.
a+2,third element by adding 4 to a,i.e., a+4 and so on. // mind the
difference of two as it is an integer array!
In answer to question 1, this is simply an aspect of the C language as designed, unlike most other modern languages C/C++ allows direct manipulation of addresses in memory and has built in facilities to 'understand' that. There are many articles online that explain this better than I could in this small space. Here is one and I am sure there are many others: http://www.cprogramming.com/tutorial/c/lesson8.html
From C99 Standard n1124 6.3.2.1 p3
Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
a and &a have the same value because a long time ago you were required to use the address operator & on arrays to get the array's address, but it is no longer necessary. The name of the array (a in this case) these days just represents the memory address of the array itself, which is also what you get from &a. It's a shorthand that the compiler handles for you.
Up till now I was pretty much sure that
int arr[4][5];
Then arr will decay to pointer to pointer.
But this link proves me wrong.
I am not sure how did I get about arr being pointer to pointer but it seemed pretty obvious to me. Because arr[i] would be a pointer, and hence arr should be a pointer to pointer.
Am I missing out on something.
Yep you are missing out on a lot :)
To avoid another wall of text I'll link to an answer I wrote earlier today explaining multi-dimensional arrays.
With that in mind, arr is a 1-D array with 4 elements, each of which is an array of 5 ints.
When used in an expression other than &arr or sizeof arr, this decays to &arr[0]. But what is &arr[0]? It is a pointer, and importantly, an rvalue.
Since &arr[0] is a pointer, it can't decay further. (Arrays decay, pointers don't). Furthermore, it's an rvalue. Even if it could decay into a pointer, where would that pointer point? You can't point at an rvalue. (What is &(x+y) ? )
Another way of looking at it is to remember that int arr[4][5]; is a contiguous bloc of 20 ints, grouped into 4 lots of 5 within the compiler's mind, but with no special marking in memory at runtime.
If there were "double decay" then what would the int ** point to? It must point to an int * by definition. But where in memory is that int * ? There are certainly not a bunch of pointers hanging around in memory just in case this situation occurs.
A simple rule is:
A reference to an object of type array-of-T which appears in an expression decays (with three exceptions) into a pointer to its first element; the type of the resultant pointer is pointer-to-T.
When you deal with 1D array, array name converts to pointer to first element when passed to a function.
A 2D array can be think as of an array of arrays. In this case int arr[4][5];, you can think arr[] as an array name and when passed to a function then converts to a pointer to the first element of array arr. Since first element of arr is an array, arr[i] is a pointer to ith row of the array and is of type pointer to array of 5 ints.
In general, a 2-dim array is implemented as an array of pointers (which, in a sense, is a pointer to a pointer... a pointer to the first element (i.e., pointer) in the array) When you specify the first index (i.e., arr[x]), it indexes into the array of pointers and gives the pointer to the x-th row. The the second index (i.e., arr[x][y]) gives the y-th int in that row.
In the case of a static declared array (as in your example), the actual storage is allocated as a single block ... in your example, as a single contiguous block of 20 integers (80 bytes, on most platforms). In this case, there IS no array of pointers, the compiler just does the appropriate arithmetic to address the correct element of the array. Specifically, arr[x][y] is equivalent to *(arr + x * 5 + y). This automatically-adjusted-arithmetic only happens in the original scope of the array... if you pass the array to a function, the dimension information is lost (just as the dimension is lost for a 1-dim array), and you have to do the array-indexing calculations explicitly.
To avoid this, do NOT declare the array as static, but as an array of pointers, with each pointer pointed to a 1-dim array, such as in this example:
int arr0[5];
int arr1[5];
int arr2[5];
int arr3[5];
int* arr[4] = { arr0, arr1, arr2, arr3 };
Then, when you pass arr to a function, you can address it as a 2-dim array within the function as well.