[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