Every pointer has a type. A pointer might be a “pointer to an int” (int*), a “pointer to a double” (double*), or a “pointer to a Circle” (Circle*). This information is essential because it tells the compiler how to interpret the data that the pointer points to. For example, given double* p, the compiler knows that if we try to use the memory p points at, it only makes sense to use it as a double.
It is also an error to try to store a value other than a memory address into a pointer. Given int x = 10;, we can’t ask a pointer to store x - that names the value 10, not the memory address of x.
It is possible to declare a pointer with no specific type as a void*. However, because that address has no defined type, the compiler won’t let us do much with that address. It does not know if the address points at data that should be treated as a double, an int, or a Circle.
Much like references, pointers can be declared “const”. But, there is an added wrinkly: you can declare either the data it points to as “const”, meaning you can’t change the data through the pointer, or the pointer itself as “const”, meaning the memory address the pointer has can’t be changed. You can even declare both the pointer and the data it points at as being const. Here are some examples:
int* const p = &x p is a constant pointer to an int, meaning the pointer itself cannot be changed to point to another int, but the int it points to (x) can be modified.
const int* p = &x p is a pointer to a constant int, meaning the int it points to cannot be modified through the pointer, but the pointer itself can be changed to point to another int.