Deferencing void pointer

D

Douglas Garstang

I can't believe I've been trying to work this out for hours now, and I
can't believe I couldn't find someone asking for a similar solution in
the newsgroups. No wonder I hate C so much, and every time I get the
textbooks out end up throwing them against the wall in rage. Thats
been going on for 10 years now.

Anyway, I have:

typedef struct _record {
int age;
} record;

typedef struct _LinkedList {
void *data;
struct _LinkedList *next;
} LinkedList;

I can add items to my linked list, traverse the list etc. However I
CANNOT get the data OUT of the list.

curr_ptr = head;
while ( curr_ptr != NULL ) {
printf ("traverse: this=%p data=%d\n",
curr_ptr, curr_ptr->data->age);
}

This results in the error:
warning: deferencing 'void *' pointer
request for member 'age' in something not a structure or union.

I know I have to cast it or something.... but I just can't figure it
out!
Arrgh!

Doug
 
R

Richard Heathfield

Douglas said:
I can't believe I've been trying to work this out for hours now, and I
can't believe I couldn't find someone asking for a similar solution in
the newsgroups. No wonder I hate C so much, and every time I get the
textbooks out end up throwing them against the wall in rage. Thats
been going on for 10 years now.

Anyway, I have:

typedef struct _record {
int age;
} record;

typedef struct _LinkedList {
void *data;
struct _LinkedList *next;
} LinkedList;

(A side-point: I used to do this too, but then I discovered that names that
begin with leading underscores are reserved for the implementation. So I
started putting the underscores on the end_ instead_, which_ works_ just_
as_ well_ and_ has_ the_ virtue_ of_ correctness_.)
I can add items to my linked list, traverse the list etc. However I
CANNOT get the data OUT of the list.

curr_ptr = head;
while ( curr_ptr != NULL ) {
printf ("traverse: this=%p data=%d\n",
curr_ptr, curr_ptr->data->age);
}

This results in the error:
warning: deferencing 'void *' pointer
request for member 'age' in something not a structure or union.

I know I have to cast it or something.... but I just can't figure it
out!

No, you don't need to cast (except to print the actual pointer value). I
presume that in this case your data pointer points to a structure of type
record. If so, then...

curr_ptr = head;
{
while(curr_ptr != NULL)
{
record *p = curr_ptr->data;
printf ("traverse: this=%p data=%d\n",
(void *)curr_ptr, p->age);

curr_ptr = curr_ptr->next;
}
}

Feeling better now? :)
 
S

Sheldon Simms

typedef struct _record {
int age;
} record;

typedef struct _LinkedList {
void *data;
struct _LinkedList *next;
} LinkedList;

I can add items to my linked list, traverse the list etc. However I
CANNOT get the data OUT of the list.

curr_ptr = head;
while ( curr_ptr != NULL ) {
printf ("traverse: this=%p data=%d\n",
curr_ptr, curr_ptr->data->age);
}

This results in the error:
warning: deferencing 'void *' pointer
request for member 'age' in something not a structure or union.

Richard gave you a good answer, so I won't write any code for you.
However, I thought I might try to get to the root of your problem.

It seems to me that you are (at least in this case) confusing what
you know about the data structure and what the compiler knows about
the data structure.

Look at your struct LinkedList declaration: You have declared data
as a pointer to void. This tells the compiler that every member
called data of every struct LinkedList is a generic pointer. It
can point to anything at all. *You* know that your code is using
data to point at struct record objects, but the compiler doesn't
know that. All it knows is that data could be pointing at anything
at all.

Therefore, when you try to retrieve a record by dereferencing
curr_ptr->data, the compiler has no choice but to complain. It does
not have the knowledge that, in this situation, curr_ptr->data is
pointing at a struct record. So it says (paraphrased):

"You are asking me to retrieve the value of the member 'age' from
the struct that curr_ptr->data is pointing to, but I can't do that,
because as far as I know, curr_ptr->data isn't pointing to a struct
at all!"

So, in order to access members of the struct record that you know
curr_ptr->data is pointing to, you have to give that information to
the compiler. You have to specify that, in this case, curr_ptr->data
is pointing to a struct record. Richard showed you the cleanest way
to do that, use a temporary variable:

record *p = curr_ptr->data;
printf("data: data=%d\n", p->age);

I recommend you use temporary variables in situations like this as
Richard suggested. I will show you how it would be possible to cast
curr_ptr->data so you can see that it's really just a matter of
telling the compiler what kind of thing curr_ptr->data is pointing
to:

printf("data: data=%d\n", ((record *)curr_ptr->data)->age);

In this case, the cast informs the compiler that curr_ptr-data is
pointing to a record. The compiler is then able to convert
curr_ptr->data into a pointer to record, and then access the age
member of the record being pointed to.

-Sheldon
 
D

Douglas Garstang

Richard Heathfield said:
(A side-point: I used to do this too, but then I discovered that names that
begin with leading underscores are reserved for the implementation. So I
started putting the underscores on the end_ instead_, which_ works_ just_
as_ well_ and_ has_ the_ virtue_ of_ correctness_.)


No, you don't need to cast (except to print the actual pointer value). I
presume that in this case your data pointer points to a structure of type
record. If so, then...

curr_ptr = head;
{
while(curr_ptr != NULL)
{
record *p = curr_ptr->data;
printf ("traverse: this=%p data=%d\n",
(void *)curr_ptr, p->age);

curr_ptr = curr_ptr->next;
}
}


Feeling better now? :)

Thanks Richard, well I felt better for literally 5 minutes. Now I've
reached my next stumbling block. I'm trying to make the list functions
generic. I did have something like this in my append function:

int list_append ( LinkedList **list, void *data ) {

LinkedList *new_item;

if ( (new_item == (LinkedList *)malloc(sizeof(LinkedList))) ==
NULL ) {
return -1;
}
new_item->data = data
....

(btw, I don't know why the pointer to a pointer (ie **list) lets me
return the list pointer in the function arguments without having to
use a return statement... I just know it works because I've seen
others do it! Arrrgh!)

However, I found that because I was only keeping a pointer to the data
in each node, every time I added a new node with a different value,
all the nodes got updated.

So, I changed my code to:
int list_append ( LinkedList **list, void *data, int data_size ) {

LinkedList *new_item;
void *data_blk;

if ( (new_item == (LinkedList *)malloc(sizeof(LinkedList))) ==
NULL ) {
return -1;
}
if ( (data_blk == (void *)malloc(data_size)) == NULL ) {
return -1;
}
memcpy ( data_blk, data, sizeof(data))
new_item->data = data_blk;
....

Once again, the use of memcpy is not something I just KNEW would work.
I remembered I'd used it a few years ago when I spent hours
researching it. NOTHING in C comes naturally to me...)

This doesn't seem like a perfect solution. The function has no way to
know the size of the data because its working with voids. The only way
I can see to make it work is to pass the size of the data to the
function. Is there a better way?

Btw, I have a HUGE pile of textbooks. They can't all be bad.

Doug.
 
S

Sheldon Simms

Thanks Richard, well I felt better for literally 5 minutes. Now I've
reached my next stumbling block. I'm trying to make the list functions
generic. I did have something like this in my append function:

int list_append ( LinkedList **list, void *data ) {

LinkedList *new_item;

if ( (new_item == (LinkedList *)malloc(sizeof(LinkedList))) ==
NULL ) {
return -1;
}
new_item->data = data
...

(btw, I don't know why the pointer to a pointer (ie **list) lets me
return the list pointer in the function arguments without having to
use a return statement... I just know it works because I've seen
others do it! Arrrgh!)

Ok, so I'll try to explain it. Assume you already have a linked list with
one or more elements. Assume further that the variable you use to keep
track of your linked list was declared like this:

int main (void)
{
LinkedList * my_list;
...
list_append(&my_list, stuff);
...
}

In memory, this will look like this:

Object of type LinkedList
+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----> (next node)
+---------+ +-------|-+---------+
Object of type pointer |
to LinkedList V
+---------+
|some data|
+---------+

The variable my_list has an address in memory. You can access it like
this:

&a

This address is just a way of locating my_list in memory. The address
itself has a type in C. That type is pointer to (whatever it's the
address of). In this case that would be pointer to (pointer to LinkedList)
We can store that address in a variable, as long as the variable also
has type pointer to pointer to LinkedList, like this:

LinkedList ** address_of_my_list = &my_list;

An argument to a function is very much like a variable in the function,
that has already been initialized with a value from outside the function.
So when you define your function list_append:

int list_append ( LinkedList **list, void *data )

You are specifying that it will have an argument of type pointer to
pointer to LinkedList and, just as above, you can initialize that argument
with the address of your my_list variable when you call the function:

list_append(&my_list, some_data);

Now when list_append() is called like this, the layout of the data
structures in memory will be like this:

Object of type pointer to
pointer to LinkedList
+---------+
list:| | (this is the argument to list_append)
+-------|-+
|
|
V Object of type LinkedList
+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----> (next node)
+---------+ +-------|-+---------+
Object of type pointer |
to LinkedList V
+---------+
|some data|
+---------+

Notice that this looks almost exactly the same as before. That's because
all that happened is that a pointer to the variable my_list was stored
in the argument named list.

Now list_append() presumably will add the new data to the front of the
list. Probably like this:

1 LinkedList * new_item;
2 new_item = malloc(sizeof *new_item); /* allocate a new LinkedList */
if (new_item != NULL)
{
3 new_item->data = data;
4 new_item->next = *list;
5 *list = new_item;
}

Now lets consider what each numbered line does:

(1) LinkedList * new_item;

This line defines a new object of type pointer to LinkedList called
new_item. The value of this variable is undefined. It doesn't point
to anything in the program yet.

+---------+
new_item:| ---|-----> ?
+---------+

(2) new_item = malloc(sizeof *new_item);

This line allocates a new object big enough to be a LinkedList struct
and stores its address in new_item. (i.e. new_item is set to point at
the new LinkedList node). The members of the LinkedList node (data and
next) have undefined values. They are pointers, but don't point at any
known object yet.

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----> ?
+---------+ +-------|-+---------+
|
V
?


(3) new_item->data = data;

This line makes the data member of the LinkedList node point to the
data that was passed as an argument to list_append():

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----> ?
+---------+ +-------|-+---------+
|
V
+---------+
(the argument called 'data') |new data |
+---------+

(4) new_item->next = *list;

This is where things start to get interesting, and it may be helpful
to break this line down further. From the last big diagram up before
the code, we know that list looks like this in memory:

+---------+
list:| | (this is the argument to list_append)
+-------|-+
|
V
+---------+ +----
my_list:| ---|----->| ...
+---------+ +----

now *list simply means "the thing that list is pointing to". You can
see that list is pointing to the object named my_list, which was
declared OUTSIDE the list_append() function. The line of code we
are considering is (once again):

(4) new_item->next = *list;

So now you should be able to see that this means "copy the value of
the thing list is pointing to into the 'next' member of the LinkedList
node". Since the thing list is pointing to is the variable my_list,
new_item->next will point to whatever my_list is pointing to:

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----+
+---------+ +-------|-+---------+ |
| |
V |
+---------+ |
|new data | |
+---------+ |
|
+---------+ |
list:| | +---------------------+
+-------|-+ |
| |
| |
V V
+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----> (next node)
+---------+ +-------|-+---------+
|
V
+---------+
|some data|
+---------+

(5) *list = new_item;

Finally, this line copies the value of new_item into the thing that
list is pointing to. (i.e., it makes my_list point to the same thing
that new_item is pointing to):

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----+
+---------+ +-------|-+---------+ |
^ | |
| V |
| +---------+ |
| |new data | |
| +---------+ |
+-----+ |
+---------+ | |
list:| | | +---------------------+
+-------|-+ | |
| | |
| | |
V | V
+---------+ | +---------+---------+
my_list:| ---|---+ |data :next ---|-----> (next node)
+---------+ +-------|-+---------+
|
V
+---------+
|some data|
+---------+

Now this probably looks like a mess, but you can see that we have
now changed the value of my_list, which was declared in main().
Once the function list_append() is finished and returns, the
value in my_list will still be the updated value. The variable
new_item and the argument list will disappear when list_append()
returns, since they are local to the function list_append(). So
after list_append() returns, the objects in memory will look like
this:

+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----+
+---------+ +-------|-+---------+ |
| |
V |
+---------+ |
|new data | |
+---------+ |
|
+---------------------+
|
|
V
+---------+---------+
|data :next ---|-----> (next node)
+-------|-+---------+
|
V
+---------+
|some data|
+---------+
 
D

Douglas Garstang

Sheldon Simms said:
Ok, so I'll try to explain it. Assume you already have a linked list with
one or more elements. Assume further that the variable you use to keep
track of your linked list was declared like this:

int main (void)
{
LinkedList * my_list;
...
list_append(&my_list, stuff);
...
}

In memory, this will look like this:

Object of type LinkedList
+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----> (next node)
+---------+ +-------|-+---------+
Object of type pointer |
to LinkedList V
+---------+
|some data|
+---------+

The variable my_list has an address in memory. You can access it like
this:

&a

This address is just a way of locating my_list in memory. The address
itself has a type in C. That type is pointer to (whatever it's the
address of). In this case that would be pointer to (pointer to LinkedList)
We can store that address in a variable, as long as the variable also
has type pointer to pointer to LinkedList, like this:

LinkedList ** address_of_my_list = &my_list;

An argument to a function is very much like a variable in the function,
that has already been initialized with a value from outside the function.
So when you define your function list_append:

int list_append ( LinkedList **list, void *data )

You are specifying that it will have an argument of type pointer to
pointer to LinkedList and, just as above, you can initialize that argument
with the address of your my_list variable when you call the function:

list_append(&my_list, some_data);

Now when list_append() is called like this, the layout of the data
structures in memory will be like this:

Object of type pointer to
pointer to LinkedList
+---------+
list:| | (this is the argument to list_append)
+-------|-+
|
|
V Object of type LinkedList
+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----> (next node)
+---------+ +-------|-+---------+
Object of type pointer |
to LinkedList V
+---------+
|some data|
+---------+

Notice that this looks almost exactly the same as before. That's because
all that happened is that a pointer to the variable my_list was stored
in the argument named list.

Now list_append() presumably will add the new data to the front of the
list. Probably like this:

1 LinkedList * new_item;
2 new_item = malloc(sizeof *new_item); /* allocate a new LinkedList */
if (new_item != NULL)
{
3 new_item->data = data;
4 new_item->next = *list;
5 *list = new_item;
}

Now lets consider what each numbered line does:

(1) LinkedList * new_item;

This line defines a new object of type pointer to LinkedList called
new_item. The value of this variable is undefined. It doesn't point
to anything in the program yet.

+---------+
new_item:| ---|-----> ?
+---------+

(2) new_item = malloc(sizeof *new_item);

This line allocates a new object big enough to be a LinkedList struct
and stores its address in new_item. (i.e. new_item is set to point at
the new LinkedList node). The members of the LinkedList node (data and
next) have undefined values. They are pointers, but don't point at any
known object yet.

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----> ?
+---------+ +-------|-+---------+
|
V
?


(3) new_item->data = data;

This line makes the data member of the LinkedList node point to the
data that was passed as an argument to list_append():

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----> ?
+---------+ +-------|-+---------+
|
V
+---------+
(the argument called 'data') |new data |
+---------+

(4) new_item->next = *list;

This is where things start to get interesting, and it may be helpful
to break this line down further. From the last big diagram up before
the code, we know that list looks like this in memory:

+---------+
list:| | (this is the argument to list_append)
+-------|-+
|
V
+---------+ +----
my_list:| ---|----->| ...
+---------+ +----

now *list simply means "the thing that list is pointing to". You can
see that list is pointing to the object named my_list, which was
declared OUTSIDE the list_append() function. The line of code we
are considering is (once again):

(4) new_item->next = *list;

So now you should be able to see that this means "copy the value of
the thing list is pointing to into the 'next' member of the LinkedList
node". Since the thing list is pointing to is the variable my_list,
new_item->next will point to whatever my_list is pointing to:

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----+
+---------+ +-------|-+---------+ |
| |
V |
+---------+ |
|new data | |
+---------+ |
|
+---------+ |
list:| | +---------------------+
+-------|-+ |
| |
| |
V V
+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----> (next node)
+---------+ +-------|-+---------+
|
V
+---------+
|some data|
+---------+

(5) *list = new_item;

Finally, this line copies the value of new_item into the thing that
list is pointing to. (i.e., it makes my_list point to the same thing
that new_item is pointing to):

+---------+ +---------+---------+
new_item:| ---|----->|data :next ---|-----+
+---------+ +-------|-+---------+ |
^ | |
| V |
| +---------+ |
| |new data | |
| +---------+ |
+-----+ |
+---------+ | |
list:| | | +---------------------+
+-------|-+ | |
| | |
| | |
V | V
+---------+ | +---------+---------+
my_list:| ---|---+ |data :next ---|-----> (next node)
+---------+ +-------|-+---------+
|
V
+---------+
|some data|
+---------+

Now this probably looks like a mess, but you can see that we have
now changed the value of my_list, which was declared in main().
Once the function list_append() is finished and returns, the
value in my_list will still be the updated value. The variable
new_item and the argument list will disappear when list_append()
returns, since they are local to the function list_append(). So
after list_append() returns, the objects in memory will look like
this:

+---------+ +---------+---------+
my_list:| ---|----->|data :next ---|-----+
+---------+ +-------|-+---------+ |
| |
V |
+---------+ |
|new data | |
+---------+ |
|
+---------------------+
|
|
V
+---------+---------+
|data :next ---|-----> (next node)
+-------|-+---------+
|
V
+---------+
|some data|
+---------+

Sheldon,
Thanks for the really detailed response. Unfortunately after reading
your post several times, I still just don't get it. I can't even
visualise pointers to anything in my head, let alone pointers to
pointers. I've got something pretty good working right now...
simulating perl's (now there's something I can work with) push, pop,
shift and unshift functions... and its only taken 10 years to do it!

I have to wonder if there's any point in trying any more. Even if I
manage to get something working, I still don't understand all the
fundamentals and probably never will. You'd think for a guy who
started programming at the age of 10 with his vic-20, went to Uni, and
scripts on a daily basis, I'd have SOME idea by now.
 
R

Richard Heathfield

Douglas said:
Thanks Richard, well I felt better for literally 5 minutes. Now I've
reached my next stumbling block. I'm trying to make the list functions
generic. I did have something like this in my append function:

int list_append ( LinkedList **list, void *data ) {

LinkedList *new_item;

I just use new, rather than new_item (mainly for the apoplexy it causes in
C++ programmers).
if ( (new_item == (LinkedList *)malloc(sizeof(LinkedList))) ==
NULL ) {

Better: if( (new_item == malloc(sizeof *new_item)) == NULL) {

which, as well as being quicker to type, needs less maintenance and is
easier to read.
return -1;
}
new_item->data = data
...

(btw, I don't know why the pointer to a pointer (ie **list) lets me
return the list pointer in the function arguments without having to
use a return statement... I just know it works because I've seen
others do it! Arrrgh!)

"return the list pointer" is really the wrong way to say it. Think of
"return value" as "thing that comes out of the left-hand end of a function"
rather than something the compiler vomits back up to the caller via a
parameter.

Okay, why does it work? Well, C is always pass-by-value, as you know
already. Parameters are /copies/ of the arguments provided, not the
original objects. So changing a parameter's value has no effect in the
caller. BUT consider this. I have a piece of paper here which tells me
where I can find my ATM card and PIN. I give you a copy of the piece of
paper. Now, if you want, you can write all over that piece of paper. Will
it affect my original piece? No, of course not. Will it affect my bank
account in any way? No, of course not. BUT if you take that copy and use it
to track down my ATM and PIN, you can use them to do all sorts of mischief.

The piece of paper is a pointer. Changing its value is equivalent to
scribbling on the paper. A waste of time. But /using/ its value to locate
another object in memory? That's very different, because an accurate /copy/
of an accurate /description/ of a location is just as good as the
/original/ description of the location.

People get mildly confused when what they need to change is a pointer. But
nothing is different here. If you want a function to change the value of a
foo, you must pass in the address of that foo. Just because foo might
already be a pointer type, doesn't mean that anything's changed. If you
want to update a pointer, you must pass that pointer's address (i.e. a
pointer to the pointer!) to the function, just like you'd have to pass
anything else's address.
However, I found that because I was only keeping a pointer to the data
in each node, every time I added a new node with a different value,
all the nodes got updated.
Right.

So, I changed my code to:
int list_append ( LinkedList **list, void *data, int data_size ) {

Better: size_t data_size (or just size_t size)
LinkedList *new_item;
void *data_blk;

if ( (new_item == (LinkedList *)malloc(sizeof(LinkedList))) ==
NULL ) {

if( (new_item == malloc(sizeof *new_item)) == NULL) {
return -1;
}
if ( (data_blk == (void *)malloc(data_size)) == NULL ) {

Casting malloc is already silly, but casting from void * to void * to assign
to a void * pointer is actually quite funny. :)

if((data_blk = malloc(data_size)) == NULL) {
return -1;
}
memcpy ( data_blk, data, sizeof(data))
new_item->data = data_blk;
...

Once again, the use of memcpy is not something I just KNEW would work.

Well, now you know.
I remembered I'd used it a few years ago when I spent hours
researching it. NOTHING in C comes naturally to me...)

It will, it will.
This doesn't seem like a perfect solution. The function has no way to
know the size of the data because its working with voids. The only way
I can see to make it work is to pass the size of the data to the
function. Is there a better way?

Well, not if you want to keep the generic nature of the code.

You might benefit from taking a look at the linked list code at
http://users.powernet.co.uk/eton/unleashed/code/ch11/ch11.zip - if only to
reassure you that you are basically on the right track.
Btw, I have a HUGE pile of textbooks. They can't all be bad.

Yeesh. You'd be amazed. (No, you're probably right. There's probably at
least one good C book in the pile. Maybe.)
 
R

Richard Heathfield

Douglas Garstang wrote:

Sheldon,
Thanks for the really detailed response.

We'd already read it. Please only include /necessary/ text when responding,
for the sake of those of us who have modems rather than cable or satellite
connections. Thanks.
Unfortunately after reading
your post several times, I still just don't get it. I can't even
visualise pointers to anything in my head, let alone pointers to
pointers.

Okay. That's fixable. And you're gonna love this...

If you want to understand pointers, write a computer. Or rather, write an
emulator for the non-existent computer of your choice.

Start off simple. A 1-bit computer with 2 1-bit "bytes" of memory, a 1-bit
instruction register (it holds the address in memory of the next
instruction to be executed), and one 1-bit register. Decide on an
instruction set for it (it'll have just two different instructions), and
then write it. /Then/ write programs for it. (These are easy: 00, 01, 10,
11. All done.)

This can be done in around 300 lines of C code. Less if you're a terse
coder, but mine is 300 lines (excluding a few printfs and comments), and it
even includes a rudimentary disassembler.

Then write a 2-bit computer. That'll have four 2-bit "bytes" of memory, a
2-bit instruction register, and perhaps you can be daring and give it two
2-bit registers. The instruction set can have a massive FOUR instructions.

My version takes about 350 lines. And the 3-bit version is about 400 lines.
It's not difficult, believe me. And by the time you get up to 8 bits, you
/will/ understand pointers.
 
J

James Hu

... Unfortunately after reading your post several times, I still just
don't get it. I can't even visualise pointers to anything in my head,
let alone pointers to pointers.

A pointer is simply an indirect reference to an entity (in C parlance,
and object).

Here is an analogy. C objects are voice mailboxes. These objects are
named according to the people to whom the mailboxes belong. A pointer
to a C object would correspond to the phone number you would dial to
access the voice mailbox of the person you wanted leave a message for.

Dialing a number is analagous to dereferencing a pointer. Leaving
a message in a voice mailbox is analagous to assigning a value to a
dereferenced pointer. Listening to a voice mailbox message is analagous
to reading the value of a deferenced pointer.

A pointer to a pointer is analgous to a phone number to a voice mailbox
which can only hold a message that is another phone number.

-- James
 
F

Floyd Davidson

James Hu said:
A pointer is simply an indirect reference to an entity (in C parlance,
and object).

Here is an analogy. C objects are voice mailboxes. These objects are
named according to the people to whom the mailboxes belong. A pointer
to a C object would correspond to the phone number you would dial to
access the voice mailbox of the person you wanted leave a message for.

Dialing a number is analagous to dereferencing a pointer. Leaving
a message in a voice mailbox is analagous to assigning a value to a
dereferenced pointer. Listening to a voice mailbox message is analagous
to reading the value of a deferenced pointer.

A pointer to a pointer is analgous to a phone number to a voice mailbox
which can only hold a message that is another phone number.

Ahem... a _pointer_ _to_ _a_ _pointer_ is the phone number of directory
assistance. (Which needs care in accessing, because as soon as
you give it the address to dereference, it is possible that the
results are a trap represention, which comes not from the pleasant
sounding human voice of the operator you were talking to, but from
a mechanized voice in a machine located in some remote corner of
the collective network memory of the telephone network.)

A _pointer_ _to_ _a_ _pointer_ _to_ _a_ _pointer_ is the number of that guy
down the block, who works for the phone company and knows the
right number to call to ask where to call to get a number to
call to get whatever number it is that you want.

A _pointer_ _to_ _a_ _pointer_ _to_ _a_ _pointer_ _to_ _a_ _pointer_ may have a
different size though, because that is the little black book the
guy down the block has been keeping in his pocket for the past
20-30 years, where he looks up all these numbers to get numbers
to get numbers to get a number.

I mean, how could anybody be confused??? ;-)
 
C

CBFalconer

Richard said:
.... snip ...

Better: if( (new_item == malloc(sizeof *new_item)) == NULL) {

which, as well as being quicker to type, needs less maintenance and is
easier to read.

Harumph. Are you sure? :)
 
J

James Hu

Ahem... a _pointer_ _to_ _a_ _pointer_ is the phone number of directory
assistance.
...

No, that is a pointer to an oracle, for which there is no C analog, so
it is off-topic in comp.lang.c. Please try rec.humor.oracle.

-- James
 
D

Dan Pop

In said:
Modulo the first == (which should be = instead), yes, I'm sure. (Oops.)

See, it's much better to always triple check than to rely on silly gadgets
that work only when you're lucky!

Dan
 
C

Christopher Benson-Manica

Richard Heathfield said:
I just use new, rather than new_item (mainly for the apoplexy it causes in
C++ programmers).

Well, whatever flaws may result from doing so are well worth causing
apoplexy in C++ programmers, eh? ;)
 
R

Richard Heathfield

Dan said:
In <[email protected]> Richard Heathfield


See, it's much better to always triple check than to rely on silly gadgets
that work only when you're lucky!

I don't /rely/ on what you call silly gadgets. I use the const==var order as
an additional check, because I know I make mistakes of this kind, no matter
how carefully I check. I know you do, too, because you have done so in an
article posted to this group, so please don't get all high and mighty on
me.

In any event, const==var can never catch a mistakenly typed == where = was
meant; its purpose is to catch (where possible) the /opposite/ mistake.
 
R

Richard Heathfield

Christopher said:
Well, whatever flaws may result from doing so are well worth causing
apoplexy in C++ programmers, eh? ;)

Er, flaws? I haven't spotted any yet. Do feel free to share them. :)
 
K

karl malbrain

Richard Heathfield said:
I just use new, rather than new_item (mainly for the apoplexy it causes in
C++ programmers).

Bzzzt. Not around here you don't! and I'm not even a C++ programmer.
It's generally necessary to port C code to C++.

karl m
 
S

Sheldon Simms

Bzzzt. Not around here you don't! and I'm not even a C++ programmer.
It's generally necessary to port C code to C++.

Why? Did the C compiler stop working?
 
R

Richard Heathfield

karl said:
Bzzzt. Not around here you don't!

I see no reason why not.
and I'm not even a C++ programmer.
Irrelevant.

It's generally necessary to port C code to C++.

That has not been my experience. If it ain't broke, don't fix it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Return pointer from void only gives the memory address 0
Array of structs function pointer 10
void pointers 36
void * 36
void pointer 4
Void pointer to pass by reference 11
arithmetic on a void * pointer 140
void pointer 8

Members online

No members online now.

Forum statistics

Threads
474,102
Messages
2,570,645
Members
47,247
Latest member
GabrieleL2

Latest Threads

Top