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.