Subject: Re: toolchain/22118: make won't compile with -Wcast-qual -Wstrict-prototypes and more
To: der Mouse <mouse@Rodents.Montreal.QC.CA>
From: Greg A. Woods <woods@weird.com>
List: tech-toolchain
Date: 07/15/2003 04:12:10
[ On Monday, July 14, 2003 at 20:14:27 (-0400), der Mouse wrote: ]
> Subject: Re: toolchain/22118: make won't compile with -Wcast-qual -Wstrict-prototypes and more
>
> > If a pointer passed to free() may ever have been derived from a
> > string constant then there's a very nasty bug lurking somewhere.
>
> Yes...but it's quite possible that a given data structure may contain
> something which in some cases is freed, but in other cases never is,
> and the pointer points to a string constant only in the "other cases".
Yes, but in that case the variable declared to hold a pointer to that
storage MUST NOT be qualified as pointing to "const" storage since that
qualifier would not always be true.
> For example, in a tree structure, the code is often designed such that
> the root node is never freed.
You seem to be trying to go way too far with a cheap add-on language
hack that cannot possibly taken so far as you wish in its current form.
As you know "const" is a "Johnny-come-lately" to the C language and
though it's quite simple and elegant for what it is, it's also not
anywhere nearly so useful as it might seem on first glance. Even worse
it is used in conflicting ways within the language and thus it isn't
quite so simple as you attempted to describe it in another message. In
a parameter declaration it does mean what you said in that other
message: "data is not changed through this access path." However in a
string constant, and thus a pointer initialized to point at the storage
holding a string constant, it means somewhat the opposite: "the area
this pointer points to might be read-only and this pointer must always
be passed in parameters declared as pointing to const storage, or be
assigned to other variables similarly const-qualified."
const-qualified storage can never be portably de-const-ified, and
pointer aliasing does not count. If your API requires string constants
to be writable then there's a simple and guaranteed solution (with GCC):
use the '-fwritable-strings' option, but otherwise to make your code
portable you first have to copy string constants to writable storage
before using a pointer to that writable storage in your API.
(i.e. if you ever have to pass a pointer to free() then the parameter
you acquire that pointer from must not be qualified as pointing to const
storage, even if you don't always pass that pointer to free() -- the
compiler obviously cannot guess when the path to free() might be
followed.)
> You then have to do one of
>
> 1) Make the element pointer-to-const, and deconst it when freeing;
>
> 2) Make the element pointer-to-nonconst, and deconst it when assigning;
>
> 3) Don't use string literals even for the root;
>
> 4) Duplicate the struct definition, with a consted version for the root
> and a non-consted version for non-roots;
>
> 5) Disable or ignore the warnings (-Wno-cast-qual, -fwritable-strings,
> etc, I count here).
>
> (4) is the most abstractly correct, but I consider the duplication bad
> enough that I prefer (1) or (2) (or occasionally (3)) in practice.
Given the current definition of Standard C, (3) and (4) are the only
correct and portable solutions, with (3) being the safest unless (4) is
achieved by use of a macro that generates the two alternate declarations
where necessary. Remember though that (4) only works for structure
fields and not for parameters or direct variable assignments (and it is,
IIRC, a parameter declaration which triggered this discussion).
(3) is what I tried to show in my first example with the use of strdup()
to copy the string constant to writable storage.
(5) essentially violates the strict standard, but is necessary for a lot
of real-world code that was written back when string constants were not
expected to be located in read-only storage. Indeed it was probably
this silly idea of using read-only storage by default in the standard
for string constants that precipitated the need for "const" in the first
place. Forcing this new invention to be the default in the standard was
one of the many serious mistakes the comittees made in standardizing C,
but of course hindsight is 20/20.
(1) and (2) are very bad hacks that should never be attempted.
--
Greg A. Woods
+1 416 218-0098 VE3TCP RoboHack <woods@robohack.ca>
Planix, Inc. <woods@planix.com> Secrets of the Weird <woods@weird.com>