2

I want a macro to free multiple (variadic number) pointers of different type. Based on similar questions in SO I made this code which seems to work

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

/* your compiler may need to define i outside the loop */
#define FREE(ptr1, ...) do{\
    void *elems[] = {ptr1, __VA_ARGS__};\
    unsigned num = sizeof(elems) / sizeof(elems[0]);\
    for (unsigned i=0; i < num; ++i) free(elems[i]);\
} while(0)


int main(void) 
{
    double *x = malloc(sizeof(double)); /* your compiler may need a cast */
    int    *y = malloc(   sizeof(int)); /* ditto */

    FREE(x, y); 
}

My question is

  • Is the creation of a void* array correct in this context? (I saw the same trick with *int[], so the question is will a *void[] do what I expect)
  • Is the code C99 compliant, are there any compilers that would have problems with this?
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • If you're aiming for C99, why do you declare `i` outside the loop?! And why do you cast the result of `malloc`? – Kerrek SB Jan 20 '15 at 09:18
  • @KerrekSB I'm somewhat uptight when writing C; The ghost of past versions made me do it :P ... will edit the question, thnx – Nikos Athanasiou Jan 20 '15 at 09:25
  • Side notes: 1. You can get rid of that `ptr1` in your macro. 2. Declare `i` as `unsigned` (similarly to `num`). 3. Function `main` should return a value. 4. You should also include `stdlib.h`. – barak manos Jan 20 '15 at 09:33
  • @barakmanos 1. Don't won't to risk creating 0sized arrays and have incomprehensible error messages due to macro expansion (better off with a mandatory argument) 2. Agree 3. Implicit return types – Nikos Athanasiou Jan 20 '15 at 09:36
  • It will not create a 0-size array. You'll get a compilation error if you try to "invoke" `FREE()`. – barak manos Jan 20 '15 at 09:38
  • your comment "prior to C99 you need..." but It does not work in C99 previous – BLUEPIXY Jan 20 '15 at 09:38
  • @barakmanos Last return value of main in C99 can be omitted. – BLUEPIXY Jan 20 '15 at 09:42
  • @BLUEPIXY Ok, a little bit of confusion there. I added a more generic message; I'm using VS2012 and its implementation of C99 doesn't include those feature, I left the comments for those that are stuck in the middle (as I am) – Nikos Athanasiou Jan 20 '15 at 09:44
  • If you don't get rid of `ptr1`, I think you *can't* legally use this with only one argument (although most compilers will allow it and the `...` can be empty). While it doesn't add anything to `free` in that case, it's nice for such things to be generic. – Alex Celeste Jan 20 '15 at 09:46
  • @Leushenko Yes, you can – Nikos Athanasiou Jan 20 '15 at 09:49
  • @barakmanos The error messages would be `Not enough actual parameters for macro FREE` vs `syntax error }`. Both are correct but I opted for the first – Nikos Athanasiou Jan 20 '15 at 09:51
  • VS2012 doesn't have C99 support, does it? As I recall first signs of improved support started in VS2013. Do you compile it with VS2012 in C++ mode? – keltar Jan 20 '15 at 09:59
  • While I have no doubt that this macro could work, all it seems to do is to remove some typing at the expense of readability and memory use (unless free list is big, which is usually not the case). – user694733 Jan 20 '15 at 10:01
  • 1
    @barakmanos After the answer by Leushenko, I'll have to admit the trade offs are not quite cutting it for the `warnings` case. Thnx for initially pointing the `ptr1` problem – Nikos Athanasiou Jan 20 '15 at 10:23

2 Answers2

2

That's pretty cool, and yes it's correct to use void *.

You could improve it somewhat (more const, and of course use size_t instead of unsigned) but in general it seems alright.

Also, drop the casts in main(), there's no need to cast the return value of malloc() in C and doing so can mask actual errors so it's just bad.

To address @Leushenko's answer, you might be able to glue something together by adding an extra macro expansion step that always adds a NULL in the varargs macro call. That way, you're never going to call the actual varargs macro with just a single argument, even if the toplevel macro is called with only one. Of course, calling free(NULL) is always safe and well-defined, so that should work.

Community
  • 1
  • 1
unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    Actual errors which the OP *is actually committing* by not including `stdlib.h` :-) – Kerrek SB Jan 20 '15 at 09:28
  • It looks like VS implements C99 in its own "flavour" , I need to use the initial code, but I updated the question to comply with the corrections (and C99 ), thnx – Nikos Athanasiou Jan 20 '15 at 09:40
2

One potential usability problem with this is that it doesn't scale to freeing only a single pointer, similar to the regular free. While this isn't necessary (since you could require the user to spot this and use free), it's usually elegant for things to be as generic as possible and automatically scale themselves to fit such use cases.

C99 (also C11) standard section 6.10.3 paragraph 4:

If the identifier-list in the macro definition does not end with an ellipsis ... Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).

i.e. in strictly conforming C, the __VA_ARGS__ must be used. GCC will even highlight this for you (a compiler can't prove something is compliant, but it can warn you when it isn't) when using -std=c99 -Wall -pedantic:

test.c: In function 'main':
test.c:18:11: warning: ISO C99 requires rest arguments to be used [enabled by default] FREE(x); ^

Technically you don't need the actual value, just the trailing comma (FREE(x,); - an empty macro argument is still an argument, and the array initializer it populates also allows trailing commas), but that's not very... integrated with the language.

In practice real compilers won't directly object to missing rest-args, but they might warn about it (as shown above), because a non-fatal error is often reasonable to interpret as a sign that something is wrong elsewhere.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • Ok, so we're back to `lose ptr1` ? – Nikos Athanasiou Jan 20 '15 at 10:17
  • Well, it's an argument for it (that it might cause warnings, which some people don't like). If you still think the tradeoff against better potential error messages is worthwhile, keep it. – Alex Celeste Jan 20 '15 at 10:18
  • @Nope compliance beats warnings, will go credit barak manos as well. Thnx for being thorough (I'll leave the question uneditted so that this answer stays in context) – Nikos Athanasiou Jan 20 '15 at 10:21