During a recent discussion (see comments to this answer), R.. recommended to never create aliases for pointer-to-const
types as you won't be able to deallocate the referenced objects easily in a conforming C program (remember: free()
takes a non-const
pointer argument and C99 6.3.2.3 only allows conversions from non-qualified to qualified).
The C language obviously assumes the existance of an owner for any allocated object, ie someone somewhere has to store a non-const
pointer to the object, and this someone is responsible for deallocation.
Now, consider a library allocating and initializing objects which are non-modifyable from user-space code, so function calls always return const
-qualified pointers.
Clearly, the library is the owner of the object and should retain a non-const
pointer, which is somewhat silly as the user already supplies a perfectly valid, but const
copy of the pointer on each library call.
To deallocate such an object, the library has to discard the const
qualifier; as far as I can tell, the following
void dealloc_foo(const struct foo *foo)
{
free((void *)foo);
}
is valid C; it would only be invalid if the foo
parameter were additionally restrict
-qualified.
However, casting away const
seems somewhat hack-ish.
Is there another way aside from dropping the const
from all return values of library functions, which would lose any information about object mutability?
I don't read the same thing in 6.3.2.3. That paragraph is about conversions that happen implicitly and that don't need a casts. So an implicit conversion from a pointer-to-const
object may be not permitted, but this says nothing about explicit casts.
Casts are handled in 6.5.4, and I don't see anything that would constrain you from a cast of any pointer-to-const
that is by itself not const
qualified with (void*)
. In the contrary it says
Conversions that involve pointers,
other than where permitted by the
constraints of
6.5.16.1, shall be specified by means of an explicit cast.
So I read there that if you do things explicitly, they are permitted.
So I think the following is completely valid
char const *p = malloc(1);
free((void*)p);
What 6.5.4 forbits would be the following
char *const p = malloc(1);
free((void*)p); /* expression of cast is const qualified */
As a side note, for your second line of thoughts, a library that returns a pointer-to-const
qualified object that then is to be placed in the responsibility of the caller, makes not much sense to me. Either
- the library returns a pointer to an
internal object, then it should
qualify it pointer-to-
const
to have some weak
protection that the caller doesn't
change it, or
- the library returns a freshly
allocated object that falls in the
responsibility of the caller. Then it
shouldn't care much of whether the
caller changes it, so it may return
a pointer-to-unqualified object. If the caller
then wants to ensure that the
contents is not accidentally
overwritten or something he might
assign it to a
const
pointer, for
his use.
If the object is truly immutable, why give the user the pointer to it and risk an opportunity of corrupt object? Keep the object address in the table, return an opaque handle (an integer table index?) to the user and accept that handle in your library routines.
If you return a const
-qualified pointer, the semantics are that the caller of your function is not permitted to modify the object in any way. That includes freeing it, or passing it to any function in your library that would in turn free it. Yes I'm aware that you could cast away the const
qualifier, but then you're breaking the contract that const
implies.