Re: A question about void pointers
- From: Eric Sosman <Eric.Sosman@xxxxxxx>
- Date: Mon, 01 Dec 2008 16:34:13 -0500
Richard Harter wrote:
On Mon, 01 Dec 2008 13:02:34 -0500, Eric Sosman
<Eric.Sosman@xxxxxxx> wrote:
Richard Harter wrote:Here is a simple problem. In the calling environment there is a pointer to some data, e.g., an arrays of structs with a declaration that looks something like this:Okay. Just keep in mind that the two parameters point at the
struct rabbit *peter, *molly;
We have a function that will alter the data and change the pointers. We might call it like this:
carrot_patch(&peter,&molly);
Suppose carrot_patch is some kind of generic function that can
operate on data of many different types? It would be nice if
the prototype had generic pointers, e.g.,
void carrot_patch(void *, void *);
pointer variables `peter' and `molly', and not to `struct rabbit'
instances.
Fine so far. In the code for function carrot_patch we have aThere are two (plausible) things it could do: It could
little issue. How is it going to handle these pointers to
pointers?
"handle" them blindly, just passing them around as "pointers
to whatever," or it could somehow determine that they in fact
point to `struct rabbit*' pointers, convert them appropriately,
and then use and/or modify the pointed-at pointers (`peter' and
`molly'). In the latter case, it could also use those pointers
to get at the `struct rabbit' instances they in turn point at.
The continuation was meant to rule out the latter possibility.
As far as carrot_patch is concerned the pointers are pointers to
raw storage objects.
Then you're out of luck: C has no "generic pointer to pointer"
type, so there's no way for carrot_patch() to know how many bytes
its arguments point to nor how to interpret them. There are N cases:
1) `char*' and `void*' have the same size and representation
2) All pointers to complete or incomplete struct types have
the same size and representation
3) All pointers to complete or incomplete union types have
the same size and representation
4,5,...,N) Pointers to other types can have any sizes and
representations the implementation feels like using
On many systems it will turn out that all of (1) through (N)
devolve to a single representation that works for all pointed-to
types, but this is not something the C language guarantees, and
there have been systems where different pointer types have different
representations.
[...]
The actual code is a bit of code to resize an array of structs;
however it could just as well be to encrypt them. Be that as it
may, here is what I ended up writing.
static void
exparray(char **a, size_t *a_sz, size_t d_sz)
{
size_t keep;
size_t sz;
size_t newb;
trace("exparray");
sz = *a_sz;
keep = sz*d_sz;
if (sz <4) sz = 4;
else if (sz < 32) sz *= 2;
else sz += sz/2;
newb = sz*d_sz;
*a_sz = sz;
*a = morespace(*a,keep,newb,LINELOC);
}
with example usage
exparray((char **)&(src->ct),&src->sz_ct,sizeof(CONTABLE));
[src->ct is of type CONTABLE *, src->ct is the table size,
LINELOC is a macro concatenating file name and line number,
and morespace is an allocator routine that resizes a storage
object that takes care of things like failures.]
Since `char*' and `CONTABLE*' may have different sizes and
representations, both references to `*a' in the last statement
are suspect. They rely not on the C language, but on peculiarities
of particular C implementations, and are not guaranteed to work
as you desire.
When exparray is called it determines a new table size, allocates
space for the new table, copies in the old table, and changes the
table pointer and table size in the calling routine.
The morespace prototype is (void *)morespace(void *,size_t,size_t,char *,
i.e., it takes a generic pointer and returns one. I could play
the same trick with exparray, but that's not nice for a couple or
reasons. The quoted code isn't nice either because of the (char **) cast. What would be nice is usage that looks like
exparray(&(src->ct),&src->sz_ct,sizeof(CONTABLE));
with a prototype
void exparray(void *, size_t *,size_t)
without any hackery inside.
If you know that the arrays being resized are always arrays
of structs, you can take advantage of guarantee (2) above. But
by using a `void*' as the first parameter you give up any chance
of getting the compiler to enforce your "must be a struct pointer"
requirement.
Have you considered passing a pointer to the `src' object (a
sort of "array descriptor," it appears) instead of individual
pointers to bits and pieces of its contents?
--
Eric.Sosman@xxxxxxx
.
- Follow-Ups:
- Re: A question about void pointers
- From: Richard Harter
- Re: A question about void pointers
- References:
- A question about void pointers
- From: Richard Harter
- Re: A question about void pointers
- From: Eric Sosman
- Re: A question about void pointers
- From: Richard Harter
- A question about void pointers
- Prev by Date: Re: tree
- Next by Date: Re: volatile Keyword Question
- Previous by thread: Re: A question about void pointers
- Next by thread: Re: A question about void pointers
- Index(es):
Relevant Pages
|