free(3) const void *

Alejandro Colomar alx@kernel.org
Fri Jan 26 13:21:13 GMT 2024


Hi,

ISO C and POSIX say that free(3) shall take a `void *`.  But free() does
nothing to the pointee during its lifetime.  Of course, the lifetime of
the object ends right at the boundary of the call to free() itself.  And
even if the lifetime of the object would be extended to the end of
free(), it doesn't really modify the pointee internally; it only uses
the pointer to know which memory it is freeing, but never writes to it.
And even if some implementation would want to write to it for some
reason, it'd be as easy as discarding `const` internally.

It is sometimes (often?) useful to allocate some object, write
immediately to it, and then use it read-only.  The standard definition
of free() forces the programmer to keep a non-const pointer around just
for the sake of freeing, which is unnecessarily dangerous.

Since a `const void *` will accept anything that a `void *` would accept
(and more), how about changing the prototype of free() in glibc as an
extension to the language?

I'd like to refor free(3) to be:

	void free(const void *p);

But this would break function pointers.  I don't know if there's any way
to avoid that.  What was done when const was added to string functions?
Was it considered an acceptable breaking change?

	$ cat fp.c 
	#include <stdlib.h>

	typedef void (*free_t)(void *p);

	extern void free_const(const void *p);

	int
	main(void)
	{
		free_t  fp;

		fp = free;
		fp = free_const;
	}
	$ cc -Wall -Wextra fp.c 
	fp.c: In function ‘main’:
	fp.c:13:12: warning: assignment to ‘free_t’ {aka ‘void (*)(void *)’} from incompatible pointer type ‘void (*)(const void *)’ [-Wincompatible-pointer-types]
	   13 |         fp = free_const;
	      |            ^
	fp.c:10:17: warning: variable ‘fp’ set but not used [-Wunused-but-set-variable]
	   10 |         free_t  fp;
	      |                 ^~
	/usr/bin/ld: /tmp/ccVlhr05.o: in function `main':
	fp.c:(.text+0x12): undefined reference to `free_const'
	collect2: error: ld returned 1 exit status

Maybe we could add a function-like macro so that direct calls with a
`const` pointer use the macro and don't get a warning, but one can still
take the address of the function and it will use the standard prototype:

	$ cat fp.c 
	#include <stdlib.h>
	#include <string.h>

	typedef void (*free_t)(void *p);

	#define free(p)  free((void *) &*p)

	int
	main(void)
	{
		free_t      fp;
		const char  *s = strdup("foo");

		fp = free;
		(void) fp;
		free(s);
	}
	$ cc -Wall -Wextra fp.c 
	$

Does this sound reasonable?

Cheers,
Alex

-- 
<https://www.alejandro-colomar.es/>
Looking for a remote C programming job at the moment.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20240126/5412469f/attachment.sig>


More information about the Libc-alpha mailing list