Consider this simple snippet of code
int a = 5;
int* b;
int c;
b = &c
Crudely drawn. If you think about memory like a char array. Then
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
are all the bytes lined up in memory So when you say int a. That variable occupies 4 bytes of space on the stack
[a, a, a, a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Then you create int *b on the stack. Because b is a pointer it occupies 8 bytes of memory (assuming a 64 bit computer)
[a, a, a, a, b, b, b, b, b, b, b, b, 0, 0, 0, 0]
Then you create your variable c on the stack which also occupies 4 bytes.
[a, a, a, a, b, b, b, b, b, b, b, b, c, c, c, c]
So that is a representation of your variables in memory. It would be right to say &a address of a is at memory location 0, &b is at memory location 4 &c is at memory location 12. So assigning the address of c to the pointer b would say b = 12. But that is where b points to. b holds the value 12 as a pointer that points to the variable c in memory.
The key here is, the address of your variable b is at memory location 4, but as a pointer it looks at memory address 12. b needs 8 bytes to store the memory address of what it is looking at (pointing to).
We can then ask b "What is the value of the thing you point to" by dereferencing b, and we can assign something to the variable it points to.
*b = 10;
c == 10 // true
We have changed the value where c is, so the value of *b == c, which is the number 10.
Finally. A quick observation just to compare the address of b and c.
&b == &c // false
b == &c // true
So although trivial. The point of this example is an attempt at simplifying the computers memory in a way where we can see what pointers are, and what they are doing. Maybe this is helpful.