There are a couple of ways of answering this.
We say that a pointer value is the address of a memory location. But different computers have used different addressing schemes for memory. C is a higher-level language, portable across many kinds of computers. C does not mandate a particular memory architecture. As far as the C programming language is concerned, memory addresses could literally be things like "123 Fourth Ave.", and it's hard to imagine converting back and forth between an integer and an address like that.
Now, for any machine you're likely to use, memory is actually linearly addressed in a reasonably straightforward and unsurprising way. If your program has 1,000 bytes of memory available to it, the addresses of those bytes might range from 0 up to 999. So if you say
char *cp = (char *)10;
you're just setting up a pointer to the byte located at address 10 (or, that is, the 11th byte in your program's address space).
Now, in C, a pointer is not just the raw address of some location in memory. In C, a pointer is also declared to specify what type of data it points to. So if we say
int *ip = (int *)10;
we're setting up a pointer to one int's worth of data located at address 10. It's the same point in memory as cp
pointed to, but since it's an int pointer, it's going to access an int's worth of bytes, not one byte like cp
did. If we're on an old 16-bit machine, and int is two bytes, we could think of ip
as pointing at the fifth int in our address space.
A cast in C can actually do two things: (1) convert a value ("change the bits"), or (2) change the interpretation of a value. If we say float f = (float)3;
, we're converting between the integer representation of 3 and a floating-point representation of 3, which is likely to be quite different. If we go in the other direction, with something like int i = (int)3.14;
, we're also throwing away the fractional part, so there's even more conversion going on. But if we say int *ip = (int *)10;
, we're not really doing anything with the value 10, we're just reinterpreting it as a pointer. And if we say char *cp = (char *)ip
, we're again not changing anything, we're just reinterpreting to a different kind of pointer.
I hasten to add, though, that everything I've said here about pointer conversions is (a) very low-level and machine-dependent, and (b) not the sort of thing that ordinary C programmers are supposed to have to think about during ordinary programming tasks, and (c) not guaranteed by the C language.
In particular, even when programming for a computer with a conventional, linearly-addressed memory model, it's likely that your program doesn't have access to address 10, so these pointers (cp
and ip
) might be pretty useless, might generate exceptions if you try to use them. (Also, when we have a pointer like ip
that points at more than 1 byte, there's the question of which bytes it points to. If ip
is 10, it probably points at bytes 10 and 11 on a 16-bit, byte-addressed machine, but which of those two bytes is the low-order half of the int and which is the high-order half? It depends on whether it's a "big endian" or "little endian" machine.)
But then we come to null pointers. When you use a constant "0" as a pointer value, things are a little different. If you say
void *p = (void *)0;
you are not, strictly speaking, saying "make p
point to address 0
". Instead, you are saying "make p
be a null pointer". But it turns out this has nothing to do with the cast, it's because of a special case in the language: in a pointer context, the constant 0 represents a null pointer constant.
A null pointer is a special pointer value that's defined to point nowhere. It might be represented internally as a pointer to address 0, or it might be represented some other way. (If it is in fact represented as a pointer to address 0, your compiler will be careful to arrange that there's never any actual data at address 0, so that it's still true that the pointer "points nowhere" even though it points to address 0. This is sort of confusing, sorry about that.)
Although pointers to raw addresses like 10
are low-level and dangerous and machine-dependent, null pointers are well-defined and perfectly fine. For example, when you call malloc
and it can't give you the memory you asked for, it returns a null pointer to tell you so. When you test malloc
's return value to see if it succeeded or failed, you just check to see if it gave you a null pointer or not, and there's nothing low-level or nonportable or discouraged about doing so.
See http://c-faq.com/null/index.html for much more on all this.