Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C question: single dereference on a void** double indirection pointer

I got this message:

expected 'void **' but argument is of type 'char **'

when I tried to compile something similar to this:

void myfree( void **v )
{
    if( !v || !*v )
        return;

    free( *v );
    *v = NULL;

    return;
}



I found what I think is a solution after reading this question on stack overflow:
Avoid incompatible pointer warning when dealing with double-indirection - Stack Overflow

So I adjusted to something like this:

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

void myfree( void *x )
{
    void **v = x;

    if( !v || !*v )
        return;

    free( *v );
    *v = NULL;

    return;
}

int main( int argc, char *argv[] )
{
    char *test;

    if( ( test = malloc( 1 ) ) )
    {
        printf( "before: %p\n", test );
        myfree( &test );
        printf( "after: %p\n", test );
    }

    return 0;
}

Is this legal C? I am dereferencing a void pointer aren't I?

Thanks guys


EDIT 12/10/2010 4:45PM EST:
As it has been pointed out free(NULL) is safe and covered by the C standard. Also, as discussed below my example above is not legal C. See caf's answer, Zack's answer, and my own answer.

Therefore it's going to be easier for me to initalize any to-be-malloc'd pointers as NULL and then later on to just free() and NULL out directly in the code:

free( pointer );
pointer = NULL;

The reason I was checking for NULL in myfree() like I did was because of my experiences with fclose(). fclose(NULL) can segfault depending on platform (eg xpsp3 msvcrt.dll 7.0.2600.5512) and so I had assumed (mistakenly) the same thing could happen with free(). I had figured rather than clutter up my code with if statements I could better implement in a function.

Thanks everyone for all the good discussion

like image 992
Anonymous Question Guy Avatar asked Dec 06 '10 21:12

Anonymous Question Guy


1 Answers

No, this is not legal C, unless you pass the address of a void * object to myfree() (so you might as well just keep your original definition).

The reason is that in your example, an object of type char * (the object declared as test in main()) is modified through an lvalue of type void * (the lvalue *v in myfree()). §6.5 of the C standard states:

7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of 
the object,
— a type that is the signed or unsigned type corresponding to the effective
type of the object,
— a type that is the signed or unsigned type corresponding to a qualified
version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned
types among its members (including, recursively, a member of a subaggregate
or contained union), or
— a character type.

Since void * and char * are not compatible types, this constraint has been broken. The condition for two pointer types to be compatible is described in §6.7.5.1:

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

To achieve the effect you want, you must use a macro:

#define MYFREE(p) (free(p), (p) = NULL)

(There is no need to check for NULL, since free(NULL) is legal. Note that this macro evaluates p twice).

like image 96
caf Avatar answered Sep 30 '22 01:09

caf