Re: The void** pointer breaking symmetry?
- From: Michael Mair <Michael.Mair@xxxxxxxxxxxxxxx>
- Date: Sat, 06 May 2006 17:29:31 +0200
elmar@xxxxxxxxxx schrieb:
Thanks for your detailed reply, Keith.
Of which you unfortunately did not quote anything to provide
context.
In my humble view, the ideal solution would be:
1) void* is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !once!
(e.g. char*, int*, also char**, int**, but not char,int)
2) void** is a generic pointer type that can be implicitly converted
to/from any other object that can be dereferenced at least !twice!
(e.g. char**, int**, also char***, int***, but not char*,int*,char,int)
3) etc...
Again, I am not one of those who like to (have the time to) discuss
philosophic questions about language details. This is a purely
practical issue, having identified the main cause of entropy (and also
crashes ;-) in my current sources: The inability to safely pass a
pointer to any pointer as a function argument.
The problem with this approach is that if, for example, char *foo,
int *bar, and struct qux *quux have different sizes, alignment
requirements and representations, then there is no way that
void **baz (containing either the address of foo, bar, or quux, or
of a variable containing their addresses, or ...) helps you
deal with the object(s) they point to if you pass the addresses of
such pointers, because dereferencing the void ** variable once gives
you at least three possible interpretations of the bit pattern
involved. The only safe way to obtain the address of the object would
be if *baz would evaluate to a type which can contain any address that
can be pointed to by any of, foo, bar and quux. One such type is void*.
So,
void *aux = bar;
void **baz = &aux;
essentially is the best you can get if you do not want the language
to have to "remember" types at runtime.
Typical example: the function mem_freesetnull which frees a pointer and
sets it to NULL (very helpful in the context of exception handling):
Ideally, it would take the address of a pointer as argument and look
like that:
void mem_freesetnull(void **ptradd)
{ mem_free(*ptradd);
*ptradd=NULL; }
Unfortunately, that's not possible, because I'd have to use an ugly
explicit cast to (void**) in every call to the function.
So in practice, I have to move the explicit cast to the function
itself:
void mem_freesetnull(void *ptradd)
{ mem_free(*(void**)ptradd);
*(void**)ptradd=NULL; }
Now the function looks ugly, but more importantly, I lost an important
piece of type-safety:
If I accentally forget the reference operator & in the function call,
noone will complain but the program will crash:
char *cp;
cp=mem_alloc(1000);
mem_freesetnull(cp); /* Crash! */
Even
mem_freesetnull(&cp)
works portably only due to special guarantees for char*.
If you did the same for int *ip or struct qux *sp, you could run
into trouble on a system where not all pointers are equal.
With the approach suggested above, the compiler would immediately
identify the problem, since cp cannot be dereferenced at least 2 times,
as required by the ideal function declaration
void mem_freesetnull(void **ptradd).
Not portably if you do not want to change the information the
programme must have at runtime -- and then you could change other
restrictions of C as well.
In short: less code entropy and more safety in one shot.
At a very high price for the language.
In addition, not every place the now-stale address is stored will
be "nulled" by this function -- it is more or less a false sense
of safety. Realloc()ing to zero or even to a smaller size of the
originally malloc()ed storage gives you similar headaches.
Having a way to mark the starting address of the allocated object
and all addresses inside or one past the object as trap
representation with ways to find out where the whole thing trapped
would be much more useful.
Malloc debugging tools already do at least part of the job for you.
Just add "runs cleanly under <YourToolHere> for the test set" after
"compiles without warning" and "is <YourLintToolHere>-clean".
It seems that
others are bothered by the same thing, since this in the FAQ:
4.9: Can I use a void ** pointer as a parameter so that a function
can accept a generic pointer by reference?
A: Not portably.
(not sure what the answer means in this context. Does it cause problems
on a VAX from 1968? ;-)
I'm thinking about a GCC patch for an option to specifically disable
the warning in the cases outlined above. But if that has zero chance of
acceptance, I'll save my time ;-)
As I did not read what you really want to achieve (you snipped the
context), I do not know whether this is the perfect solution for you
or just the bad idea it seems to be...
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
.
- References:
- The void** pointer breaking symmetry?
- From: elmar
- Re: The void** pointer breaking symmetry?
- From: Eric Sosman
- Re: The void** pointer breaking symmetry?
- From: elmar
- Re: The void** pointer breaking symmetry?
- From: Keith Thompson
- Re: The void** pointer breaking symmetry?
- From: elmar
- The void** pointer breaking symmetry?
- Prev by Date: Re: declaration meaning
- Next by Date: Re: printf headache
- Previous by thread: Re: The void** pointer breaking symmetry?
- Next by thread: Re: The void** pointer breaking symmetry?
- Index(es):
Relevant Pages
|