Hi, I think you're mostly right, but you wrote it in a way, that
could easily be mis-interpreted, especially by someone who
needs to have this explained ...
Malcolm said:
A pointer can point to anything.
Better: Pointers can point to any type of object in memory.
You can't point it to, say, the controller of your hard disk
;-)
If it points to an integer it is an int *, if it points to a character it is
a char *.
To further clarify, this does *not* mean that an int pointer
that accidentally points to a char object suddenly changes
its type from int* to char*. An int* is an int* and will remain
an int* as long as the program is running.
An expample:
struct { char str[10]; int i[10];} array;
char *pc = &array.str[9]; // takes the address of the last element in str
int *pi = &array.i; // address of the first (zero'th) element in i
Now incrementing pc like in:
pc = pc + 1;
does not change the type of the pointer. pc is still a char*
and when you try to do something with it, you get undefined
behaviour. The operation to increment a pointer to one place
_behind_ the last element of an array is perfectly legal and
well defined.
On the other hand:
pi = pi - 1;
does not change the type of pi from int* to char*, in fact
(even if it were allowed and defined what to do when you de-
crement a pointer to a place before the beginning of an array,
which it isn't) does not even point to str[9] but probably
to str[7]. In real life it can point anywhere, because of
padding bytes the compiler puts between the end of str[] and
the beginning of i[] to ensure that the elements contained
in i[] can be accessed (some systems cannot access integers
that reside on odd adresses, some can but extremely slow,
some will cause bus errors or protection faults, ....
it's undefined, _anything_ can happen).
It can also point to another pointer.
Additionally, a void* can point to _any_ type of object.
But that's about all that a void* can, or what you can
do with a void*. a void* can point to a void** (can it?)
but I have yet to discover of what use a void** could be.
Before you use it for something more trivial than just
pointing around, e.g. dereferencing it to access the object
it points to, you have to cast it to a pointer of a
particular type, meaning that _you_ have to know the type
of object it points to. The compiler cannot do that, it
relies on your knowing what you are doing. On some systems
(especially embedded controllers) this can be used for
some nasty tricks, but in 'standard' C this leads to
undefined behaviour.
The advantage of void* is that they can be
casted to any other type and the accesses made via the casted
(and therefore typed) pointer are guaranteed to succed in re-
retrieving a porperly typed object at the location it points
to. If you point a void* to the address of str[1] in the above
struct of my 'example' and then cast it to an int*, you'll
get undefined behaviour on most modern systems where
sizeof(char) != sizeof(int). The result of this misaligned
pointing is undefined, it you do nothing more than point
to objects which, on a particular system, cannot be stored
at this location nothing serious will happen, but if you
try to access the misaligned object, your program will
most certainly some kind of access error that will crash
your program.
to cast a void* to some other type of pointer, in C you
do not need the explicit cast like
int i;
void* pv=&i;
int *pi=(int*)pv;
it is sufficient to just write:
int *pi = pv;
The compiler selflessly does the conversion itself ('implicit'
cast).
*ptr gives us the 12
*(ptr +1) gives us the 15
*(ptr +2) gives us the -4.
Note that we increment the pointer by the number of objects.
The compiler knows their size and appropriately ensures that
the increment in memory locations is correct.
This is the reason why one can't do pointer-arithmetic
with void* pointers, because the objects the pointer points
to have no known type.
You can add pointers and integers, but not pointers and
pointers. With one exception: if both pointers are of the
same type (e.g. int*) and both point to locations inside
an int-array (up to one position after the last element
of the array) you can use p2-p1 to get the number of
elements between p1 and p2.
We can also use array notation
ptr[0] is exactly the same a *ptr
ptr[1] is exactly the same as (*ptr+1)
Typo: ptr[1] == *(ptr+1)
This is especially useful for strings, because we generally don't know how
long they will be.
Which is one of the reasons why one shouldn't use functions like
gets() or strcpy().
Oops, different thread. ;-)
better: "astring\0"
we point to the 'a', and then we can examine all the other characters. The 0
tells us where the string ends.
Markus, hoping that I didn't make too much mistakes at this
early time of day...