All main memory (RAM) can be thought of as nothing but an array of bytes.
And a pointer can be thought of as nothing but an index into this array.
On modern systems with memory virtualization not all memory may be accessed, so if you make a pointer point to any random location and attempt to read or write from that location you are bound to get an illegal memory access exception. However, for as long as you keep your pointers to point to variables of your program, you are fine.
Furthermore, there exists memory which is readable but not writable, either due to permissions set by the operating system, or because it is ROM which has been mapped into the address space of RAM. So, just because you can read a value from a memory location pointed by a pointer, it does not mean you can write to it.
And no, a pointer cannot point to a CPU register. CPU registers are entirely outside of that big array of bytes called RAM. (However, there exist architectures that map certain special I/O registers of hardware into RAM locations, so these registers can in fact be accessed via a pointer. So, it depends on what kind of register we are talking about.)
A lot of confusion about pointers is caused by the fact that when we print their values we see hard-to-understand values like 0x51a4851c
, and yet we never assign literal values to them, like char* p = 0x51a4851c;
.
That's because our programs (and the data that our programs use) are located in seemingly random memory locations like 0x51a48000
, and for many decades now the trend on all but the tiniest embedded systems has been to not have to know (never have to worry about) precisely on which address your program has been loaded into, because that's too much information and of no practical use.
So, when you say char* p = &c;
the compiler will generate code that loads the address of c
into p
without you having to know precisely where c
is located.
And in fact the operating system is free to load your program into whatever address it sees fit, so this address may change between runs of your program, so it is a very good thing that you do not have to know any fixed addresses, because there really aren't any. (Except in the tiniest of embedded systems.)