Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of void*, void ** and variadic function

Tags:

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 :)

like image 998
Gam Avatar asked Feb 16 '18 12:02

Gam


People also ask

What is variadic function in C?

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.

What is variadic function in Swift?

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 .

What is variadic function in postgresql?

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.

What means variadic?

variadic (not comparable) (computing, mathematics, linguistics) Taking a variable number of arguments; especially, taking arbitrarily many arguments.


2 Answers

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.


Alternative

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);

Alternative 2

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
like image 101
chux - Reinstate Monica Avatar answered Oct 09 '22 03:10

chux - Reinstate Monica


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);
like image 43
bolov Avatar answered Oct 09 '22 01:10

bolov