int x - declaration variable x of type int
int* y - declaration of variable y being a pointer to some variable of type int (pointer to int = int*)
int** z - declaration of variable z being a pointer to a pointer to some variable of type int
and so on
Now, when used in an expression:
x - variable of type int
y - pointer to int
*y - variable of type int pointed by y (you see, in the declaration you had int* y, but it can be also written as int *y, which means that *y must be of type int).
z - pointer to pointer to variable of type int
*z - pointer to variable of type int pointed by z
*(*z) or simply **z - variable of type int pointed by pointer pointed by z :D
*x - invalid, x is not a pointer, so you may not dereference it (* on the left side of a pointer means - take the value pointed by the pointer)
**y or *(*y) - invalid, y is a pointer, but *y is a variable of type int, not a pointer
The & thing is the simplest one. It simply creates a pointer to the thing that is afterwards:
&x - pointer to (the variable x of type int)
&y - pointer to (the pointer y to some int variable)
&z - pointer to (the pointer z to a pointer to int)
So now, when you know how to declare pointers, how to create them and how to use them, you can write:
1 2 3
|
int x = 5; // simply a variable of type int
int* y = &x; // 2 things here: declare a new variable y, being a pointer to int and initialize it with a pointer to x
printf("%d\n", *y) // prints 5
|
To summarize: in expressions & is just the opposite than *. & returns a pointer to something.
* returns something pointed by a pointer. And * can be additionally used to declare pointers.