No. It's the *pointer* that is no longer valid.
There's definitely a difference between "exists and is changed" and
It's not a change to the data behind it, it's a change to the *metadata*.
No, it's why I'm right.
"kmalloc/kfree" (or any memory manager) by definition has to play games
with pointers and do things like cast them. But the users shouldn't need
No.
You are continuing to make the mistake that you think that "const" means
that the memory behind the pointer is not going to change.
Why do you make that mistake, when it is PROVABLY NOT TRUE!
Try this trivial program:
int main(int argc, char **argv)
{
int i;
const int *c;
i = 5;
c = &i;
i = 10;
return *c;
}
and realize that according to the C rules, if it returns anything but 10,
the compiler is *buggy*.
The fact is, that in spite of us having a "const int *", the data behind
that pointer may change.
So it doesn't matter ONE WHIT if you pass in a "const *" to "kfree()": it
does not guarantee that the data doesn't change, because the object you
point to has other pointers pointing to it.
This isn't worth discussing. It's really simple: a conforming program
CANNOT POSSIBLY TELL whether "kfree()" modified the data or not. As such,
AS FAR AS THE PROGRAM IS CONCERNED, kfree() takes a const pointer, and the
rule that "if it can be considered const, it should be marked const" comes
and says that kfree() should take a const pointer.
In other words - anythign that could ever disagree with "const *" is BY
DEFINITION buggy.
It really is that simple.
Linus
--
The pointer is no longer valid because the object it pointed to no longer exists. The pointer is also no longer valid, but that is not the end of the It doesn't matter what has changed. All that matters is whether this is something we normally want to happen to a const pointer or whether doing If you don't like having to cast, don't use 'const'. But if you use 'const', you have to cast when you mean to do something that you would like to be No, that's not what it means. It has nothing to do with memory. It has to do I don't. You do, because you argue 'kfree' can be const because it doesn't change the memory. The change in the memory is meaningless, the change in I don't know what you think this example proves. Nobody is arguing that so long as one const pointer to an object exists, no code anywhere should ever be able to change it. All I'm saying is that changing the logical state of an object *through* a const pointer is unusual. You should need a cast to do this because that's But that's exactly what doesn't matter. As you've said at least twice now, it has nothing to do with changing the data. It has to do with changing the logical state of the object. That's what you're not supposed to do through a I think you may be the only person in the world who thinks so. DS --
Blah. That's just your own made-up explanation of what you think "const" should mean. It has no logical background or any basis in the C language. "const" has nothing to do with "logical state". It has one meaning, and one meaning only: the compiler should complain if that particular type is used to do a write access. It says nothing at all about the "logical state of the object". It cannot, since a single object can - and does - have multiple pointers to it. So your standpoint not only has no relevant background to it, it's also not even logically consistent. Linus --
To some extent, I agree. You can use "const" for pretty much any reason. It's just a way to say that you have a pointer and you would like an error if certain things are done with it. You could use it to mean anything you want it to mean. The most common use, and the one intended, is to indicate that an object's logical state will not You are the only one who has suggested it has anything to do with changes through other pointers or in other ways. So you are arguing against only yourself here. Nobody has said it has anything to do with anything but operations through Actually, that is true of your position. On the one hand, you defend it because kfree does not change the data. On the other hand, you claim that it has nothing to do with whether or not the data is changed. The normal use of "const" is to indicate that the logical state of the object should not be changed through that pointer. The 'kfree' function changes the logical state of the object. So, logically, 'kfree' should not be const. The usefulness of "const" is that you get an error if you unexpectedly modify something you weren't expected to modify. If you are 'kfree'ing an object that is supposed to be logically immutable, you should be made to indicate that you are aware the object is logically immutable. Simply put, you you have to cast in any case where you mean to do something that you want to get an error in if you do not cast. I would like to get an error if I call 'kfree' through a const pointer, because that often is an error. I may have a const pointer because my caller still plans to use the object. Honestly, I find your position bizarre. DS --
So why do you complain? No, I'm saying that "const" has absolutely *zero* meaning on writes to an object through _other_ pointers (or direct access) to the object. And you're seemingly not understanding that *lack* of meaning. kfree() doesn't do *squat* to the object pointed to by the pointer it is passed. It only uses it to look up its own data structures, of which the pointer is but a small detail. .. and I'm telling you: kfree() does *nothing* conceptually through that pointer. No writes, and not even any reads! Which is exactly why it's const. The only thing kfree does through that pointer is to update its own concept of what memory it has free. Now, what it does to its own free memory is just an implementation detail, and has nothing what-so-ever to do with the pointer you passed it. See? Linus --
Because the object ceases to exist. However, any modification requires write Nonsense. The 'kfree' function *destroys* the object pointer to by the It destroys the object the pointer points to. Destroying an object requires That is not what it does, that is how it does it. What it does is destroy I now have a much better understanding of what you're saying, but I still think it's nonsense. 1) An operation that modifies the logical state of an object should not normally be done through a 'const' pointer. The reason you make a pointer 'const' is to indicate that this pointer should not be used to change the logical state of the object pointed to. 2) The 'kfree' operation changes the logical state of the object pointed to, as the object goes from existent to non-existent. 3) It is most useful for 'kfree' to be non-const because destroying an object through a const pointer can easily be done in error. One of the reasons you provide a const pointer is because you need the function you pass the pointer to not to modify the object. Since this is an unusual operation that could be an error, it is logical to force the person doing it to clearly indicate that he knows the pointer is const and that he knows it is right anyway. I'm curious to hear how some other people on this feel. You are the first competent coder I have *ever* heard make this argument. By the way, I disagree with your metadata versus data argument. I would agree that a function that changes only an object's metadata could be done through a const pointer without needed a cast. A good example would be a function that updates a "last time this object was read" variable. However, *destroying* an object is not a metadata operation -- it destroys the data as well. This is kind of a philosophical point, but an object does not have a "does this object exist" piece of metadata. If an object does not exist, it has no data. So destroying an object destroys the data and is thus a write/modification ...
I don't think that kfree() itself changes the state of the object. It doesn't call a destructor or anything like that, so the object itself must be "inert" before the call to kfree(). That is, at the time of the kfree() call the system must have ensured that the object will no longer be used by anything. The call to kfree() is simply bookkeeping--allowing that memory to be I have a certain amount of sympathy for this view...it's a fairly painless way to reduce the likelihood of errors. At the same time, I don't think I've ever run into this problem myself--is it really all that common? Chris --
Here's an idea. Think it through. Why don't we need write permissions to a file to unlink it? Here's a hint: because unlinking doesn't *write* to it. In fact, it doesn't read from it either. It doesn't do any access at all to that object, it just *removes* it. Is the file gone after you unlink it? Yes (modulo refcounting for aliasing "pointers" aka filenames, but that's the same for any memory manager - malloc/free just doesn't have any, so you could think of it as a non-hardlinking filesystem). So you're the one who are speaking nonsense. Making something "not exist" is not at all the same thing as accessing it for a write (or a read). It is a metadata operation that doesn't conceptually change the data in any way, shape or form - it just makes it go away. And btw, exactly as with kfree(), a unlink() may well do something like "disk scrubbing" for security purposes, or cancel pending writes to the backing store. But even though it may write (or, by undoing a pending write, effectively "change the state") to the disk sectors that used to contain the file data, ONLY AN IDIOT would call it "writing to the file". Because "the file" is gone. Writing to the place where the file used to be is a different thing. So give it up. You're wrong. Freeing a memory area is not "writing to it" or accessing it in *any* manner, it's an operation on another level entirely. Linus --
You cannot unlink a file. Given a file, if you were to attempt to unlink it, what directory would you remove it from? Unlinking a file is an operation on the directory the file is in, not on the directory itself. We do need write permissions to the directory. If you had only a const pointer to the data in the file, you should definitely not be able to use that pointer to find a directory the file is in and remove it without clearly indicating you know *exactly* what you're doing. Given just that 'const' pointer, you're not supposed to be modifying the data and certainly using that pointer to modify anything logically above Right. It's an operation on the directory the file is in that might have The file is gone if and only if the directory was the only thing that needed the file to exist. A file that is only on one directory "belongs to" that directory. So write permission to the directory is all that is needed. What you are arguing is essentially that you should be able to remove a file from any directory it is in just because you have write access to the file's I agree with you about that part. I can't understand why you keep thinking this is where our disagreement lies when I've stated at least three times that I agree about this. The issue has nothing to do with whether or not 'kfree' modifies the particular bytes pointed to. It has to do with whether or not 'kfree' is the kind of operation one would normally want to allow on Nevertheless, it's a modification operation on an object that's not supposed to be modified. By the way, I did think of one argument that supports your position: Suppose you have a reference counted object. You have a 'release reference and free if zero' function. Should it be 'const'? If not, how can a 'lookup and reference for read' function return a const pointer to the object? However, on balance, I think a 'release reference and free if zero' function that operates on a const pointer is sufficiently unusual that a cast to ...
Freeing a const pointer is not and has never been unusual. It happens all the time for objects whose lifecycle is "initialise at the start, readonly afterwards", of which names, in particular in the form of strings, are a large subset. It also happens in cases of late deletion on refcounted objects, when the main owner (the one who is allowed to change the object and has the non-const pointer) has dropped its reference, but some object needs a readonly instance a little longer. Think virtual files in proc, sysfs or friends kept open after the underlying information source, often a device, is gone. OG. --
In C++ you can delete a const pointer. Now I know kernel hackers aren't especially impressed with C++ but maybe someone could look up the rationale for that design decision (I couldn't find it). It might shed some light on this discussion. /DM --
And to demostrate that Linus is not the only person with this view, I copy some paragraphs from C99 rationale (you can find standard, rationale and other documents in http://clc-wiki.net/wiki/C_standardisation:ISO ) Page 75 of C99 rationale: Type qualifiers were introduced in part to provide greater control over optimization. Several important optimization techniques are based on the principle of "cacheing": under certain circumstances the compiler can remember the last value accessed (read or written) from a location, and use this retained value the next time that location is read. (The memory, or "cache", is typically a hardware register.) If this memory is a machine register, for instance, the code can be smaller and faster using the register rather than accessing external memory. The basic qualifiers can be characterized by the restrictions they impose on access and cacheing: const No writes through this lvalue. In the absence of this qualifier, writes may occur through this lvalue. volatile No cacheing through this lvalue: each operation in the abstract semantics must be performed (that is, no cacheing assumptions may be made, since the location is not guaranteed to contain any previous value). In the absence of this qualifier, the contents of the designated location may be assumed to be unchanged except for possible aliasing. restrict Objects referenced through a restrict-qualified pointer have a special association with that pointer. All references to that object must directly or indirectly use the value of this pointer. In the absence of this qualifier, other pointers can alias this object. Cacheing the value in an object designated through a restrict-qualified pointer is safe at the beginning of the block in which the pointer is declared, because no pre-existing aliases may also be used to ...
I'd say this implies the exact opposite. It almost sounds like the compiler is free to change: void foo(const int *x); foo(x); printf("%d", x); to: void foo(const int *x); printf("%d", x); foo(x); especially if it can prove that the pointer to x doesn't otherwise escape or that foo doesn't call anything that could see the pointer (and given that gcc has special magical markings for malloc, one way this could be "proven" is to have x be some freshly malloced object. If foo is kfree, then the above transformation is clearly invalid. (Note that this isn't just a problem for optimizers -- a programmer might expect that passing a pointer to a function that takes a const pointer argument does not, in and of itself, change the pointed-to value. Given that const certainly does not mean that no one else changes the object, I'm not sure what else it could mean. kfree does not have either property, so I'm don't think it makes sense for it to take a const argument.) --
That's only if neither function has side effects noticeable by the Most of the time, const pointer arguments means "I won't change the contents of the object so that you'll notice by reading it in a normal way afterwards". That's pretty much what mutable in a variety of languages (including C++) is about, saying "this field is internal management stuff not visible from the external interface, so I need to be able to change it even through const pointers I got as parameters". Reference counters for copy-on-write setups is the usual example of use. In the case of deallocation functions you are not allowed to do anything through the pointer or its aliases after the function returns. So we're outside of the "most of the time" case, since you're not allowed to try to notice any change. Pragmatism takes over, you want the type that catches as many possible types as possible while staying reasonable (volatile is never reasonable), and that's const void *. As simple as that. As for releasing resources through const pointers, that happens all the time as soon as your const use is tight, and if you think forcing the systematic addition of a (void *) cast is going to make your code more readable, well, you need more experience in maintaining other delete in C++ allows const pointers. Think about it. OG. --
That's what __attribute__ ((pure)) is for, but if none of the
functions is pure, the compiler can not be sure about side effects
and can not reorder things. Don't forget that functions can do
anything apart from mangling with their arguments.
And allocator/deallocator functions never can be pure, they must
change global data, the pool of free blocks.
--
J.A. Magallon <jamagallon()ono!com> \ Software is like sex:
\ It's better when it's free
Mandriva Linux release 2008.1 (Cooker) for i586
Linux 2.6.23-jam05 (gcc 4.2.2 20071128 (4.2.2-2mdv2008.1)) SMP PREEMPT
--
Though it seems it could legally transform:
void kfree(const int *x);
{
int v, *ptr = malloc(sizeof(int));
*ptr = 51;
v = *ptr;
kfree(ptr);
printf("%d", v);
into:
{
int v, *ptr = malloc(sizeof(int));
*ptr = 51;
kfree(ptr);
v = *ptr;
printf("%d", v);
}
if it knows that malloc generates unaliased pointers, which seems
reasonable in general.
--
Krzysztof Halasa
--
I'd say this implies the exact opposite. It almost sounds like the compiler is free to change: void foo(const int *x); foo(x); printf("%d", x); to: void foo(const int *x); printf("%d", x); foo(x); especially if it can prove that the pointer to x doesn't otherwise escape or that foo doesn't call anything that could see the pointer (and given that gcc has special magical markings for malloc, one way this could be "proven" is to have x be some freshly malloced object. If foo is kfree, then the above transformation is clearly invalid. (Note that this isn't just a problem for optimizers -- a programmer might expect that passing a pointer to a function that takes a const pointer argument does not, in and of itself, change the pointed-to value. Given that const certainly does not mean that no one else changes the object, I'm not sure what else it could mean. kfree does not have either property, so I'm don't think it makes sense for it to take a const argument.) --
That's absolutely not true. Let's unravel the code, by fixing usage of 'x' (which seems to vary at will between value and pointer in the above example), and by replacing printf with another opaque function. Our decls: void foo(const int *ptr); void bar(int val); You're saying that this: foo(&x); bar(x); can be reordered into this: bar(x); foo(&x); No way. First, the way that const is currently defined, the compiler cannot assume that the value of x did not change while foo was executing. So, it will not only be forced to leave the two functions in that order, it will even reload the value of x before passing it into bar. Go figure. Second, even if const did have stronger semantics that forbade the value of x from being modified during execution of foo, the compiler still could not reorder the two function calls, before it cannot assume that the two functions (in their internal implementations) do not touch some other, unknown to this code, global variable. -- Vadim Lobanov --
Oh, absolutely. The problem, however, is that very very few people actually
use these function attributes in their code. Heck, we don't even use the
standard restrict keyword, much less gcc-specific function annotations.
I think that one part of the problem is because gcc seems to have had
attribute diarrhea -- I know of noone who can recite more than 10% of the
available attributes without glancing at the documentation. The other part of
the problem is that gcc does no sanity checks on the provided attributes. For
example, the code below compiles perfectly fine without a peep, even
with -Wall and -Wextra.
int global;
void foobar(const int *x) __attribute__((const));
void foobar(const int *x)
{
if (*x)
*(int *)x = 7;
global = 11;
}
*grumble, grumble* :-)
-- Vadim Lobanov
--
The restrict keyword controls aliasing, to be exact. And I'm skeptical that I must ask what relationship you think the const keyword has to compiler optimizations. I know of none, and I've yet to see that keyword cause any difference in the resulting assembly. It forces you to make your code clean and well-structured, but that's about it. Of course, it would be an interesting experiment to potentially redefine the const keyword to have stronger semantics, such as having the compiler assume that a function taking a const pointer argument will not modify the memory the pointer points to, and thus saving itself a memory load in the caller after the function executes, as long as the data is not global. I imagine that this would lead to some simple and measurable optimizations, all the while (this is where I get into hand-waving territory) breaking a minimum amount of code in current existence. But that is emphatically not how C is currently defined, and you're basically inventing an entirely new language... C2009 perhaps? :-) -- Vadim Lobanov --
"restrict" exists for this reason. const is only about lvalue.
You should draw a line, not to make C more complex!
Changing the name of variables in your example:
extern print_int(const int *);
int main(int argc, char **argv)
{
extern int errno;
errno = 0;
print_int(&i);
return errno;
}
print_int() doesn't know that errno is also the argument.
and this compilation unit doesn't know that print_int() will
modify errno.
Ok, I changed int to extern int, but you see the point?
Do you want complex rules about const, depending on
context (extern, volatile,...) ?
ciao
cate
--
On Fri, Jan 18, 2008 at 12:47:01PM +0100, Giacomo A. Catenazzi wrote: You think that I try to put more meaning into const than I do - but I don't. Please read what I wrote, not what you want to think I wrote. I agree that if I said what you seem to imply I said, then I would have been wrong. But I didn't so I'm not ;) -- / jakob --
Except that changing int to extern int makes all the difference in the world: the variable went from being local to being global. The way const is currently defined, however, the compiler cannot take advantage of the fact Sometimes complexity is worth it. -- Vadim Lobanov --
Not at all.
#include <stdio.h>
#include <stdlib.h>
char *lookup[5];
const char *get()
{
*(*lookup = malloc(1)) = '1';
return *lookup;
}
void set(const char *d, char val)
{
for (int i = 0; i < 5; ++i)
if (lookup[i] == d)
*(lookup[i]) = val;
}
int main()
{
const char *p = get();
printf("%c\n", *p);
set(p, '2');
printf("%c\n", *p);
return 0;
}
Do you see anything that casts the const away? No? Me neither. Still,
the memory that p points to was changed, because there was another
The only thing that const can tell you is that you should not modify the
value _yourself_, using that pointer _directly_. It's somewhat like a
soft "half" protected/private specifier. You may read this value, but if
you want to write to it, please use the setter function I provide for
you. Because that setter function might do some special stuff, like
counting how often that value was written.
And accepting a pointer to a const as an argument does _only_ say: It's
ok to call this function if you only received a pointer to a const, the
function does the Right Thing for such pointers. It does not guarantee
at all, that the function won't change the memory the pointer is
pointing to. Take a set of functions that manage memory for foo objects:
const struct foo *get(someIdentifier);
struct foo *makeWritable(const struct foo *);
void free(const struct foo *);
get() returns a pointer to a foo object, and it might return a pointer
to a _shared_ instance. Obviously it should make the pointer const, the
caller should not modify the shared instance.
makeWritable() accepts a pointer to a const foo, because you generally
want to pass it such a pointer to get a non-const one instead. The
function might just use ref-counting and see if it needs to create a
copy returning a pointer to points to a different location in memory or
if it can just return its _internal_ _non-const_ pointer. No casts!
free() also accepts a pointer to a const foo, because you obviously ...