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

14,800

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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

Solution 5

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.

Share:
14,800

Related videos on Youtube

SO Stinks
Author by

SO Stinks

Stack Overflow is a useful site but hampered with a lot of terrible rules and obnoxious mods. Can't wait until a rival site overtakes it.

Updated on June 11, 2022

Comments

  • SO Stinks
    SO Stinks almost 2 years

    I'm working on an old code base and pretty much every invocation of free() uses a cast on its argument. For example,

    free((float *)velocity);
    free((float *)acceleration);
    free((char *)label);
    

    where each pointer is of the corresponding (and matching) type. I see no point in doing this at all. It's very old code, so I'm left wondering if it's a K&R thing. If so, I actually wish to support the old compilers that may have required this, so I don't want to remove them.

    Is there a technical reason to use these casts? I don't even see much of a pragmatic reason to use them. What's the point of reminding ourselves of the data type right before freeing it?

    EDIT: This question is not a duplicate of the other question. The other question is a special case of this question, which I think is obvious if the close voters would have read all the answers.

    Colophon: I'm giving the checkmark to the answer that gave a reason why this might still need to be done; however, the answer about it being a pre-ANSI C custom (at least among some programmers) seems to be the reason it was used in my case. If there were two checkmarks to give they'd both get one. Lots of good points by many people here. Thank you for your contributions.

    • m0skit0
      m0skit0 over 8 years
      "What's the point of reminding ourselves of the data type right before freeing it?" Maybe to know how much memory will be freed?
    • Codor
      Codor over 8 years
      According to this documentation, the argument is of type void*. Perhaps the explicit casting is done due to the belief that some 'actual type' of the pointer is needed for the compiler to know how to do a correct deallocation?
    • m0skit0
      m0skit0 over 8 years
      @Codor The compiler doesn't do the deallocation, the operating system does.
    • fuz
      fuz over 8 years
      @m0skit0 That's actually an implementation detail, but traditionally, free() didn't return any memory to the operating system. It only made memory available to malloc() once again.
    • Codor
      Codor over 8 years
      @m0skit0 To my understanding, the actual type of the pointer is not necessary for the deallocation; is that correct or am I mistaken?
    • m0skit0
      m0skit0 over 8 years
      @FUZxxl You're right, it heavily depends on architecture and implementation, but speaking for hardware with a modern OS, malloc() also uses system calls to reserve memory.
    • user694733
      user694733 over 8 years
      @m0skit0 "Maybe to know how much memory will be freed?" Type is not necessary to know how much to free. Cast for that reason only is bad coding.
    • user694733
      user694733 over 8 years
      What are types of velocity, acceleration and label? If they are integers, cast would make sense, considering old code.
    • m0skit0
      m0skit0 over 8 years
      @Codor Correct, it is not necessary since the OS doesn't care (or even know) about the type the program is using. You're only telling the OS: "reserve me this much bytes starting from this address". This is tracked by the OS on your memory allocation table, so when you say: "ok, now free the memory starting from this address", the OS knows exactly how much memory to free.
    • m0skit0
      m0skit0 over 8 years
      @user694733 I know it is not necessary. I'm saying it is maybe a readability "feature". And casting for readability is not bad coding if it doesn't have other unwanted side-effects.
    • user694733
      user694733 over 8 years
      @m0skit0 Casting for readabilitys sake is always bad coding, because casting changes how types are interpreted and it may hide serious errors. When readability is needed, comments are better.
    • m0skit0
      m0skit0 over 8 years
      @user694733 Fine. I said if it doesn't have other unwanted side-effects.
    • Lundin
      Lundin over 8 years
      In ancient days when dinosaurs walked the earth, and wrote programming books, I believe there was no void* in pre-standard C, but only char*. So if your archaeological findings reveal code casting the parameter to free(), I believe it must either be from that time period, or written by a creature from that time. I can't find any source for this though, so I'll refrain from answering.
    • SO Stinks
      SO Stinks over 8 years
      @Lundin OF COURSE! Duh. It's obvious to me now. (Been coding too long right now. I should stop.) You are right, it's ancient code. Pre-ANSI C did not have void (although some compilers supported beforehand, of course). You should recast your comment as an answer. It may be the one I accept after a few days grace to give people the chance to answer.
    • user694733
      user694733 over 8 years
      @Dr.PersonPersonII I think Lundin might be at the right track, but I don't think it still explains the cast to float* completely. I think we need some veteran to explain ancient C type conversion rules.
    • Sebastian Mach
      Sebastian Mach over 8 years
      @m0skit0: Maybe to know how much memory will be freed?: I don't get how this tells the programmer about how much is freed.
    • sqykly
      sqykly over 8 years
      @m0skit0 this is not how it works. Read the documentation for the winapi functions VirtualFree, HeapFree etc, or think about it: how safe would it be to allow the user to free more or less memory than was actually allocated for a particular object? Is there any advantage to allowing this, i.e. can it ever succeed? The answers are both "no", which is why the memory allocation underlying malloc, regardless of whether it calls into the OS, keeps track of the size of each allocation on its own, and never needs to be told the size to free
    • Nick Matteo
      Nick Matteo over 8 years
      @sqykly: That's...what he said?
    • sqykly
      sqykly over 8 years
      @kundor apparently he did eventually. All but one comment appears to support the idea that the cast may be "to know how much memory will be freed". Not sure why one would write that knowing that "the OS knows exactly how much memory to free" later. You learn something new every day?
    • Nick Matteo
      Nick Matteo over 8 years
      @sqykly: it's for the reader to know how much will be freed.
    • sqykly
      sqykly over 8 years
      @kundor but we just established that nothing useful can come of that. If I allocate an array of int[10] and then free it as an int* (or anything else for that matter), the reader can't conclude that he can only use its [1] and up, the whole thing is gone. Further there would be no error if the type was wrong due to change or mistake, so concluding anything from the cast would be non-smart
    • NobodyNada
      NobodyNada over 8 years
      Since in K&R malloc returned a char*, and you said that this was really old code, maybe whoever wrote this was used to casting the result of malloc, but he didn't know why and figured that he needed to cast the parameter to free.
    • Seng Cheong
      Seng Cheong over 8 years
      @sqykly You should have a look at the FreeBSD or Solaris kernel sources. free() takes a size, too.
    • GMasucci
      GMasucci over 8 years
      I can remember firsthand having to cast before freeing, and yep that looks like exactly the era the code you mention is from, though I speculate a LOT as there's only 3 lines, and without context:) then theres the case mentioned in the next answer where it is used to cast away CONSTness . . .
    • jpmc26
      jpmc26 over 8 years
      Looking through the answers, I don't think this question should be closed. Some of the proposed answers are certainly not duplicate information, and the question isn't really opinion based. It's asking if this is some kind of best practice.
  • Andrew Henle
    Andrew Henle over 8 years
    Note that in the code posted in the question, the casts are not to void *, but to float * and char *. Those casts are not just extraneous, they're wrong.
  • m0skit0
    m0skit0 over 8 years
    The question is actually about the opposite.
  • user694733
    user694733 over 8 years
    @AndrewHenle float * and char * are implicitly converted to void * in C, so the end result is the same.
  • Andrew Henle
    Andrew Henle over 8 years
    The cast in the question is free((float *)velocity);. Not to void *.
  • m0skit0
    m0skit0 over 8 years
    @AndrewHenle The cast in this answer is also to float*.
  • Manos Nikolaidis
    Manos Nikolaidis over 8 years
    @AndrewHenle The cast is free((float *)velocity); in my answer but free((void *)velocity); would also clear the warning
  • m0skit0
    m0skit0 over 8 years
    @ManosNikolaidis As Lundin comments: "there was no void* in pre-standard C, but only char*"
  • Codor
    Codor over 8 years
    I don't understand the answer; in what sense would free(p) fail? Would it give a compiler error?
  • Manos Nikolaidis
    Manos Nikolaidis over 8 years
    @m0skit0 that doesn't explain why someone would cast to float* before freeing. I tried free((void *)velocity); with gcc 4.8.3. Of course it wouldn't work with an ancient compiler
  • m0skit0
    m0skit0 over 8 years
    @ManosNikolaidis I don't know why it is using float* (maybe because that was the original pointer type). What I do know is that it cannot use void* like you're suggesting. The question states several times it is a very old code. Very in like GCC didn't even exist.
  • sjsam
    sjsam over 8 years
    Could you include the warning in your answer please?
  • Lundin
    Lundin over 8 years
    These are good points. Same goes with const qualifier pointers, obviously.
  • jwodder
    jwodder over 8 years
    But why would you cast the argument to free to the same type that it already is?
  • Nils_M
    Nils_M over 8 years
    But why would you need to dynamically allocate constant memory? You could never use it!
  • Lundin
    Lundin over 8 years
    @jwodder Unless the type had a qualifier, as suggested in previous answers, there's probably no rationale for it, simple as that. Likely the person who wrote the code didn't understand why they had to cast the parameter to free() in the first place. Back in the old days, people did a lot of stupid things, period. The K&R book for example, has several examples of weird casts of the parameter to free into void*, which don't make any sense neither pre nor post standardization.
  • Manos Nikolaidis
    Manos Nikolaidis over 8 years
    @Nils_M it's a simplified example to make a point. What I have done in actual code in a function is allocate non-const memory, assign values, cast to a const pointer and return it. Now, there is a pointer to preassigned const memory that someone has to free.
  • Erroneous
    Erroneous over 8 years
    @Nils_M One reason you might free a const pointer (as opposed to a pointer to a const data type) would be in a function that accepts something like a const char * (c string) that contractually frees the memory. This is probably more likely inside of an object like struct or helper functions where the original author didn't want to free everything after calling it. Personally if I ever free a pointer I like to set it to null so I'd never free a const pointer.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 8 years
    With pre-standard C and no void *, did not that oblige the code to cast to char *? Unclear how casting to int * (or any non- char *) would be used in this context.
  • Lundin
    Lundin over 8 years
    @chux The problem with pre-standard is just that: there are no obligations for anything. People just pointed at the K&R book for canon because that was the only thing they had. And as we can see from several examples in K&R 2nd edition, K&R themselves are confused about how casts of the parameter to free work in standard C (you don't need to cast). I haven't read the 1st edition so I can't tell if they were confused in the 80s pre-standard times as well.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 8 years
    Sounds good to me that pre-standard used K&R as standard - warts and all. Those disagree about "harms readability".
  • Ian Abbott
    Ian Abbott over 8 years
    Pre-standard C didn't have void*, but it didn't have function prototypes either, so casting the argument of free was still unnecessary even in K&R (assuming all data pointer types used the same representation).
  • gnasher729
    gnasher729 over 8 years
    @AndrewHenle: Cast to float* or char* isn't wrong. free() handles anything that isn't const or volatile. As a rule to avoid bugs you always use the cast that makes the minimum change necessary, like const float* -> float*, not void*. So if you ever try to pass a const float* to a function wanting int* the compiler can still catch your error. Casting to void* hides that error.
  • Holger
    Holger over 8 years
    @m0skit0: out of curiosity…these ancient compilers (that don’t even know void*) issue warnings when a pointer to a const data type is assigned to a non-const one? Really? Do they actually know const? Or are these possible reasons excluding each other?
  • Holger
    Holger over 8 years
    The OP said that “pretty much every invocation of free()” in that code base has a cast which makes it unlikely that this is the reason. Plus the fact that the question is about a rather old codebase (there were speculations about K&R issues) and that volatile was added to the standard with C99
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 8 years
    For multiple reasons stated in the comments already, I don't think this answer makes sense.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 8 years
    volatile has existed since C was standardized if not longer. It was not added in C99.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 8 years
    restrict is only a non-issue because of where it's placed -- it affects the object rp not the pointed-to type. If you instead had char *restrict *rp, then it would matter.
  • m0skit0
    m0skit0 over 8 years
    @Holger No idea, ask Lundin.
  • AnT stands with Russia
    AnT stands with Russia over 8 years
    I don't see how this answer would really answer anything relevant. The original question involves casts to other types, not only to char *. What sense would it make in old compilers without void? What would such casts achieve?
  • Carsten S
    Carsten S over 8 years
    Example:“These subroutines return the string in newly malloc'ed memory, pointed to by *stringValueP, that you must eventually free. Sometimes, the OS function you use to free memory is declared to take a pointer to something non-constant as its argument, so because *stringValueP is a pointer to a const.”
  • Lundin
    Lundin over 8 years
    The answer explains the cast to char*. As for the cast to float, I believe it simply doesn't make any sense - maybe the cast sated some obscure pre-standard compiler. Unless of course the OP can dig up more code and show the variable declarations: if they were const or volatile, then there's another answer explaining the purpose of the casts.
  • Deduplicator
    Deduplicator over 8 years
    @gnasher729: Rather, you do the cast which is least likely to go wrong. If you later change from double to float, the cast to double* would be silent UB (you said it was alright, so the compiler cannot check), while a cast to void* would keep being right.
  • Deduplicator
    Deduplicator over 8 years
    @m0skit0: If it was that old (pre-1989), it would have to cast to char*. Or more likely there wouldn't be any qualifiers, and the type-mismatch would simply be ignored.
  • Ray
    Ray over 8 years
    Erroneous, if a function takes const char *p as an argument and then frees it, the correct thing to do isn't to cast p to char* before calling free. It's to not declare it as taking const char *p in the first place, since it modifies *p and should be declared as accordingly. (And if it takes a const pointer instead of pointer to const, int *const p, you don't need to cast since it's actually legal and thus works fine without the cast.)
  • robert bristow-johnson
    robert bristow-johnson over 8 years
    seems to me that if the function prototype takes void* as an argument, the compiler should be happy if any pointer goes into the argument.
  • chux - Reinstate Monica
    chux - Reinstate Monica over 8 years
    The first half I fully agree with. And the 2nd half is an intriguing and plausible conjecture.
  • SO Stinks
    SO Stinks over 8 years
    @Ludin No variables in the entire code base are declared with const or volatile. This is C code developed from around 85 to 88. There's really two reasons why it might have been done: A) whatever original compiler they were using required it, or B) it was simply a habit of the original programmer.
  • Lundin
    Lundin over 8 years
    @Dr.PersonPersonII Yep, I suspected as much. So the answer is simply: "the cast doesn't make any sense".
  • haccks
    haccks over 7 years
    Casting may be required to resolve compiler warnings if the pointers are const: I can not see any const pointer in the answer.
  • Maciej Piechotka
    Maciej Piechotka about 3 years
    "I am not aware of any such environment, but that doesn't mean there wasn't one." - x86_64 is one where sizeof(float *) > sizeof(int) though I don't think there are many K&R compilers/code for x86_64.
  • supercat
    supercat over 2 years
    A bigger issue would be platforms where pointer to different types have different sizes or representations, e.g. representing an int* using a word address held in one word, but representing a char* using two words. Code which passed free() a pointer that was not representation-compatible with void* or char* would need a cast or prototype to function correctly.