Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C, why do some people cast the pointer before freeing it?

People also ask

What does casting a pointer do in C?

In the C language, casting is a construct to view a data object temporarily as another data type. When you cast pointers, especially for non-data object pointers, consider the following characteristics and constraints: You can cast a pointer to another pointer of the same IBM® i pointer type.

What happens to a pointer when it is freed?

Calling free() on a null pointer results in no action being taken by free() . Setting message to NULL after it is freed eliminates the possibility that the message pointer can be used to free the same memory more than once.

Can you use a pointer after freeing it?

Yes, when you use a free(px); call, it frees the memory that was malloc'd earlier and pointed to by px. The pointer itself, however, will continue to exist and will still have the same address. It will not automatically be changed to NULL or anything else.

Why do we need to cast malloc in C?

In C, you don't need to cast the return value of malloc . The pointer to void returned by malloc is automagically converted to the correct type. However, if you want your code to compile with a C++ compiler, a cast is needed.


Casting may be required to resolve compiler warnings if the pointers are const. Here is an example of code that causes a warning without casting the argument of free:

const float* velocity = malloc(2*sizeof(float));
free(velocity);

And the compiler (gcc 4.8.3) says:

main.c: In function ‘main’:
main.c:9:5: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected ‘void *’ but argument is of type ‘const float *’
 extern void free (void *__ptr) __THROW;

If you use free((float*) velocity); the compiler stops complaining.


Pre-standard C had no void* but only char*, so you had to cast all parameters passed. If you come across ancient C code, you might therefore find such casts.

Similar question with references.

When the first C standard was released, the prototypes for malloc and free changed from having char* to the void* that they still have today.

And of course in standard C, such casts are superfluous and just harm readability.


Here's an example where free would fail without a cast:

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

In C you can get a warning (got one in VS2012). In C++ you'll get an error.

Rare cases aside, the casting just bloats the code...

Edit: I casted to void* not int* to demo the failure. It will work the same as int* will be converted to void* implicitly. Added int* code.


Old reason: 1. By using free((sometype*) ptr), code is explicit about the type the pointer should be considered as part of the free() call. The explicit cast is useful when free() is replaced with a (do-it-yourself) DIY_free().

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

A DIY_free() was (is) a way, especially in debug mode, to do run-time analysis of the pointer being freed. This is often paired with a DIY_malloc() to add sententials, global memory usage counts, etc. My group used this technique for years before more modern tools appear. It obliged that the item being free'd was cast to the type is was originally allocated.

  1. Given the many hours spent tracking down memory issues, etc., little tricks like casting the type free'd would aid in searching and narrowing the debugging.

Modern: Avoiding const and volatile warnings as addressed by Manos Nikolaidis@ and @egur. Thought I would note the effects of the 3 qualifiers: const, volatile, and restrict.

[edit] Added char * restrict *rp2 per @R.. comment

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}

Here is another alternative hypothesis.

We are told that the program was written pre-C89, which means it can't be working around some kind of mismatch with the prototype of free, because not only was there no such thing as const nor void * prior to C89, there was no such thing as a function prototype prior to C89. stdlib.h itself was an invention of the committee. If the system headers bothered to declare free at all, they would have done it like this:

extern free();  /* no `void` return type either! */

Now, the key point here is that the absence of function prototypes meant the compiler did no argument type checking. It applied the default argument promotions (the same ones that still apply to variadic function calls) and that was it. Responsibility for making the arguments at each callsite line up with the callee's expectations lay entirely with the programmer.

However, this still doesn't mean it was necessary to cast the argument to free on most K&R compilers. A function like

free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

should have been compiled correctly. So I think what we've got here is a program written to cope with a buggy compiler for an unusual environment: for instance, an environment where sizeof(float *) > sizeof(int) and the compiler wouldn't use the appropriate calling convention for pointers unless you cast them at the point of the call.

I am not aware of any such environment, but that doesn't mean there wasn't one. The most probable candidates that come to mind are cut-down "tiny C" compilers for 8- and 16-bit micros in the early 1980s. I also wouldn't be surprised to learn that early Crays had problems like this.