170

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.

SO Stinks
  • 3,258
  • 4
  • 32
  • 37
  • 13
    *"What's the point of reminding ourselves of the data type right before freeing it?"* Maybe to know how much memory will be freed? – m0skit0 Dec 01 '15 at 12:03
  • According to [this documentation](http://www.cplusplus.com/reference/cstdlib/free/), 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? – Codor Dec 01 '15 at 12:04
  • 12
    @Codor The compiler doesn't do the deallocation, the operating system does. – m0skit0 Dec 01 '15 at 12:04
  • 7
    @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. – fuz Dec 01 '15 at 12:06
  • @m0skit0 To my understanding, the actual type of the pointer is not necessary for the deallocation; is that correct or am I mistaken? – Codor Dec 01 '15 at 12:07
  • @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. – m0skit0 Dec 01 '15 at 12:09
  • 20
    @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 Dec 01 '15 at 12:09
  • 1
    What are types of `velocity`, `acceleration` and `label`? If they are integers, cast would make sense, considering old code. – user694733 Dec 01 '15 at 12:11
  • 1
    @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 Dec 01 '15 at 12:11
  • @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. – m0skit0 Dec 01 '15 at 12:12
  • 9
    @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. – user694733 Dec 01 '15 at 12:14
  • 1
    @user694733 Fine. I said **if it doesn't have other unwanted side-effects**. – m0skit0 Dec 01 '15 at 12:17
  • 67
    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. – Lundin Dec 01 '15 at 12:22
  • 4
    @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. – SO Stinks Dec 01 '15 at 12:33
  • 2
    @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. – user694733 Dec 01 '15 at 12:46
  • 4
    @m0skit0: `Maybe to know how much memory will be freed?`: I don't get how this tells the programmer about how much is freed. – Sebastian Mach Dec 01 '15 at 15:50
  • @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 – sqykly Dec 01 '15 at 19:49
  • 1
    @sqykly: That's...what he said? – Nick Matteo Dec 01 '15 at 20:08
  • @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? – sqykly Dec 01 '15 at 20:14
  • 1
    @sqykly: it's for the reader to know how much will be freed. – Nick Matteo Dec 01 '15 at 20:14
  • 1
    @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 – sqykly Dec 01 '15 at 20:26
  • 2
    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`. – NobodyNada Dec 01 '15 at 21:22
  • @sqykly You should have a look at the FreeBSD or Solaris kernel sources. `free()` takes a size, too. – Jonathon Reinhart Dec 02 '15 at 02:53
  • 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 . . . – GMasucci Dec 02 '15 at 11:04
  • 2
    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. – jpmc26 Dec 03 '15 at 03:56

6 Answers6

173

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
  • The cast in the question is `free((float *)velocity);`. Not to `void *`. – Andrew Henle Dec 01 '15 at 12:32
  • @AndrewHenle The cast in this answer is also to `float*`. – m0skit0 Dec 01 '15 at 12:32
  • @AndrewHenle The cast is `free((float *)velocity);` in my answer but `free((void *)velocity);` would also clear the warning – Manos Nikolaidis Dec 01 '15 at 12:34
  • @ManosNikolaidis As Lundin comments: *"there was no `void*` in pre-standard C, but only `char*`"* – m0skit0 Dec 01 '15 at 12:35
  • 2
    @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 – Manos Nikolaidis Dec 01 '15 at 12:40
  • @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. – m0skit0 Dec 01 '15 at 12:41
  • 56
    But why would you need to dynamically allocate constant memory? You could never use it! – Nils_M Dec 01 '15 at 13:50
  • 35
    @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. – Manos Nikolaidis Dec 01 '15 at 14:19
  • @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. – Erroneous Dec 01 '15 at 14:30
  • @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 Dec 01 '15 at 16:44
  • @Holger No idea, ask Lundin. – m0skit0 Dec 01 '15 at 17:55
  • 2
    [Example](http://xmlrpc-c.sourceforge.net/doc/libxmlrpc.html):“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.” – Carsten S Dec 01 '15 at 22:20
  • @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. – Deduplicator Dec 02 '15 at 13:00
  • 4
    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.) – Ray Dec 03 '15 at 01:06
  • 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. – robert bristow-johnson Dec 03 '15 at 03:03
  • 1
    *Casting may be required to resolve compiler warnings if the pointers are `const`*: I can not see any `const` pointer in the answer. – haccks Aug 31 '16 at 14:27
61

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.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 24
    But why would you cast the argument to `free` to the same type that it already is? – jwodder Dec 01 '15 at 13:49
  • 2
    @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. – Lundin Dec 01 '15 at 14:05
  • 2
    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. – chux - Reinstate Monica Dec 01 '15 at 15:26
  • 4
    @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. – Lundin Dec 01 '15 at 15:38
  • Sounds good to me that pre-standard used K&R as standard - warts and all. Those disagree about "harms readability". – chux - Reinstate Monica Dec 01 '15 at 15:44
  • 7
    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). – Ian Abbott Dec 01 '15 at 16:04
  • 6
    For multiple reasons stated in the comments already, I don't think this answer makes sense. – R.. GitHub STOP HELPING ICE Dec 01 '15 at 17:07
  • 4
    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? – AnT stands with Russia Dec 01 '15 at 20:56
  • 1
    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. – Lundin Dec 02 '15 at 07:49
  • @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. – SO Stinks Dec 03 '15 at 15:41
  • @Dr.PersonPersonII Yep, I suspected as much. So the answer is simply: "the cast doesn't make any sense". – Lundin Dec 03 '15 at 16:05
34

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.

egur
  • 7,830
  • 2
  • 27
  • 47
  • 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. – Andrew Henle Dec 01 '15 at 12:18
  • 1
    The question is actually about the opposite. – m0skit0 Dec 01 '15 at 12:18
  • @AndrewHenle `float *` and `char *` are implicitly converted to `void *` in C, so the end result is the same. – user694733 Dec 01 '15 at 12:20
  • 1
    I don't understand the answer; in what sense would `free(p)` fail? Would it give a compiler error? – Codor Dec 01 '15 at 12:35
  • Could you include the warning in your answer please? – sjsam Dec 01 '15 at 12:45
  • 1
    These are good points. Same goes with `const` qualifier pointers, obviously. – Lundin Dec 01 '15 at 12:58
  • @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. – gnasher729 Dec 01 '15 at 16:41
  • 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`… – Holger Dec 01 '15 at 16:54
  • 2
    `volatile` has existed since C was standardized if not longer. It was **not** added in C99. – R.. GitHub STOP HELPING ICE Dec 01 '15 at 17:08
  • @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 Dec 02 '15 at 12:53
33

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 appearred. It obliged that the item being free'd was cast to the type it 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;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 3
    `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. – R.. GitHub STOP HELPING ICE Dec 01 '15 at 17:10
18

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.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 2
    The first half I fully agree with. And the 2nd half is an intriguing and plausible conjecture. – chux - Reinstate Monica Dec 03 '15 at 03:59
  • "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. – Maciej Piechotka Feb 28 '21 at 08:55
  • 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. – supercat Jan 04 '22 at 23:45
9

free takes in only non-const pointers as parameter. So in case of const pointers, explicit casting to a non-const pointer is required.

Unable to free const pointers in C

Community
  • 1
  • 1
Nobody
  • 91
  • 7