void free(void *ptr);
Is there some reason why the argument can't be const void*? That would
allow the common situation where a pointer is allocated, the data set up
and the pointer returned as a pointer to constant data, and that is then
freed, without having to add the unnecessary (and unsafe) cast back to
void* to free the memory. For example, a funtion to get an environment
variable into an allocated string could be:
const char *get_env(const char *name)
{
const char *env = getenv(name);
if (env)
{
char *str = malloc(strlen(env)+1);
if (str)
strcpy(str, env);
return str;
}
return NULL;
}
(headers omitted)
The calling function would then do something like:
const char *shell = get_env("SHELL");
...
if (shell)
{
... use shell
}
...
free(shell);
At present, this will result (correctly) in a diagnostic, possibly a
fatal one, when the attempt is made to pass the const char* to free().
The usual 'solution' is to use the ugly call
free((void*)shell);
but changing the argument of free() to be const void* would avoid that
without, as far as I can see, having any adverse effects of other code.
Chris C
Interesting - I ran into this (once more) just yesterday, and was
thinking about whether free() could be modified.
I suspect that the reason why free() is not shown as taking a const void
pointer is that it does not guarantee not to modify the data pointed at
by the pointer, hence the data is not const, and hence the const
qualifier is not 'appropriate'. However, it is also germane to note
that no standard conforming program could tell that the data had been
modified since it is undefined behaviour to access the space freed after
the free call completes.
> At present, this will result (correctly) in a diagnostic, possibly a
> fatal one, when the attempt is made to pass the const char* to free().
> The usual 'solution' is to use the ugly call
>
> free((void*)shell);
>
> but changing the argument of free() to be const void* would avoid that
> without, as far as I can see, having any adverse effects of other code.
I agree... My workaround is even uglier than yours - I use a macro
inspired by the C++ const_cast<type>(value) operator:
#define CONST_CAST(type,value) ((type)(value))
free(CONST_CAST(void *, shell));
The only merit of this over your solution is equivalent to the merit of
the C++ const_cast<type>(value) -- it makes it clear what the cast is
doing and makes it possible to locate the places where you remembered to
use the macro.
--
Jonathan Leffler #include <disclaimer.h>
Email: jlef...@earthlink.net, jlef...@us.ibm.com
Guardian of DBD::Informix v2005.02 -- http://dbi.perl.org/
> The standard function free() [section 7.20.3.2] is defined as
>
> void free(void *ptr);
>
> Is there some reason why the argument can't be const void*? That would
> allow the common situation where a pointer is allocated, the data set up
> and the pointer returned as a pointer to constant data, and that is then
> freed, without having to add the unnecessary (and unsafe) cast back to
> void* to free the memory.
I don't follow how this happens. Doesn't malloc return void* ? Since
you pass the same pointer to free that malloc returned, why would you
not want the types to be the same?
If your program is changing the type of the pointer from malloc to
something incompatible with free, well, then, don't do that. Or,
understand that that you will have to undo that before you free the
memory.
> Chris Croughton wrote:
>> The standard function free() [section 7.20.3.2] is defined as
>>
>> void free(void *ptr);
>>
>> Is there some reason why the argument can't be const void*? That would
>> allow the common situation where a pointer is allocated, the data set up
>> and the pointer returned as a pointer to constant data, and that is then
>> freed, without having to add the unnecessary (and unsafe) cast back to
>> void* to free the memory. [...]
>
> Interesting - I ran into this (once more) just yesterday, and was
> thinking about whether free() could be modified.
It's something I've noticed for years, and a guy at work got bitten by
it (and threatened to remove the keyword 'const' from the whole
project!).
> I suspect that the reason why free() is not shown as taking a const void
> pointer is that it does not guarantee not to modify the data pointed at
> by the pointer, hence the data is not const, and hence the const
> qualifier is not 'appropriate'.
Yes, I thought that. But as you say:
> However, it is also germane to note
> that no standard conforming program could tell that the data had been
> modified since it is undefined behaviour to access the space freed after
> the free call completes.
Yup.
>> At present, this will result (correctly) in a diagnostic, possibly a
>> fatal one, when the attempt is made to pass the const char* to free().
>> The usual 'solution' is to use the ugly call
>>
>> free((void*)shell);
>>
>> but changing the argument of free() to be const void* would avoid that
>> without, as far as I can see, having any adverse effects of other code.
>
> I agree... My workaround is even uglier than yours - I use a macro
> inspired by the C++ const_cast<type>(value) operator:
>
> #define CONST_CAST(type,value) ((type)(value))
>
> free(CONST_CAST(void *, shell));
I really hate the C++ cast thingys, I don't think that they add anything
since I only use casts when I have to.
> The only merit of this over your solution is equivalent to the merit of
> the C++ const_cast<type>(value) -- it makes it clear what the cast is
> doing and makes it possible to locate the places where you remembered to
> use the macro.
True. My solution would be to write:
#define free(p) free((void*)(p))
and ignore it. (Yes, #defining a name in the standard library is also
undefined, but this project does so many non-portable things anyway that
I give up trying to get them fixed...)
I know that even if the standard is changed it will take years before I
get to see it in an implementation, but it would be nice to know that my
grandchildren might have a better interface (well, it would be if I had
any children, let along grandchildren).
Chris C
Chris C
It is perfectly allowable to pass void * to a function expecting const
void *, so this is no argument against the proposed change.
> Since you pass the same pointer to free that malloc returned, why
> would you not want the types to be the same?
Now hold on a second - how often is that actually the case? I'll bet
that 99% of the time you use malloc() in your own code, you assign the
result to a variable of a type different from void *.
> If your program is changing the type of the pointer from malloc to
> something incompatible with free, well, then, don't do that. Or,
> understand that that you will have to undo that before you free the
> memory.
It's not always that simple in real life.
DES
--
Dag-Erling Smørgrav - d...@des.no
> Rudolf <rth...@bigfoot.com> writes:
>> I don't follow how this happens. Doesn't malloc return void* ?
>
> It is perfectly allowable to pass void * to a function expecting const
> void *, so this is no argument against the proposed change.
>
>> Since you pass the same pointer to free that malloc returned, why
>> would you not want the types to be the same?
>
> Now hold on a second - how often is that actually the case? I'll bet
> that 99% of the time you use malloc() in your own code, you assign the
> result to a variable of a type different from void *.
>
"free(ptr);" means "give this memory to some other part of the
programm for it to reuse it (and thus modify it)". You will have to
make it non-const at some point.
Also the allocator usually does modify the memory for its
bookkeeping.
--
Philippe Amarenco, aka Phix
epita 2007 - LSE - EpX
> Rudolf <rth...@bigfoot.com> writes:
> > I don't follow how this happens. Doesn't malloc return void* ?
>
> It is perfectly allowable to pass void * to a function expecting const
> void *, so this is no argument against the proposed change.
Changing the prototype of free to take a const-qualified pointer changes the
type of the function. Whilst a void* can be implicitly qualified to const
void*, the existing type "void (*)(void *)" does not impicitly cast to the
proposed "void (*)(const void *)", requiring code changes to anything that
stores the address of free().
In my experience, it is quite common to wrap memory allocation calls with
thin veneers that monitor the dynamic memory allocations for the purposes of
recording them, debugging them, redirecting them to a different memory
manager.
> > If your program is changing the type of the pointer from malloc to
> > something incompatible with free, well, then, don't do that. Or,
> > understand that that you will have to undo that before you free the
> > memory.
>
> It's not always that simple in real life.
The project on which I am currently working actually has a free_const()
function that takes a const-qualified pointer, casts the const away and
passes the unqualified pointer on to the free() function. I'm not sure if I
like that all that much, but it gets the job done without requiring casts at
the point of use.
Another thing I do from time to time is to allocate some memory with
malloc() on application initialisation, initialise it to some value that has
to be computed dynamically but is otherwise unchanging, and then store the
address of that memory in a const-qualified manner to guarantee (in as much
as it can be guaranteed) that the data is not altered whilst the application
is running normally.
--
Stewart Brodie
> I really hate the C++ cast thingys, I don't think that they add anything
> since I only use casts when I have to.
The whole idea was to make you hate them, IIUC, thus discouraging what
was perceived as undue fondness for casting. That and, of course,
making your casts easy to find (as already noted).
--
Christopher Benson-Manica | I *should* know what I'm talking about - if I
ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
But how often do those wrappers need to take the address of the free()
function?
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Probably because the implementation is allowed to alter the bytes
pointed to by 'ptr'.
More likely, it's because the act of deallocating storage is
technically a modification to that storage.
> That would
> allow the common situation where a pointer is allocated, the data set up
> and the pointer returned as a pointer to constant data, and that is then
> freed, without having to add the unnecessary (and unsafe) cast back to
> void* to free the memory.
Yes, I do that all the time. But passing a const pointer to free()
is a dangerous practice, since any code with access to the pointer
could do it. You're returning it as const for a reason - to protect
from inadvertent modifications to the data - which presumably
should also include deallocating that data.
It's arguably safer to require an explicit cast to remove the
constness from the pointer in these cases, if for no other reason
than it makes it obvious you're doing the free() on purpose.
That "unsafe" cast therefore exists for a reason - to make it
obvious that the deallocation is also an inherently "unsafe"
thing to do to the data.
> Stewart Brodie <stewart...@ntlworld.com> writes:
> > d...@des.no (Dag-Erling Smørgrav) wrote:
> >
> >> Rudolf <rth...@bigfoot.com> writes:
> >> > I don't follow how this happens. Doesn't malloc return void* ?
> >>
> >> It is perfectly allowable to pass void * to a function expecting const
> >> void *, so this is no argument against the proposed change.
> >
> > Changing the prototype of free to take a const-qualified pointer changes
> > the type of the function. Whilst a void* can be implicitly qualified to
> > const void*, the existing type "void (*)(void *)" does not impicitly
> > cast to the proposed "void (*)(const void *)", requiring code changes to
> > anything that stores the address of free().
> >
> > In my experience, it is quite common to wrap memory allocation calls
> > with thin veneers that monitor the dynamic memory allocations for the
> > purposes of recording them, debugging them, redirecting them to a
> > different memory manager.
>
> But how often do those wrappers need to take the address of the free()
> function?
Every time - usually to initialise the defaults. For example, something
like this: (although probably with more functions)
struct api_memory_manager
{
void *(*allocate)( size_t );
void (*deallocate)( void *);
} mem_functions =
{
malloc, free
};
Or perhaps you have an API for setting the functions:
app_set_mem_functions( malloc, free );
And so on.
--
Stewart Brodie
Ok. I was thinking more in terms of something like
void deallocate(void *ptr)
{
do_some_stuff(...);
free(ptr);
do_some_more_stuff(...);
}
but yes, if you want to be able to use something other than free() it
makes sense to use function pointers.
Yes, we don't want a diagnostic for the most common usage.
I tried cancelling that but our news server is broken..
You wouldn't get a diagnostic so much as a changed function
signature, with added support burden. Since the pointed-to
storage is modified via the pointer argument, "const" would
not be appropriate.
If the app applies constness to the inherently non-const
storage, then it seems only fair that it needs to remove
the constness before returning the storage.
Interesting because one of the major complaints C++ programmers have
over delete is that it can take a pointer to a const qualified object
and they dislike the idea that a a const object can be deleted without
even a warning. Yet here you are asking that free() should work even on
a pointer to an object that the programmer has elected to protect with
const.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
That's much too broad a statement about C++ programmers, I believe.
Quite a lot of C++ programmers have complained that VC6 makes it an
error.
One argument for why it should be allowed is that deleting const auto
(or static, for that matter) objects is allowed too. Here's a more
extensive rationale by Andrew Koenig:
http://groups.google.de/group/comp.std.c++/msg/287a62692131980b
-- Niklas Matthies
> In article <slrndkkl46...@ccserver.keris.net>, Chris Croughton
> <ch...@keristor.net> writes
> >The standard function free() [section 7.20.3.2] is defined as
> >
> > void free(void *ptr);
> >
> >Is there some reason why the argument can't be const void*?
>
> Interesting because one of the major complaints C++ programmers have over
> delete is that it can take a pointer to a const qualified object and they
> dislike the idea that a a const object can be deleted without even a
> warning. Yet here you are asking that free() should work even on a
> pointer to an object that the programmer has elected to protect with
> const.
It is partly a question of determining for whose benefit is the const
qualification. Neither in the C standard section 6.7.3 nor the rationale
does it indicate that const qualification is for any purpose other than
permitting the compiler to store the const-qualified object in read-only
storage (or not store it at all if its address isn't taken)
On the other hand, it is extremely common for well-designed code to use
const qualification in formal parameter specifications to document (and
enforce, most of the time) additional guarantees about the behaviour of the
function with respect to those such qualified parameters. Indeed, IME
programmers are often taught that this is what it means, and then they see
that the C standard library itself makes extensive use of const
qualification and, with any luck, see that using const qualification
properly is a good thing. Yet this is *not* the stated purpose of const -
what it really means is that you are permitted to pass an actual parameter
that is the address of an object in read-only storage, which implies that
the object may not be altered. Now that distinction almost never matters in
the real-world code, but in the world of the language specification, it
makes all the difference.
Taking that together with the restriction on the values of pointers that are
passed to the free() function that these pointers must have been returned by
malloc() or realloc(), they cannot, by definition, be pointers to objects in
read-only storage. Consequently, there is no case for changing the standard
prototype of free() on that basis.
I do not know C++, but I would guess that you could make a similar argument
in favour of stopping operator delete from accepting addresses of const
objects, since the pointers cannot have come from anywhere other than
operator new?
Adding the const qualification also changes the prototype of free() such
that the address of the free() function can no longer be stored in an object
of type "void (*)(void *)", which as I've indicated previously, would break
lots (IMHO) of existing real-world code.
Yes, this means that you have to cast the pointer to remove the constness
(that is one facility that I *would* like C - the ability to remove
const-ness without needing to know the actual type of the object). For a
bonus, from the library's point of view, the library has successfully
abdicated responsibility for performing a cast that may lead to undefined
behaviour, too.
--
Stewart Brodie
While I develop and maintain a few relatively large projects in strict C89,
I'm running into the same problem again and again: there is no way to define
securely enough an interface I need. The case with the free function looks
like a special case of the general problem. The more sophisticated example
is to get a consistent interface for a stack which should able to work with
both const and non-const items, e.g.:
struct stack;
void push_item(struct stack **top, struct item *item);
void push_const_item(struct stack **top, const struct item *item);
/* or even:
struct conststack;
void push_const_item(struct conststack **top, const struct item *item);
*/
While it's clear that the functions should actually do the exactly same
work, it's necessary either to use an explicit cast:
static struct item *unconst_item(const struct item *item)
{
reutrn (struct item*) item;
}
void push_const_item(struct stack **top, const struct item *item)
{
push_item(top, unconst_item(item));
}
or duplicate bodies of the functions (no matter with or without macros).
Another example is searching functions (like strchr, see
http://www.lysator.liu.se/c/dmr-on-noalias.html ): absence of a
nonindependent type qualifiers deduce results into the inconsistent
language. At the same time, anyone who's working on a large project know
that the consistency is probably the biggest problem: it's quite difficult
to track all the explicit casts to and from all the pointers to
modifiable/non-modifiable and freeable/non-freeable data elements.
What we intend to do is to introduce custom type qualifiers and optional
type qualifiers in our C89/C99/EC++ compilers to make our code more
consistent. With the extension, the strchr function prototype would look
like the following:
const:q char strchr(const:q char *s, int c);
which mean that q is a name of a flag indicating whether the optional const
qualifiers is applying. Once it's known that the flag should be raised, all
other qualifiers with the same flag name should be applied in the prototype
. For example,
const char *s1 = "abc";
char *s2 = strchr(s1, 'b'); // error: since the argument requires q to be
raised, the call returns const char
The problem with the free function would be solved with a custom qualifier
then:
void free(const void *p); // or void free(const:q void *p);?
void f(qual<nofree> struct item *item) // now we know that we shouldn't
free the data
{
item -> member = 0; // ok: we still can modify the data
free(item); // error: cannot convert struct item* to qual<nofree>
struct item*
}
I'm not sure the syntax will not be changed before we will have implemented
the functionality in our front ends, but the very principle is what we would
like to try.
Thanks.
--
Unicals Group -- Development Tools for OEMs
http://www.unicals.com
Have you looked at CQual? It seems like it might be relevant to what
you want to do: it provides a way to add custom type qualifiers to existing
C code, and extends the C type system in a clean way. In particular, it
provides for polymorphic type qualifiers, which looks like exactly what
you want. The syntax is different from what you listed, but the concepts
sounds very similar. There is quite a bit of experience with using CQual
for various applications, so there is some evidence that type qualifiers
are indeed useful.