FAQ 4.9 and void**

M

MackS

Hi

FAQ 4.9 states that void** cannot be used portably "to pass a generic
pointer to a function by reference". I would like to hear from you
whether there is anything wrong with the following approach when you
need a function to be able to modify a (void*) arg it has been passed:

struct pointer
{
void *p;
};

void fun(struct pointer *ptr)
{
ptr->p = NULL; /* assign whatever value you want to the (void*) we were
(indirectly) passed */
}

int main(void)
{
struct pointer ptr;

fun(&ptr);

return 0;
}

Is this a good way to do it? Or can I do it in a cleaner way?

Thank you,

MackS
 
M

Michael Mair

MackS said:
Hi

FAQ 4.9 states that void** cannot be used portably "to pass a generic
pointer to a function by reference". I would like to hear from you
whether there is anything wrong with the following approach when you
need a function to be able to modify a (void*) arg it has been passed:

struct pointer
{
void *p;
};

void fun(struct pointer *ptr)
{
ptr->p = NULL; /* assign whatever value you want to the (void*) we were
(indirectly) passed */
}

int main(void)
{
struct pointer ptr;

fun(&ptr);

return 0;
}

Is this a good way to do it? Or can I do it in a cleaner way?

Umh, usually you use void * for data where the type plays no role
or where you need a generic interface, e.g.

void fun1 (void *data, double param, double *pos)
{
int i;
double (*points)[2];

points = data;

for (i=0; i<2; i++)
pos = (1.0-param)*points[0] + param*points[1];
}

void fun2 (void *data, double param, double *pos)
{
int i;
struct circle *circ;

circ = data;

pos[0] = circ->mp[0] + cos(2*PI*param)*circ->radius;
pos[1] = circ->mp[1] + sin(2*PI*param)*circ->radius;
}

.....

I am not sure whether this answers your question.
Maybe you can explain a little bit more what you want to achieve
or what application you have in mind where you would need to use
a void **.
Even a void ** can be passed as void *...


Cheers
Michael
 
B

Ben Pfaff

MackS said:
FAQ 4.9 states that void** cannot be used portably "to pass a generic
pointer to a function by reference". I would like to hear from you
whether there is anything wrong with the following approach when you
need a function to be able to modify a (void*) arg it has been passed:

You misunderstand the FAQ. You can use void ** to pass a void *
for initialization by a function. You can't use it, however, to
pass an int *, or a struct foo *, or a FILE *, for initialization
by a function. Your approach, of wrapping a void * in a struct,
is awkward and not likely to be of additional benefit. I could
see using a union, however, if you did want to initialize one out
of several types of pointers.
 
A

Andrey Tarasevich

MackS said:
Hi

FAQ 4.9 states that void** cannot be used portably "to pass a generic
pointer to a function by reference".

It is important to understand what exactly is meant by it. What the FAQ
is actually saying is that one can't portable do this

void foo(void** p) {
*p = /* something */;
}


int* p;
...
foo((void**) &p);

The above code attempts to do a raw low-level re-interpretation of
'int*' pointer as a 'void*' pointer. This will not work in general case.
That's what FAQ is saying.

However, the following will work just fine (assuming that 'foo' is
implemented correctly)

int* p;
void* vp;
...
vp = p; /* <- if necessary */
foo(&vp);
p = vp;

Note that in this case the code performs a language-level conversion of
'int*' value to 'void*' value (if necessary), invokes the function and
then performs the reverse conversion, again at language level. These
language-level pointer conversions is what preserves the portability of
the code (as opposed to raw memory re-interpretation in the previous
non-portable example).
I would like to hear from you
whether there is anything wrong with the following approach when you
need a function to be able to modify a (void*) arg it has been passed:

struct pointer
{
void *p;
};

void fun(struct pointer *ptr)
{
ptr->p = NULL; /* assign whatever value you want to the (void*) we were
(indirectly) passed */
}
...
Is this a good way to do it? Or can I do it in a cleaner way?
...

In essence this way is no different from the second (portable) example
above. Your approach can be misused in exactly the same way as the
'void**' one

int* p;
...
fun((struct pointer*) &p); /* Non-portable !!! */
...

However, I'd agree that from the point of view of pure "visual
readability", your approach seems to encourage the user to use the
proper technique more that the 'void**' variant. So it is up to you to
decide which variant is "better".
 
M

MackS

Thank you all for your replies. My original post wasn't very clear.

Ben Pfaff wrote:

You can use void ** to pass a void * for initialization by a function.
You can't use it, however, to
pass an int *, or a struct foo *, or a FILE *, for initialization by a
function.

The latter is close to what I'm trying to do. Continuing with my
original example, assume I need fun() to be able to change the pointer
it is passed, and this pointer can be of several types -- eg, (int*) or
(struct foo*). So I tried to define the function as accepting a
(void**) argument. Consider the following case:

struct a {int i;};
struct b {int i;};

void fun(void **ptr)
{
*ptr = NULL;
return;
}

int main(void)
{
struct a p_a;
struct b p_b;

fun(p_a);

return 0;
}

This compiles succesfully but with the warning "passing arg 1 of 'fun'
from incompatible pointer type". My original post was an attempt at
understanding what might be going wrong with this code.

Looking at the example above, what is the optimal way to define fun()?

- should I ignore the warning?
- should I use a union? (Ben Pfaff)
- should I just "convert" the non-NULL pointer to a (void*) and then
pass its address as a (void**)?(Andrey Tarasevich)

Of the three, Andrey's suggestion of just adding an additional line
before the call to fun() in the code above

void *ptr = p_a;

and then calling

fun(&ptr);

seems like the cleanest way to do it. Is this valid, correct code?

Thank you for any help,

Mack
 
M

MackS

As must be clear, what I meant was

struct a *p_a;
struct b *p_b;

and not

struct a p_a;
struct b p_b;
 
E

Eric Sosman

MackS said:
Thank you all for your replies. My original post wasn't very clear.

Ben Pfaff wrote:

You can use void ** to pass a void * for initialization by a function.
You can't use it, however, to
pass an int *, or a struct foo *, or a FILE *, for initialization by a
function.

The latter is close to what I'm trying to do. Continuing with my
original example, assume I need fun() to be able to change the pointer
it is passed, and this pointer can be of several types -- eg, (int*) or
(struct foo*). So I tried to define the function as accepting a
(void**) argument. Consider the following case:

struct a {int i;};
struct b {int i;};

void fun(void **ptr)
{
*ptr = NULL;
return;
}

int main(void)
{
struct a p_a;
struct b p_b;

fun(p_a);

return 0;
}

This compiles succesfully

Complain to the compiler vendor. fun() expects to
recieve a `void**' argument, but is being given a `struct a'
argument instead. `struct a' is not convertible to `void**',
not even by casting.

Are you sure this is the actual code? I'll bet you
forty-two dozen Krispy Kreme doughnuts and a cholesterol
screening that it's a messed-up paraphrase. In what follows,
I'll assume you actually had

struct a * p_a;
...
fun(&p_a);

.... and if my assumption is wrong you have only yourself to
blame. Next time, cut and paste.
but with the warning "passing arg 1 of 'fun'
from incompatible pointer type". My original post was an attempt at
understanding what might be going wrong with this code.

Looking at the example above, what is the optimal way to define fun()?

- should I ignore the warning?

Yes, if you don't mind taking chances. The code (as
reconstructed) is not portable; it may work on some systems
while failing in mysterious ways on others. If your intent
is to write programs that resemble houses of cards ...
- should I use a union? (Ben Pfaff)

You'd need some way to tell fun() which of the union's
members it should set.
- should I just "convert" the non-NULL pointer to a (void*) and then
pass its address as a (void**)?(Andrey Tarasevich)

That would work.
Of the three, Andrey's suggestion of just adding an additional line
before the call to fun() in the code above

void *ptr = p_a;

and then calling

fun(&ptr);

seems like the cleanest way to do it. Is this valid, correct code?

Yes. Valid, correct -- and probably pointless. If fun()
is to do anything "useful" with the targetted pointer (e.g.,
allocate some memory for it), it usually needs to know something
about the type of the pointed-to object (e.g., how big it is).
The two "usual" ways to handle this are (1) to write fun() so
it accepts a `KnownType**' instead of a `void**', or (2) to
have fun() return a `void*' that the caller can then assign to
a `KnownType*' variable.

There's an important and vexing situation where (1) and (2)
don't quite suffice, and that's in writing functions to operate
on user-defined linked structures: sorting a linked list, say,
or traversing a binary tree. This requires that fun() be able
to manipulate pointers of unknown type, and C isn't good at that.
However, there's an escape clause that usually handles this sort
of thing: Although C pointers to different object types may have
different representations, all pointers to all struct types have
the same representation. On the likely assumption that the nodes
in the linked list or binary tree are structs of some kind, you
can have fun() work on `struct unknown*' pointers with reasonable
safety.

Turning back to why things are the way they are, here's an
analogy that may help you appreciate the difficulty. It seems
to you that fun() should manipulate "pointers," and you seek a
way to point to a pointer variable without worrying about the
variable's type. Now consider writing a fun2() that takes as
an argument a pointer to a number

void fun2( SomeNumberType *ptr ) {
*ptr = 42;
}

.... and the question is how to write "SomeNumberType" so that
fun2() will work correctly in all of these calls:

short s;
int i;
size_t z;
double d;
unsigned char uc;
...
fun2(&s);
fun2(&i);
fun2(&z);
fun2(&d);
fun2(&uc);

Most people seem able to appreciate that this isn't going to
work; even though all the pointed-to variables are "numbers"
they have different sizes and represent forty-two differently.
The lone assignment inside fun2() cannot cope with all the
different format possibilities simultaneously. The case is
similar with variables that are pointers to different types:
they are all "pointers," but they may have different sizes
and represent their values differently. The single assignment
in fun() cannot deal with all pointer types simultaneously.
 

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

Members online

Forum statistics

Threads
474,159
Messages
2,570,881
Members
47,418
Latest member
NoellaXku

Latest Threads

Top