I was expecting a garbage value from the first printf() statement but I got the address instead. Why?
#include<stdio.h>
int main()
{
int (*p)[10];
int a[10]={1,2,3,4,5,6,7,8,9,0};
p=a+10;
printf("%d\n",*(p));
printf("%u",(p));
}
I was expecting a garbage value from the first printf() statement but I got the address instead. Why?
#include<stdio.h>
int main()
{
int (*p)[10];
int a[10]={1,2,3,4,5,6,7,8,9,0};
p=a+10;
printf("%d\n",*(p));
printf("%u",(p));
}
Given this declaration:
int (*p)[10];
p
is a pointer to an array. The expression *p
therefore designates the pointed-to array itself. But in almost all C contexts, including in particular when it is an argument in a function call expression, the value of an expression of array type is automatically converted to a pointer to the first element of the array. The address of that element is the same as the address of the array itself, so if p
is in fact a valid pointer then it is entirely reasonable for
printf("%p\n", (void *) (*p));
printf("%p\n", (void *) (p));
to print two identical lines.
Do note, however, the use of the %p
format directive instead of %u
or %d
for printing a (void) pointer value, and the casts to void *
. These changes to your original code are necessary for the behavior of the printf
calls to be well defined.
It is true that as far as the C language is concerned, *p
does not designate an object in your case, so in that sense, evaluating the expression *p
produces undefined behavior. This is one of the reasons why I say only that it is "reasonable" for the code to emit two identical lines. In practice, however, implementations are likely to treat the expression *p
identically to (int *) p
, whose behavior is well-defined in this case.
You should also be aware -- and your compiler should be warning you -- that the expression a + 10
has the wrong type (int *
) to be assigned directly to your p
(an int (*)[10]
). It is allowed to convert between those two types, but a cast is required:
p = (int (*)[10])(a + 10);
or
p = (void *)(a + 10);
There is good reason to expect that your compiler is compiling the original assignment expression as if the required cast were present, especially inasmuch as it is a no-op in many implementations, probably including yours, but you should not rely on that.
For starters this statement
p=a+10;
is invalid.
The left side of the assignment has the type int( * )[10]
while the right side of the assignment has the type int *
and there is no implicit conversion from one type to another.
You have to write
p = ( int( * )[10] )( a + 10 );
Secondly these calls of printf
printf("%d\n",*(p));
printf("%u",(p));
have undefined behavior because there are used invalid conversion specifier d
and u
with arguments of pointer types because *p
and p
are both pointers.
So a valid program can look the following way
#include <stdio.h>
int main(void)
{
int (*p)[10];
int a[10]={1,2,3,4,5,6,7,8,9,0};
p = ( int( * )[10] )( a + 10 );
printf("%p\n",( void * )*p );
printf("%p\n", (void *)p );
return 0;
}
A pointer may point to the memory that follows the last element of an object (or an array).
So after this statement
p = ( int( * )[10] )( a+10 );
the pointer p
points after the first element of an imagined two-dimensional array elements of which in turn one-dimensional arrays of the type int[10]
and the first element of the imagined two-dimensional array corresponds to the array a
.
The expression *p
is also a pointer. Its type is int *
because the expression *p gives an lvalue of the type int[10]
and in expressions this lvalue is implicitly converted to pointer to its first element (in this case of the type int
).
So the both expressions p
and *p
points to the same memory and have the same value.
This is demonstrated by the program output
0x7ffcfd46ea48
0x7ffcfd46ea48
The difference between these two pointers, p
and *p
, is the type of an object they potentially point to.
Consider the following demonstrative program
#include <stdio.h>
int main(void)
{
int (*p)[10];
int a[10]={1,2,3,4,5,6,7,8,9,0};
p = ( int( * )[10] )( a+10 );
printf("%zu\n",sizeof( *p ) );
printf("%zu\n",sizeof( **p ) );
return 0;
}
Its output is
40
4
You could get undefined behavior if you wrote
printf( "%d\n", **p );
In this case the expression **p
has the type int
and there is an access to the memory beyond the array a
.
The array can be treated as (decays to) a pointer to the first element.
When you take the address of an array, you generate a pointer to the array, generally to the same address as first element
When you dereference the pointer to array, you get pointer to element, which generally has the same actual numeric value.
I say generally, because some strange C emulator, or some system that puts an extra indirection in all pointer objects, might decide that the array and the content of the array are 2 different objects.