[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.

Attachment: pgp_5TymyHo6I.pgp
Description: PGP signature

Reply via email to