[neomutt removed, since I can't post there.]
On Tue, Mar 27, 2018 at 03:04:34PM +0800, Yubin Ruan wrote:
> In mutt/memory.c, what is point of casting ptr from (void *) to (void **)? Any
> ancient C technique here?
>
> void mutt_mem_free(void *ptr)
> {
> if (!ptr)
> return;
> void **p = (void **) ptr;
> if (*p)
> {
> free(*p);
> *p = 0;
> }
> }
>
> Yubin
This does not exist in the Mutt source. It probably shouldn't...
The implementation is arguably bad and using it potentially invites
bugs.
For example:
...
char *x = (char *)malloc(buffsz);
/* do some stuff with x *
...
mutt_mem_free(x);
/* later... */
if (x) do_some_stuff();
This would likely crash, or at worst behave unexpectedly, because x
was not set to NULL when mutt_mem_free() was called on it. This will
only work correctly if the call was:
mutt_mem_free(&x);
The reason for this is that in order to set x to NULL you need to pass
in its address, otherwise you're only setting mutt_mem_free()'s
parameter to NULL, not the pointer it was assigned from. With this
implementation, the compiler will allow you to pass in either x or &x,
because both are acceptably cast to void pointers in function calls.
Ideally what you would want instead (for char** at least) is:
void xfree(char **x)
{
if (x && *x){
free(*x);
*x = 0;
}
}
[Note the vastly simpler implementation and the ability for the
compiler to do proper type checking.] However, it was probably
written as it was to make it type-agnostic, because passing (for
example) a char** to a function that takes a void** will still
generate a compiler warning for mismatched types (I'm not sure if that
behavior is standard, but it does on the compiler I'm using ATM at
least). It *will* work, but only if you never do it wrong--it
prevents the compiler from checking for you.
I think the better overall solution is to have a version like what I
wrote above for the most common case--C strings (char**). Then if you
find you need a deallocator for other types (like structs) write one
explicitly for each of those types, so the compiler can correctly
match the types at compile time. If you have a lot of them that would
get tediously repetetive, but you could minimize that repetition by
using macros to generate them.
Alternatively, use C++; just define constructors and destructors for
your structs/classes as you usually would. =8^)
--
Derek D. Martin http://www.pizzashack.org/ GPG Key ID: 0xDFBEAD02
-=-=-=-=-
This message is posted from an invalid address. Replying to it will result in
undeliverable mail due to spam prevention. Sorry for the inconvenience.
pgp_5TymyHo6I.pgp
Description: PGP signature
