0

There are times when its useful to do an assignment in a macro, but this is prevented by casting, eg:

#define SAFE_FREE(v) do { free(v); v = NULL; } while (0)

/* example use */
SAFE_FREE(foo);

however if 'foo' is a 'const int *', a cast is needed.

free((void *)foo);  /* OK */

but this fails because of the cast & assignment

SAFE_FREE((void *)foo);

Gives the warning: error: lvalue required as left operand of assignment

One possible solution is to cast in the macro: eg,

#define SAFE_FREE(v) do { free((void *)v); v = NULL; } while (0)

But I'd prefer not to cast within the macro, since this could end up hiding cases where it should warn.


Is there a way to assign a variable in a macro that happens to have a cast prefix?

ideasman42
  • 42,413
  • 44
  • 197
  • 320

3 Answers3

1

A cast is not an lvalue, so it can appear neither on the left-hand side of an assignment operator nor as the argument of the unary & operator. In other words, no, there is no construct that lets you assign to a cast lvalue. It's lvalueness has been removed by the cast.

The fact that free insists on a non-const pointer is a bit annoying, but it can also occasionally help you find bugs. But there it is. Accepting that, you could always define both SAFE_FREE and SAFE_CONST_FREE.

(Opinion-based comment on the use of the word "SAFE" in "SAFE_FREE" removed. Opinion remains.)

rici
  • 234,347
  • 28
  • 237
  • 341
  • Weather `SAFE_FREE` is really safe or not is besides the point, the question is about assignment of macros within a cast. – ideasman42 Feb 27 '15 at 03:56
  • @ideasman42: The answer came before the opinion. Now it's all that is left. – rici Feb 27 '15 at 03:59
1

The problem is probably with the macro part v=NULL because foo is constant. Can you change foo to just int*?

  • No, thats not the problem. `v = NULL;` runs just fine, the problem is passing it to a function which takes a `void *` arg. – ideasman42 Feb 27 '15 at 03:44
  • But looks like a problem with the macro, as in one part you have the substitution as: free ( (void *) foo) ; then ( void *) foo = NULL which as you mentioned the v = NULL is using a cast in the left side, where you cannot use casts on the left side as it marks you the lvalue issue – Armando Ruiz Feb 27 '15 at 04:07
0

Freeing a pointer to const doesn't make sense because it changes the object (from existing to not existing). So my first suggestion would be to arrange your code so that this is not necessary.

If you want to allow pointers to const to be freed, but don't want your cast to get rid of anything else important, then you could do:

inline void const_free(void const *ptr) { free((void *)ptr); }

#define SAFE_FREE(ptr) do { const_free(ptr); (ptr) = NULL; } while (0)

NB. see here for notes about proper use of inline in headers in C

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365