As a special case, C specifies that there are automatic conversions between void *
and all other object-pointer types. This is not any kind of pattern, but rather a provision specifically for type void *
. That is, there is no analogous special case for void **
.
You can manually (via typecast) convert among different object pointer types, such as between char **
and void **
. Some compilers may even provide such conversions implicitly, which constitutes a language extension. But writing through such a converted pointer violates the strict aliasing rule, producing undefined behavior.*
Overall, no, you cannot make a function of type void f(void **)
serve as a general-purpose function for freeing memory and setting pointers to NULL
. Alternatives include:
use a macro instead:
#define free(p) do { free(p); p = NULL; } while (0)
pass an additional argument that conveys the pointer's actual target type:
enum target_type { VOID, CHAR, INT, DOUBLE };
void freep(void *pp, enum target_type type) {
switch (type) {
case VOID: {
void **p = (void **) pp;
free(*p);
*p = NULL;
break;
}
case CHAR: {
char **p = (char **) pp;
free(*p);
*p = NULL;
break;
}
case INT: {
int **p = (int **) pp;
free(*p);
*p = NULL;
break;
}
case DOUBLE: {
double **p = (double **) pp;
free(*p);
*p = NULL;
break;
}
}
}
Of course, this requires you to choose in advance which pointer types are supported, so it is not altogether general. But if you want something like this then it might be a good use case for X macros to generate the enum definition and matching function implementation without so much boilerplate.
In principle, you could also use a type-generic macro as a front end to multiple type-specific free-and-nullify functions, but I don't see much reason to prefer that over both the preceding options. If you're ok with a macro, then the former is much simpler, and if you're not then you're not.
* One might argue that writing a char *
through a void **
-- *voidpp = NULL
-- is an allowed case, given that char *
and void *
are required to have the same representation and alignment requirement, and are generally intended to be interchangeable. But even if one accepts that, it is (another) special case. That does not address your question in its full generality.