C question: the only dereference to void ** dual direction indicator

I received 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 that I think this solution after reading this stack overflow question:
Avoid incompatible pointer warnings when working with double direction - Stack Overflow

So I adapted 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'm looking for a pointer to the void, right?

Thanks guys,

EDIT 10/12/2010 16:45 EST:
As stated, free(NULL) is safe and covered by the C standard. Also, as discussed below, my example above is not legal. See Answers in the cafe, Zack's answer and my own answer.

Therefore, it will be easier for me to initialize any to-be-malloc'd pointers as NULL, and then later just release () and NULL directly in the code:

 free( pointer ); pointer = NULL; 

The reason I checked NULL in myfree (), like me, was because of my experience with fclose (). fclose(NULL) may segfault depending on the platform (e.g. xpsp3 msvcrt.dll 7.0.2600.5512), and so I assumed (erroneously) the same thing could happen with free (). I figured out rather than cluttering up my code if I could better implement the function.

Thank you all for a good discussion.

+6
c type-conversion compiler-warnings void-pointers void
source share
4 answers

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

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

7 The object must have a stored value, accessible only by the value of the lvalue expression, which 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 restriction has been violated. The condition for two compatible pointer types is described in section 6.7.5.1:

For two types of pointers are compatible, both must be the same and both must be pointers to compatible types.

To achieve the desired effect, you should 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).

+4
source share

This is completely legal, but it can confuse other people who read your code.

You can also use casting to eliminate the warning:

 myfree((void **)&rest); 

This is more readable and understandable.

+2
source share

In C, you have no choice but to enter a throw somewhere here. I would use a macro to make sure everything was done correctly on the call site:

 void myfree_(void **ptr) { if (!ptr || !*ptr) return; free(*ptr); *ptr = 0; } #define myfree(ptr) myfree_((void **)&(ptr)) 

[In fact, you can call the function and macro "myfree", thanks to the rules of C no-infin-macro-recursion! But that would be strange to readers. During the lengthy discussion below the answer in the cafe, I also stipulate that the expression *ptr = 0 here modifies an object of an unknown type using the alias void** , which represents runtime behavior

In C ++, you can use the template function, which is better for three points: it avoids the need to accept the address of something on the call site, it does not violate the correctness of the type, and you get a compile-time error instead of a crash during execution if you accidentally pass not a pointer to myfree .

 template <typename T> void myfree(T*& ptr) { free((void *)ptr); ptr = 0; } 

But of course in C ++ you have even better options available, such as smart pointer and container classes.

Finally, it should be mentioned that experienced C programmers avoid this type of wrapper because it will not help you when there is another copy of the pointer to the memory that you just freed hung somewhere somewhere - and that’s exactly when you need help.

+1
source share

The answer to the cafe is correct: No, this is not legal . And since Zack points out breaking the law in this way, it seems to be the least likely to cause problems.

I found what seems like a different solution in the comp.lang.c question list Β· Question 4.9 , which states that the intermediate void should be used.

 #include <stdio.h> #include <stdlib.h> void myfree( void **v ) { if( !v ) return; free( *v ); *v = NULL; return; } int main( int argc, char *argv[] ) { double *num; if( ( num = malloc( sizeof( double ) ) ) ) { printf( "before: %p\n", num ); { void *temp = num; myfree( &temp ); num = temp; } printf( "after: %p\n", num ); } return 0; } 


+1
source share

All Articles