In C, why do some people cast the pointer before freeing it?
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.
- 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.
Related videos on Youtube
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, 2022Comments
-
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 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 over 8 yearsAccording 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 over 8 years@Codor The compiler doesn't do the deallocation, the operating system does.
-
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 tomalloc()
once again. -
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 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 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 over 8 yearsWhat are types of
velocity
,acceleration
andlabel
? If they are integers, cast would make sense, considering old code. -
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 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 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 over 8 years@user694733 Fine. I said if it doesn't have other unwanted side-effects.
-
Lundin over 8 yearsIn ancient days when dinosaurs walked the earth, and wrote programming books, I believe there was no
void*
in pre-standard C, but onlychar*
. 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 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 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 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 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 underlyingmalloc
, 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 over 8 years@sqykly: That's...what he said?
-
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 over 8 years@sqykly: it's for the reader to know how much will be freed.
-
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 anint*
(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 over 8 yearsSince in K&R
malloc
returned achar*
, and you said that this was really old code, maybe whoever wrote this was used to casting the result ofmalloc
, but he didn't know why and figured that he needed to cast the parameter tofree
. -
Seng Cheong over 8 years@sqykly You should have a look at the FreeBSD or Solaris kernel sources.
free()
takes a size, too. -
GMasucci over 8 yearsI 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 over 8 yearsLooking 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 over 8 yearsNote that in the code posted in the question, the casts are not to
void *
, but tofloat *
andchar *
. Those casts are not just extraneous, they're wrong. -
m0skit0 over 8 yearsThe question is actually about the opposite.
-
user694733 over 8 years@AndrewHenle
float *
andchar *
are implicitly converted tovoid *
in C, so the end result is the same. -
Andrew Henle over 8 yearsThe cast in the question is
free((float *)velocity);
. Not tovoid *
. -
m0skit0 over 8 years@AndrewHenle The cast in this answer is also to
float*
. -
Manos Nikolaidis over 8 years@AndrewHenle The cast is
free((float *)velocity);
in my answer butfree((void *)velocity);
would also clear the warning -
m0skit0 over 8 years@ManosNikolaidis As Lundin comments: "there was no
void*
in pre-standard C, but onlychar*
" -
Codor over 8 yearsI don't understand the answer; in what sense would
free(p)
fail? Would it give a compiler error? -
Manos Nikolaidis over 8 years@m0skit0 that doesn't explain why someone would cast to
float*
before freeing. I triedfree((void *)velocity);
with gcc 4.8.3. Of course it wouldn't work with an ancient compiler -
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 usevoid*
like you're suggesting. The question states several times it is a very old code. Very in like GCC didn't even exist. -
sjsam over 8 yearsCould you include the warning in your answer please?
-
Lundin over 8 yearsThese are good points. Same goes with
const
qualifier pointers, obviously. -
jwodder over 8 yearsBut why would you cast the argument to
free
to the same type that it already is? -
Nils_M over 8 yearsBut why would you need to dynamically allocate constant memory? You could never use it!
-
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
intovoid*
, which don't make any sense neither pre nor post standardization. -
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 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 over 8 yearsWith pre-standard C and no
void *
, did not that oblige the code to cast tochar *
? Unclear how casting toint *
(or any non-char *
) would be used in this context. -
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 over 8 yearsSounds good to me that pre-standard used K&R as standard - warts and all. Those disagree about "harms readability".
-
Ian Abbott over 8 yearsPre-standard C didn't have
void*
, but it didn't have function prototypes either, so casting the argument offree
was still unnecessary even in K&R (assuming all data pointer types used the same representation). -
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 over 8 years@m0skit0: out of curiosity…these ancient compilers (that don’t even know
void*
) issue warnings when a pointer to aconst
data type is assigned to a non-const
one? Really? Do they actually knowconst
? Or are these possible reasons excluding each other? -
Holger over 8 yearsThe 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 withC99
… -
R.. GitHub STOP HELPING ICE over 8 yearsFor multiple reasons stated in the comments already, I don't think this answer makes sense.
-
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 over 8 years
restrict
is only a non-issue because of where it's placed -- it affects the objectrp
not the pointed-to type. If you instead hadchar *restrict *rp
, then it would matter. -
m0skit0 over 8 years@Holger No idea, ask Lundin.
-
AnT stands with Russia over 8 yearsI 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 withoutvoid
? What would such casts achieve? -
Carsten S over 8 yearsExample:“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 over 8 yearsThe 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 wereconst
orvolatile
, then there's another answer explaining the purpose of the casts. -
Deduplicator over 8 years@gnasher729: Rather, you do the cast which is least likely to go wrong. If you later change from
double
tofloat
, the cast todouble*
would be silent UB (you said it was alright, so the compiler cannot check), while a cast tovoid*
would keep being right. -
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 over 8 yearsErroneous, if a function takes
const char *p
as an argument and then frees it, the correct thing to do isn't to castp
tochar*
before calling free. It's to not declare it as takingconst 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 over 8 yearsseems 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 over 8 yearsThe first half I fully agree with. And the 2nd half is an intriguing and plausible conjecture.
-
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 over 8 years@Dr.PersonPersonII Yep, I suspected as much. So the answer is simply: "the cast doesn't make any sense".
-
haccks over 7 yearsCasting may be required to resolve compiler warnings if the pointers are
const
: I can not see anyconst
pointer in the answer. -
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 over 2 yearsA 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 achar*
using two words. Code which passedfree()
a pointer that was not representation-compatible withvoid*
orchar*
would need a cast or prototype to function correctly.