83

I am reading a book (Programming with POSIX Threads by Butenhof, 1997) that uses C, and I came across the following line:

(void)free(data);

Here, data is just a pointer to an allocated struct,

data = malloc(sizeof(my_struct_t));

Why is the result of free being cast to void?

From my understanding of C, this doesn't seem to make sense for two reasons:

  • The free function already returns void
  • The code is not using the return value (it's not even being assigned to a variable)

The book was written in 1997. Is this some sort of legacy thing?

The author mentions that the examples were run on Digital Unix 4.0d, but I still can't imagine a reason to ever cast the result of a function if you're not going to use that result.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Adam Johnston
  • 1,399
  • 2
  • 12
  • 23
  • Some possible explanations can be found here: https://stackoverflow.com/questions/689677/why-cast-unused-return-values-to-void – Timbo Mar 10 '20 at 21:07
  • 4
    Out of curiosity, what is the date of publication of your C book? (Which book is it?) If it's before about 1995, there might be some justification for it — standard C compilers were not ubiquitous before about then. If it is published after that and still contains the cast (and no explanation of why), worry about what other bad habits it is teaching you. Get a more recent book! – Jonathan Leffler Mar 10 '20 at 21:32
  • 11
    Looks like [Programming with POSIX Threads](https://books.google.ca/books?id=_xvnuFzo7q0C&pg=PA51&lpg=PA51&dq=%22(void)free+(data);%22&source=bl&ots=Kp5R1Z-4Xi&sig=ACfU3U32Gzhv3ozUIkwpc68M-AkAqmSF8w&hl=en&sa=X&ved=2ahUKEwiF3cfD7pDoAhXdB50JHQY3AF4Q6AEwAnoECAkQAQ#v=onepage&q=%22(void)free%20(data)%3B%22&f=false) – Eugene Sh. Mar 10 '20 at 21:37
  • 3
    @JonathanLeffler as mentioned in my original post, the book was published in 1997 and was using UNIX 4.0d. The book is "Programming with POSIX Threads" by David R. Butenhof. So far it has been very informative and is written by one of the original contributors to the POSIX threads standard. – Adam Johnston Mar 10 '20 at 21:38
  • 7
    I've been using my copy of that in the last week — yes, it's still useful. It was written on the cusp of 'ubiquitous standard C' (I said 'about 1995'). The 'UNIX 4.0d' sounds like Digital UNIX — that's where Butenhof worked, and the preface does mention it. Treat the cast on `free()` as an oddity in the book that you don't need to emulate. It was semi-relevant once upon a long time ago, but it isn't relevant any more. – Jonathan Leffler Mar 10 '20 at 21:43
  • in legacy there was no `void` at all. There's no purpose on that, except perhaps to notice that you are not using the value returned by the function (in this case nothing, as `free()` returns nothing.) It's like casting the value returned of `malloc()` just to add code that make your programs fail more frequently for abusing. – Luis Colorado Mar 11 '20 at 19:14
  • FWIW: The statement in question does not cast `free` to `(void)free`, it casts `free(data)` to `(void)(free(data))`. The C cast operator has lower precedence than the function call. – Solomon Slow Mar 12 '20 at 17:23

3 Answers3

101

If we are talking about the standard free function then its prototype is

void free(void *ptr);

Therefore the cast is completely useless.
Now some speculation.

The author might have forgotten to include the stdlib.h header declaring this prototype, so the compiler is assuming the return type of it as int. Now during static analysis of this code the compiler was warning about the unused return value of what it thinks to be a non-void function. Such a warnings are usually silenced by adding the cast to void.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
  • 51
    But note that if the cast was introduced for the reason speculated, then using it to silence the warning is *incorrect*. The compiler will in that case be attributing a different type to `free` than it actually has, with the result that the call has undefined behavior (supposing C90 semantics, where calling an undeclared function does not inherently exhibit UB in all cases). In practice, it is plausible that that would result in *bona fide* misbehavior on some systems. The correct solution is to provide a correct declaration for the function. – John Bollinger Mar 10 '20 at 21:56
  • 11
    Notably, the examples in "Programming with POSIX Threads" repeatedly fail to include the relevant standard headers. Maybe this was some bad practice by the author, they could have been using a non-standard compiler setup that included all standard libs by default. – Lundin Mar 11 '20 at 11:58
77

It would be a legacy thing!

Before there was a C standard, the free() function would have been (implicitly) of type int — because there was not yet reliably a type void for it to return. There was no value returned.

When the code was first modified to work with standard C compilers, it probably didn't include <stdlib.h> (because it didn't exist before the standard). Old code would write extern char *malloc(); (maybe without the extern) for the allocation functions (similarly for calloc() and realloc()), and didn't need to declare free(). And the code would then cast the return value to the correct type — because that was necessary on at least some systems (including the one I learned C on).

Sometime later, the (void) cast was added to tell the compiler (or, more likely, lint) that "the return value from free() is deliberately ignored" to avoid a complaint. But it would have been better to add <stdlib.h> and let its declaration extern void free(void *vp); tell lint or the compiler that there was no value to ignore.

JFTR: Back in the mid-'80s, the ICL Perq was originally on a word-oriented architecture and the char * address for a memory location was a very different number from the 'anything_else pointer' to the same location. It was crucial to declare char *malloc() somehow; it was crucial to cast the result from it to any other pointer type. The cast actually changed the number used by the CPU. (There was also much rejoicing when the main memory on our systems was upgraded from 1 MiB to 2 MiB — since the kernel used about 3/4 MiB, it meant that user programs could use 1 1/4 MiB before paging etc.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 9
    I just opened a copy of K&R, 1st edition, which contains an implementation of `free()` on p. 177 which implicitly returns `int`. – ad absurdum Mar 10 '20 at 21:24
  • 9
    Of course — `void` was added to some systems (Unix System III, maybe) before the standard was released, but that wasn't part of C when K&R 1st Edn was written (1978). A function that didn't return a value was declared without a return type (which meant it returned `int`), and as long as you didn't use the value that wasn't returned, there was no problem. The C90 standard had to treat that sort of code as valid — it would have failed dismally as a standard had it not. But C99 removed the 'implicit `int`' and 'implicit function declaration' rules. Not all the code in the world has caught up. – Jonathan Leffler Mar 10 '20 at 21:27
  • 5
    Op claims the book was written in 1997. What you are talking about here is very early pre-standard "K&R C" and it seems unlikely that anyone would write a book about that at all. The only such book that existed to my knowledge was indeed K&R 1st edition. – Lundin Mar 11 '20 at 11:45
  • It was a frequent (if quixotic) practice to not include headers if you thought you could get away with implicit declaration, because people thought it would reduce build times. – Spencer Mar 11 '20 at 13:16
  • Does anybody use a `(void)` cast for `printf()`?? – Luis Colorado Mar 11 '20 at 19:17
  • I've seen it on some older code that was ”cleaned up” for `lint`, but rarely otherwise, @LuisColorado. – Jonathan Leffler Mar 11 '20 at 19:27
  • @Spencer: From a practical standpoint, the difference in build times was often dramatic. I'll admit, though, I find it curious in retrospect that headers didn't do something like: `typedef double __fdrd(double); __fdrd sin,cos,tan,sqrt, ...;` since much of the cost of including headers was the cost of reading them from disk and copying their contents into the preprocessed source; writing declarations more concisely would greatly reduce that cost. – supercat Mar 11 '20 at 21:59
  • @JonathanLeffler, it depends on what you consider is "cleaning up". Some people considered that casting to `void` the return value of a rutine whose return value is not going to be used (like `printf(3)`) was indeed "cleaning the code". I learned C in 1979, so I milked from unprototyped function definitions and to take care of how the code was written. I never saw a common sense reason to do this cast, as the same that happens with the return value of `malloc(3)`. I'm indeed trying to compile a pdp-11 unix v7 completely to be able to push a completely buildable version to tuhs.org – Luis Colorado Mar 12 '20 at 15:27
  • If you look at an [early edition](https://archive.org/details/TheCProgrammingLanguageFirstEdition/mode/2up) of K&R, you can see that the `(void)` data type wasn't yet in the language, so early versions of `free()` would have returned `int`. When void was introduced, many compilers continued to declare return values of `(int)` in the standard header files, because changing those would have produced massive numbers of lint errors in legacy code. – AndyB Mar 13 '20 at 02:24
12

This cast is not needed. It probably wouldn't have been at the time as C had been standardized in the form of C89.

If it had been, it would've been due to implicit declaration. This usually meant that the person writing the code forgot to #include <stdlib.h> and a static analyzer was being used. This is not the best workaround and a much better idea would've been to just #include <stdlib.h> instead. Here's some wording from C89 about implicit declaration:

If the expression that precedes the parenthesized argument list in a function call consists solely of an identifier, and if no declaration is visible for this identifier, the identifier is implicitly declared exactly as if, in the innermost block containing the function call, the declaration

extern int identifier();

appeared.

But that's odd because they're not casting the result of malloc either, and malloc and free are in the same header file.

It's also possible that this is just a mistake or some way to tell the reader that free returns no result.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
  • 5
    Just because a language gets standardized doesn't mean everyone instantly updates their toolchain and code to conform to it. It's plausible for old-style "K&R" C to have stuck around another 8 years. Still, I agree that it's odd that a static analysis tool would require a cast for `free` but not for `malloc`. – dan04 Mar 11 '20 at 17:00
  • 4
    @dan04 You usually use the result of malloc ;) I am no fan of writing things like (void) printf (...) to stop the compiler spitting warnings but "must compile without any warnings, even the stupid ones" is something that happens in a lot of projects. – richardb Mar 11 '20 at 20:49