First of all, if the type of a pointer in C++ is unsigned char *
that basically only tells the compiler the following things:
- When dereferencing the pointer, the compiler should just read one byte from memory, and we will treat that byte as a number from 0 to 255. (If your system is very unusual then those rules might be a bit different.)
- When doing pointer addition and subtraction, the size of the elements pointed to by the pointer is 1 (or whatever
sizeof(unsigned char)
is on your system).
The type of a pointer also has some subtle effects if you look up the concept of strict aliasing and memory alignment, but I do not want to go into detail on that.
Next, the type of a pointer does not tell the compiler whether the pointer has a valid value that can be dereferenced. The pointer might be uninitialized. It might point to memory that was freed earlier. It might be NULL. It might point one past the last element of an array (something explicitly allowed by the standard).
The type of a pointer does not tell the compiler the whole layout of the memory. You can define a string and then define pointers that point to the beginning middle, end, or one past the end, and all of these are valid pointers with the same type. The type system for the basic pointers in C++ is simply not complicated enough to encode that sort of information, because that is not how the language was designed.
const char * p = "James"; // valid pointer to the beginning of a string
const char * p1 = p + 1; // pointer to 'a'
const char * p5 = p + 5; // pointer to the null terminator (0) after 's'
const char * p6 = p + 6; // pointer to the memory directly after the terminator
Finally, the code you presented has a cast in it, which allows you to do pretty much any conversion you want without much checking by the compiler. So you should not be surprised when the compiler allows you to cast one thing to another.