I've tried to read up on the other questions here on SO with similar titles, but they are all a tiny bit too complex for me to be able to apply the solution (or even explanation) to my own issue, which seems to be of a simpler nature.
In my case, I have a wrapper around free()
which sets the pointer to NULL
after freeing it:
void myfree(void **ptr)
{
free(*ptr);
*ptr = NULL;
}
In the project I'm working on, it is called like this:
myfree((void **)&a);
This makes gcc
(4.2.1 on OpenBSD) emit the warning "dereferencing type-punned pointer will break strict-aliasing rules" if I crank up the optimization level to -O3
and add -Wall
(not otherwise).
Calling myfree()
the following way does not make the compiler emit that warning:
myfree((void *)&a);
And so I wonder if we ought to change the way we call myfree()
to this instead.
I believe that I'm invoking undefined behaviour with the first way of calling myfree()
, but I haven't been able to wrap my head around why. Also, on all compilers that I have access to (clang
and gcc
), on all systems (OpenBSD, Mac OS X and Linux), this is the only compiler and system that actually gives me that warning (and I know emitting warnings is a nice optional).
Printing the value of the pointer before, inside and after the call to myfree()
, with both ways of calling it, gives me identical results (but that may not mean anything if it's undefined behaviour):
#include <stdio.h>
#include <stdlib.h>
void myfree(void **ptr)
{
printf("(in myfree) ptr = %p\n", *ptr);
free(*ptr);
*ptr = NULL;
}
int main(void)
{
int *a, *b;
a = malloc(100 * sizeof *a);
b = malloc(100 * sizeof *b);
printf("(before myfree) a = %p\n", (void *)a);
printf("(before myfree) b = %p\n", (void *)b);
myfree((void **)&a); /* line 21 */
myfree((void *)&b);
printf("(after myfree) a = %p\n", (void *)a);
printf("(after myfree) b = %p\n", (void *)b);
return EXIT_SUCCESS;
}
Compiling and running it:
$ cc -O3 -Wall free-test.c
free-test.c: In function 'main':
free-test.c:21: warning: dereferencing type-punned pointer will break strict-aliasing rules
$ ./a.out
(before myfree) a = 0x15f8fcf1d600
(before myfree) b = 0x15f876b27200
(in myfree) ptr = 0x15f8fcf1d600
(in myfree) ptr = 0x15f876b27200
(after myfree) a = 0x0
(after myfree) b = 0x0
I'd like to understand what is wrong with the first call to myfree()
and I'd like to know if the second call is correct. Thanks.
Since a
is an int*
and not a void*
, &a
cannot be converted to a pointer to a void*
. (Suppose void*
were wider than a pointer to an integer, something which the C standard allows.) As a result, neither of your alternatives -- myfree((void**)a)
and myfree((void*)a)
-- is correct. (Casting to void*
is not a strict aliasing issue. But it still leads to undefined behaviour.)
A better solution (imho) is to force the user to insert a visible assignment:
void* myfree(void* p) {
free(p);
return 0;
}
a = myfree(a);
With clang and gcc, you can use an attribute to indicate that the return value of my_free
must be used, so that the compiler will warn you if you forget the assignment. Or you could use a macro:
#define myfree(a) (a = myfree(a))