In this code:
#include<stdio.h>
int main()
{
int num[2] = {20, 30};
printf("%d", num);
printf("%d", &num[0]);
return 0;
}
As far as I know, both the printf statement will print the address of the first element in num
because in the first statement, num
is a pointer to an int.
But if num
is a pointer, then it should also have any address but on printing its address (with printf("%d", &num)
), it's showing the address of the first element.
In a 2-D array the whole thing becomes confusing too:
#include<stdio.h>
int main(void)
{
int num[ ] [2]={20,30,40,50};
printf("%d",*num);
return 0;
}
This program is printing the address of zeroth element that is the address of num[0][0]
. But why does it do this? Why isn't it printing the value stored in it, since they all have same address( num,num[0]
and num[0][0]
)?
First things first; array variables are not pointers; they do not store an address to anything.
For a declaration such as
T a[N];
memory will be laid out as
+---+
a[0]: | |
+---+
a[1]: | |
+---+
...
+---+
a[N-1]: | |
+---+
For a 2D MxN array, it will look like
+---+
a[0][0]: | |
+---+
a[0][1]: | |
+---+
...
+---+
a[0][N-1]: | |
+---+
a[1][0]: | |
+---+
a[1][1]: | |
+---+
...
+---+
a[M-1][N-1]: | |
+---+
The pattern should be obvious for 3D and higher arrays.
As you can see, no storage is set aside for a separate variable a
that contains the address of the first element; instead, there is a rule in the C language that an expression of type "N-element array of T
" will be converted ("decay") to an expression of type "pointer to T
" and the value of the expression will be the address of the first element of the array, except when the array expression is one of the following:
sizeof
operator &
operator _Alignof
operator (C99 and later) So given the declaration
T a[N];
all of the following are true:
Expression Type Decays to Value
---------- ---- --------- -----
a T [N] T * address of first element, &a[0]
*a T n/a value stored in first element
&a T (*)[N] n/a address of the array, which is
the same as the address of the
first element of the array
a[i] T n/a value stored in the i'th element
&a[i] T * n/a address of the i'th element
sizeof a size_t n/a total number of bytes used by the
array
sizeof *a size_t n/a total number of bytes used by the
first element of the array
sizeof &a size_t n/a total number of bytes used by a
pointer to the array
The expression a
has type "N-element array of T
"; it is not the operand of the unary &
or sizeof
operators, so it is converted to a pointer to the first element of the array, amd its value is the address of that element.
The expression &a
has type "pointer to N-element array of T
"; since a
is an operand of the unary &
operator, the conversion rule above isn't applied (which is why the expression has type T (*)[N]
instead of T **
). However, since the address of the array is the same as the address of the first element of the array, it yields the same value as the expression a
.
The expression &a[0]
has type "pointer to T
", and explicitly points to the first element of the array. Again, this value will be the same as the previous two expressions.
For a 2D array
T a[M][N];
all of the following are true:
Expression Type Decays to Value
---------- ---- --------- -----
a T [M][N] T (*)[N] address of first subarray, a[0]
*a T [N] T * address pf first subarray, a[0]
&a T (*)[M][N] n/a address of the array, which is
the same as the address of the
first subarray, which is the same
as the address of the first element
of the first subarray.
a[i] T [N] T * address of first element of i'th
subarray
*a[i] T n/a value of first element of i'th subarray
&a[i] T (*)[N] n/a address of the i'th subarray
sizeof a size_t n/a total number of bytes used by the
array
sizeof *a size_t n/a total number of bytes used by the
first subarray
sizeof &a size_t n/a total number of bytes used by a
pointer to the array
Final note: to print out pointer values, use the %p
conversion specifier and cast the argument to (void *)
(this is the pretty much the only time it's considered proper to explicitly cast a pointer to void *
):
printf( " &a yields %p\n", (void *) &a );
printf( " a yields %p\n", (void *) a );
printf( "&a[0] yields %p\n", (void *) &a[0] );
Edit
To answer a question in the comments:
num,num[] and num[][] are all different thing. There types are different.Here num decays and became pointer to a pointer and num[] decays and became pointer to int and num[][] is a int. Right?
Not quite.
Assuming a declaration like
int arr[10][10];
then the expression arr
will decay to type int (*)[10]
(pointer to 10-element array of int
), not int **
; refer to the table above again. Otherwise you're right; arr[i]
will decay to type int *
, and arr[i][j]
will have type int
.
An expression of type "N-element array of T
" decays to type "pointer to T
"; if T
is an array type, then the result is "pointer to array", not "pointer to pointer".
In the second example, num
is a 2 dimensional array, or say an array of array. It's true that *num
is its first element, but this first element is an array itself.
To get num[0][0]
, you need **num
.
printf("%d\n", **num);
Look how an array looks like:
int num[ ] [2]={20,30,40,50};
is better written as
int num[][2]={{20,30},{40,50}};
It is an array with 2 elements. Those 2 elements are, again, arrays with 2 ints.
In memory, they look like
20 30 40 50
but the difference is that num
refers to the whole array, num[0]
to the first "part- array" and num[0][0]
to the first element of the first array.
They have the same address (because they start at the same place), but they have a different type.
That is, the address is not the only important thing with a pointer, the type is important as well.
Arrays are not pointers actually, though they tend to act in a bit similar way, but not always.
Say you have this array and a pointer:
int a[] = {1, 2, 3};
int i = 19;
int *ptr = &i;
Now here a is equal to &a
, but the same is not true, for pointers ( ptr is not equal to &ptr
).
Now coming to the question:
Consider a single dimensional array:
int arr[] = {11, 19, 5, 9};
Here, this array elements are stored in contiguous memory locations. Say, with starting address 0
:
---------------------
| 11 | 19 | 5 | 9 |
---------------------
0 4 8 12 16
Now when you write name of the array, arr (for this example), you will get the starting address of the 1 st element. Though if you write &arr
, then you get the starting address of the whole block(this includes all the elements of the array). Now when you write *arr
, you actually get the value inside the 1 st element of this array.
Now consider this 2-dimensional array arr[][4] = {{11, 19, 5, 9}, {5, 9, 11, 19}}:
0 4 8 12 16 -> These are memory addresses
---------------------
| 11 | 19 | 5 | 9 | ----> These values represent the values inside each index
---------------------
| 5 | 9 | 11 | 19 |
---------------------
16 20 24 28 32
Here, when you write the name of the array, as arr
, what you get is the address of the 1 st element of this array, which in this case will be address of this 0 th index:
0 16 32
----------------------------------------------
| 0<sup>th</sup> index | 1<sup>st</sup> index |
----------------------------------------------
Now when you do &arr
, here what you get is the base address for whole of the block, ie base address of this:
0 4 8 12 16
---------------------
| 11 | 19 | 5 | 9 |
---------------------
| 5 | 9 | 11 | 19 |
---------------------
16 20 24 28 32
Now, if you do *arr
, in 1-dimensional array it gives you the value inside the 1 st element, though in 2-dimensional array, the value inside each index is actually one 1-dimensional array, hence you will get the address of this array:
0 4 8 12 16
---------------------
| 11 | 19 | 5 | 9 |
---------------------
Now if you do **arr
, that is when you will actually get the value inside the 1 st element, which is 11
.
I hope it clears some doubts :-)
EDIT 1:
As brought to my attendtion, by fellow user, it seems there is a bit of a confusion somewhere, though I have explained in detail what is meant by what thingy. But just to justify, for this statement:
Now here __a is equal to &a__, but the same is not true, for pointers (__ptr is not equal to &ptr__).
The types of both a
and &a
will be different, as already stated, in the answer. If one performs pointer arithmetics, one will able to know that. Try performing a + 1
and &a + 1
, how they both react to pointer arithmetics will surely give a good idea.
Considering a 1-dimensional array:
int arr[] = {11, 19, 5, 9};
---------------------
| 11 | 19 | 5 | 9 |
---------------------
0 4 8 12 16
We cannot do a++
, though for a pointer:
int i = 4;
int *ptr = &i;
we can perform ptr++
, this will make ptr
point to the next memory location.
I think it result means that the array not really a pointer, but it is converted to a pointer in some contexts that is expected a pointer, like pass to a function that expect a pointer argument.
see this code:
void test(int* num) {
printf("test\n");
printf("%p\n",num);
printf("%p\n",&num);
printf("%p\n",&num[0]);
}
int main(){
int num[2]={20,30};
test(num);
printf("main\n");
printf("%p\n",num);
printf("%p\n",&num);
printf("%p\n",&num[0]);
//other();
return 0;
}
The output is:
test
0x7fff7a422300
0x7fff7a4222e8 //LOOK THIS! Is diferent from main!
0x7fff7a422300
main
0x7fff7a422300
0x7fff7a422300
0x7fff7a422300
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.