I am studying pointers in C and have written the following code:
int main() {
int a = 10;
int *pa = &a;
printf("Value of pa : %pn", pa); // 0x7fffd215b5b8
printf("Value of *pa : %dn", *pa); // 10
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int (*parr)[10] = &arr;
printf("Address of arr : %pn", &arr); // 0x7fffd215b5d0
printf("Address of parr : %pn", &parr); // 0x7fffd215b5c0
printf("Value of parr : %pn", parr); // 0x7fffd215b5d0
printf("Value of *parr : %pn", (*parr)); // 0x7fffd215b5d0 <---WHY?
printf("Value of first element : %dn", **parr); //1
// In order to print elements using pointer parr
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
printf("%dt", *((*parr)+i)); // 1 2 3 ... values stored in arr
return 0;
}
For the pointer pa
, the value that it holds is the address of the variable a
. When we do *pa
we are de-referencing the (value of p
)/(address of a
) i.e *(0x7fffd215b5b8)
which is equal to 10
.
For pointer parr
, the value that it holds is the base address of an array of 10 elements (0x7fffd215b5d0
). Shouldn’t *parr
i.e. *(0x7fffd215b5d0)
de-reference the value that it contains similar to what *pa
did previously and output 1
? Why does *parr
display the value that it contains i.e. base address of arr
?
And why do I need to de-reference the pointer twice to get the values for the array (I have managed to get the output by trial and error and don’t really understand why it’s supposed to work this way). The pointer *parr
that I have initialized is a single pointer and not a double pointer right?
3
An array decays into a pointer to its first element.
First, the basics.
arr
is an array.
It has type int [10]
.
The array is located at 0x7fffd215b5d0
.
parr
is a pointer to an array.
It has type int (*)[10]
.
The pointer is located at 0x7fffd215b5c0
.
Its value is 0x7fffd215b5d0
.
*parr
is an array.
It has type int [10]
.
The array is located at 0x7fffd215b5d0
.
Now on to the question.
Except in a couple of very specific situations (&a
, sizeof a
, typeof a
), using an array doesn’t produce the array but a pointer to its first element. We say it decays into a pointer to its first element.
arr
decays into a pointer to its first element.
It has type int *
.
Its value is 0x7fffd215b5d0
.
*parr
decays into a pointer to its first element.
It has type int *
.
Its value is 0x7fffd215b5d0
.
The code in question is equivalent to the following:
printf("Value of *parr : %pn", &((*parr)[0]));
Using pointers to arrays complicates things. It’s more common to use pointers to the first element of arrays.
int *pele = arr; // Same as `&(arr[0])`
printf("Value of first element : %dn", *pele); // 1
printf("Value of first element : %dn", pele[0]); // 1
More complete demo:
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int (*parr)[10] = &arr;
int *pele = arr; // Same as `&(arr[0])`
printf( "Address of arr: %pn", (void*)&arr );
printf( "Address of parr: %pn", (void*)&parr );
printf( "Value of parr: %pn", (void*)parr );
printf( "Address of pele: %pn", (void*)&pele );
printf( "Value of pele: %pn", (void*)pele );
printf( "n" );
printf( "Address of first element:n" );
printf( "via decay of arr: %pn", (void*)arr );
printf( "via decay of *parr: %pn", (void*)*parr );
printf( "via pele: %pn", (void*)pele );
printf( "n" );
printf( "Value of first element:n" );
printf( "*p, via decay of arr: %dn", *arr );
printf( "*p, via decay of *parr: %dn", **parr );
printf( "*p, via pele: %dn", *pele );
printf( "p[0], via decay of arr: %dn", arr[0] );
printf( "p[0], via decay of *parr: %dn", (*parr)[0] );
printf( "p[0], via pele: %dn", pele[0] );
In response to comment, a little about dynamically allocating 2d arrays.
A common convention to allocate an array dynamically is to use
Type *p = malloc( n * sizeof *p );
It’s no exception with 2d arrays.
// Non-dynamic.
int a[rows][cols]; a[row][col]
// Same, but saves decayed pointer.
int a[rows][cols]; int (*p)[cols] = a; p[row][col]
// Dynamic.
int (*p)[cols] = malloc( rows * sizeof *p ); p[row][col]
p
is a int (*)[cols]
, so
*p
is a int [cols]
, so
sizeof *p
is the size of a int [cols]
, so
sizeof *p
is equal to cols * sizeof int
, so
rows * sizeof *p
is equivalent to rows * cols * sizeof int
.
6
printf("Value of *parr : %pn", (*parr)); // 0x7fffd215b5d0 <---WHY?
parr
is a pointer to an array, so *parr
is the array itself. When an array appears in an expression, the actual value used by the compiler is the address of the first element, this is &(*parr)[0]
, so you get the first element address.
Beware that &arr
and arr
are pointers pointing to the same place, but they are different typed pointers (first is the address of an array of 10 int
s and the second is a pointer to int
)
In C, an array does not have a value that can be printed. Mathematically, the value of an array of n elements would be an ordered n-tuple of values, but C does not provide any way to work with ordered tuples. The elements of an array must be accessed individually, using subscripting or pointers.
To assist programmers with this, when an array appears in an expression, it is automatically converted to a pointer to its first element, unless the array is the operand of sizeof
, is the operand of unary &
, or is a string literal used to initialize an array.
In printf("Value of *parr : %pn", (*parr));
, parr
is a pointer to an array, so *parr
is an array. Since *parr
is an array, and is not in one of the exceptions listed above, it is automatically converted to the address of the first element of the array, and that address is what is printed.
In printf("Value of first element : %dn", **parr);
, *parr
is converted to a pointer to the first element of the array, and then **parr
dereferences that pointer to provide the first element of the array.
An array is already pointing toward the values it contains.
arr[0] == *arr;
If *parr = &arr
, it points towards a pointer that points towards others values.
So double pointing to point on the first value.
a contains only the value. arr contains pointers to every values.
What you correctly understand is that address is representing a section in memory where something is stored. A pointer to an address is representing the address and the value can be inferred via dereferencing it.
So, your a
variable is located at the address of &a
and has a value of 10
. Therefore, pa
has the value of the address of a
and it can be dereferenced via *a
, which will result in 10.
arr
is an array and an array is a section in memory, whose items are sections in the memory associated to this array.
arr[0]
, for example is the first element, its value can also be inferred via *a
. arr[1]
is the second element, its value can also be inferred via *(a + 1))
.
So each element of the array can be inferred as a pointer with the formula of *(a + offset)
and it is equivalent to a[offset]
.
So, when you create a pointer to this array, which you call parr
, *parr
is pointing to the array. So, the value of this is the array and in order to infer a value of the array, *parr
is not enough, because it’s not specific-enough to know which element you mean. **parr
is dereferencing the array pointer and dereferencing the first element, as it is equivalent to *(*(parr + 0))
and of course, you can change the offset from 0 to the offset that you want to infer.
In short, a pointer to an array points to the array, not to its first element and since arr
is not equivalent to arr[0]
or *arr
, *parr
does refer to the array and not to its element.
I’ll suggest you during your starting with pointers to make the notation a bit simpler:
int arr[] = {1,2,3,4,5,6,7,8,9,10}; // Array of numbers
int* parr[10] = &arr; // Array of pointers
Now, the assign is a bit in correct because on arrays that &arr means the address that the array starts.
So for a single element it should be:
int* parr = &arr; // pointer of first integer
Now to get the pointer of each element you have to move sizeof(int) to next element
so:
*parr -> 1
parr = parr + sizeof(int)
*parr -> 2
as you do on the for loop
In your case you are printing the value of the pointer, just remove the ()
3