I am posting another example, a complete C program, that may help to see the logic behind this
First, some details
I'm still confused about the 'Ptr = *data' not being a de-reference.
Well, it is a de-reference, the *
in an assignment is a de-reference in C. BUT *data
is not int
: it is int[4][3]
and holds the address of the first element of the array, and it points to an int
whose is 23
. One level of indirection was lost in your code.
Ptr is int*
*Ptr is an address, &data[0][0] or simply *data
**Ptr is 23
Key to understand this is the line
Ptr = *data;
See what gcc says when compiling this:
toninho@DSK-2009:~/projects/dsp$ gcc -o tptr -Wall -Wextra -std=c17 tptr.c
tptr.c: In function ‘main’:
tptr.c:19:9: warning: assignment to ‘int *’ from\
incompatible pointer type int (*)[4][3]’ [-Wincompatible-pointer-types]
19 | Ptr = &data;
| ^
The Microsoft compiler says:
1>------ Build started: Project: sop-0328-a, Configuration: Debug Win32 ------
1>ptr.c
1>C:\Users\toninho\source\repos\sop-0328-a\sop-0328-a\ptr.c(19,20):
warning C4047: '=': 'int *' differs in levels of indirection from 'int (*)[4][3]'
1>sop-0328-a.vcxproj -> C:\Users\toninho\source\repos\sop-0328-a\Debug\sop-0328-a.exe
1>Done building project "sop-0328-a.vcxproj".
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
clang compiler says:
C:\Users\toninho\source\repos\sop-0328-a\sop-0328-a>clang -Wall ptr.c
ptr.c:19:13: warning: incompatible pointer types assigning
to 'int *' from 'int (*)[4][3]'
[-Wincompatible-pointer-types]
Ptr = &data;
^ ~~~~~
1 warning generated.
And it is the same. You should always enable all warnings, on all compilers.
I don't understand this on a conceptual level. I the see ' * ' and I
think de-reference.
Saying Ptr = *(data + 0), doesn't help, to me it still means
de-reference and we end up de-referencing the address 'data'.
Can anyone shed some light and help me understand?
As I said, it is a de-reference. Problem is you passed a int*[4][3]
to an int*
, did not have the compiler warnings enabled and :) was not aware of this thing. Professionals do that all the time and sometimes get surprised too.
You should write Ptr = (int*) *data;
I believe the program below can show it better. Adding to 0 sure made no difference. You would need to add another *
Please see the code below and ask back if it is still not clear.
Note that at the end of the code the lines
pointer = (int*) &many;
printf("Using a cast 'pointer' now points to '(int*) many' \
and its value is %d\n", *pointer);
uses a cast and prints
Using a cast 'pointer' now points to '(int*) many' and its value is 23
and now he compiler is happy and the output is the expected 23
Example
The output of the program is
'one' is an int at address 00EFFDB4
'many'is int[4][3] at address 00EFFD7C
'pointer' is int* at address 00EFFDC0
'pointer' now points to 'one' and its value is 00EFFDB4
'pointer' now points to 'many' and its value is 00EFFD7C
'many' is a C array, a pointer pointing to the address 00EFFD7C ( &many[0][0] )
'many' content, an address, is 00EFFD7C ( *many )
Starting at this location --- 00EFFD7C ( *many ) --- we have the values of the array
First value is 23 ( **many ),
Second value is 55 ( *(1 + *many)... )
Usng a cast 'pointer' now points to '(int*) many' and its value is 23
And you can see the addresses on the debugger screen, along with the actual types of the variables (program stopped at line #23):

The code
#include <stdio.h>
int main(void)
{
int* pointer = NULL;
int one = 1;
int many[4][3] =
{
{23,55,50},
{45,38,55},
{70,43,45},
{34,46,60}
};
printf("'one' is an int at address\t%p\n", &one);
printf("'many'is int[4][3] at address\t%p\n", &many);
printf("'pointer' is int* at address\t%p\n\n", &pointer);
pointer = &one;
printf("'pointer' now points to 'one' and its value is\t%p\n", pointer);
pointer = &many;
printf("'pointer' now points to 'many' and its value is\t%p\n", pointer);
printf("\n'many' is a C array, a pointer pointing to the address\t%p ( &many[0][0] )\n", &many[0][0]);
printf("'many' content, an address, is\t%p ( *many )\n", *many);
printf("\nStarting at this location --- %p ( *many ) --- we have the values of the array\n\
First value is %d ( **many ),\n\
Second value is %d ( *(1 + *many)... )\n", *many, **many, *(1 + *many) );
pointer = (int*) &many;
printf("Using a cast 'pointer' now points to '(int*) many' \
and its value is %d\n", *pointer);
return 0;
}