I want to write a function that frees as many pointers as wanted. So I have this one :
void myfree(size_t n, ...)
{
void **del;
va_list ap;
va_start(ap, n);
while (n > 0)
{
del = va_arg(ap, void **);
free(*del);
*del = NULL;
--n;
}
va_end(ap);
}
I call it like that :
char *one = malloc(x);
sometype_t *two = malloc(x);
myfree(2, &one, &two);
I assume that one
and two
are now pointing to NULL.
It seems to be working, but I'm still a bit worried. I did some researches about void**
but I'm not sure if my function is really valid (UB are you there ?)
During my researches and tests, I tried some things I do not really understand.
Let's say we have this function
void f(void **ptr)
{
}
If I call it like this
int *intptr = NULL;
f(&intptr);
I get a compiler warning : warning: incompatible pointer types passing 'int **' to parameter of type 'void **'
So I tried this
int *intptr = NULL;
f( & ((void *)intptr) );
Compiler error : error: cannot take the address of an rvalue of type 'void *'
But if I do
void *voidptr = NULL;
f(&voidptr); // I can take the address of a void pointer right ?
Actually void*
, void**
and conversions/casts with them are still unclear to me. That is why I'm worried about myfree() I posted above.
If any of you have some explanations I would be grateful :)
Variadic functions are functions that can take a variable number of arguments. In C programming, a variadic function adds flexibility to the program. It takes one fixed argument and then any number of arguments can be passed.
In computer programming, a variadic function is a function which accepts a variable number of arguments. The function arguments are represented by … (three period characters) after the argument's type that can be accessed into their body as an array .
You can refer VARIADIC FUNCTIONS IN POSTGRESQL for details. See the wiki about Variadic functions: In computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages.
variadic (not comparable) (computing, mathematics, linguistics) Taking a variable number of arguments; especially, taking arbitrarily many arguments.
First half code is likely OK yet may invoke undefined behavior (UB).
In calling myfree(2, &one, &two);
, there are no conversions to void *
nor void **
. The pointers &one, &two
are passed unconverted as char **
, sometype_t **
.
It is the implementation of myfree()
that assumes the pointers are a compatible type to void **
when it does:
void ** del = va_arg(ap, void **);`
Salient specification:
The
va_arg
macro expands .... If ... type is not compatible with the type of the actual next argument ..., the behavior is undefined C11dr §7.16.1.1 2
The trick is that a pointer of void **
may not be compatible with a char **
, nor sometype_t **
. Since no explicit conversion is coded, this is only OK if those pointer types are compatible. Although this is very likely on the many architectures, it is not specified and so UB.
With OP's 2nd half code, the conversion is done directly and the compiler warns:
I get a compiler warning : warning: incompatible pointer types passing 'int **' to parameter of type 'void **'
In this case the conversion is coded as part of f(&intptr);
. §7.16.1.1 2 does not apply here. The specification of note:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr §6.3.2.3 7
With decades of experience, I've never came across nor heard of a system that had pointers to pointers of different alignment. So this is low risk UB, yet I would heed the warning and code otherwise.
Pass and expect to receive void *
pointers. Note that this does not set the free'd pointer to NULL
.
void myfree(size_t n, ...) {
void *del; // one *
va_list ap;
va_start(ap, n);
while (n > 0) {
del = va_arg(ap, void *);
free(del);
--n;
}
va_end(ap);
}
Character pointers are compatible with void*
and do not require a cast. Arbitrary object pointers need a cast. This is similar to the need for the cast in printf("%p\n", (void*) two);
char *one = malloc(x);
sometype_t *two = malloc(x);
void *three = malloc(x);
myfree(3, one, (void*) two, three);
I want to write a function that frees as many pointers as wanted.
Maybe instead of myfree(n, ...)
, how about coding some of the more popular sizes, even as a macro, and leave it go at that? This certainly meets the majority of coding needs and, of course, avoids a myfree(3, p1, p2)
error. Recall that is is OK to call free4(p1, p2,p3,0);
free2(void *p1, void *p2)`
free3(void *p1, void *p2, void *p3)
free4(void *p1, void *p2, void *p3, void *p4)
// maybe a few others
as @dbush said in his answer your code has UB. If you want to keep your function then you can pass the pointer directly, free the memory and not modify the pointer. The setting to NULL is mostly useless anyway.
void myfree(size_t n, ...)
{
void* del;
va_list ap;
va_start(ap, n);
while (n > 0)
{
del = va_arg(ap, void *);
free(del);
--n;
}
va_end(ap);
}
char *one = malloc(x);
sometype_t *two = malloc(x);
myfree(2, one, two);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With