C Semantics, Constants and Pointers

Submitted by Jeremy
on January 17, 2008 - 3:20pm

"'Const' has *never* been about the thing not being modified. Forget all that claptrap. C does not have such a notion," began Linus Torvalds, responding to a query about why kfree() takes a const pointer. He continued, "'const' is a pointer type issue, and is meant to make certain mis-uses more visible at compile time. It has *no* other meaning, and anybody who thinks it has is just setting himself up for problems." He offered two explanations, beginning with simple C semantics, "from a very obvious and very *real* caller perspective, 'free()' really doesn't change the thing the pointer points to. It does something totally different: it makes the *pointer* itself invalid." He then added his second reason, "anything that *can* take a const pointer should always do so. Why? Because we want the types to be as tight as possible, and normal code should need as few casts as possible." When it was pointed out that GCC 4.2 displays warnings when casting a const pointer to a non-const, Linus replied:

"Either don't use a broken compiler (casting a const pointer to a non-const is definitely not a bug), or cast to 'unsigned long' (if it still complains, now the compiler is not just stupid, it's broken). The whole point of memory management is that we know how pointers work, and understand that they have a *bit* representation, not just the C semantics."


From: Johannes Weiner
Subject: Why is the kfree() argument const?
Date: Jan 16, 9:32 am 2008

Hi,

is there any reason why kfree() takes a const pointer just to degrade it
with the call to slab_free()/__cache_free() again?  The promise that the
pointee is not modified is just bogus in this case, anyway, isn't it?

	Hannes
--

From: Linus Torvalds Subject: Re: Why is the kfree() argument const? Date: Jan 16, 11:39 am 2008 On Wed, 16 Jan 2008, Johannes Weiner wrote: > > is there any reason why kfree() takes a const pointer just to degrade it > with the call to slab_free()/__cache_free() again? The promise that the > pointee is not modified is just bogus in this case, anyway, isn't it? "const" has *never* been about the thing not being modified. Forget all that claptrap. C does not have such a notion. "const" is a pointer type issue, and is meant to make certain mis-uses more visible at compile time. It has *no* other meaning, and anybody who thinks it has is just setting himself up for problems. In the particular case of kfree(), the pointer argument *should* be const, and the fact that the C library gets this wrong is not the kernels problem, it's a problem with the C library. Why? - From a very obvious and very *real* caller perspective, "free()" really doesn't change the thing the pointer points to. It does something totally different: it makes the *pointer* itself invalid. In other words, if you think that "kfree()" changed the thing you free'd, you're simply wrong. It did no such thing. The memory is 100% the same, it's just that you cannot access it any more, and if you try, you'll get somebody elses memory. In other words, "kfree()" can be const. - Anything that *can* take a const pointer should always do so. Why? Because we want the types to be as tight as possible, and normal code should need as few casts as possible. Here's a question for you: let's say that you have a structure that has a member that is never changed. To make that obvious, and to allow the compiler to warn about mis-use of a pointer, the structure should look something like struct mystruct { const char *name; .. and let's look at what happens if the allocation of that const thing is dynamic. The *correct* way to do that is: char *name = kmalloc(...) /* Fill it in */ snprintf(name, ...) mystruct->name = name; and there are no casts anywhere, and you get exactly the semantics you want: "name" itself isn't constant (it's obviously modified), but at the same time the type system makes it very clear that trying to change it through that mystruct member pointer is wrong. How do you free it? That's right, you do: kfree(mystruct->name); and this is why "kfree()" should take a const pointer. If it doesn't, you have to add an *incorrect* and totally useless cast to code that was correct. So never believe that "const" is some guarantee that the memory under the pointer doesn't change. That is *never* true. It has never been true in C, since there can be arbitrary pointer aliases to that memory that aren't actually const. If you think "const *p" means that the memory behind "p" is immutable, you're simply wrong. Anybody who thinks that kfree() cannot (or should not) be const doesn't understand the C type system. Linus --
From: Johannes Weiner Subject: Re: Why is the kfree() argument const? Date: Jan 16, 3:19 pm 2008 Hi, Linus Torvalds <torvalds@linux-foundation.org> writes: [...] > "const" is a pointer type issue, and is meant to make certain mis-uses > more visible at compile time. It has *no* other meaning, and anybody who > thinks it has is just setting himself up for problems. [...] > - From a very obvious and very *real* caller perspective, "free()" really > doesn't change the thing the pointer points to. It does something > totally different: it makes the *pointer* itself invalid. > > In other words, if you think that "kfree()" changed the thing you > free'd, you're simply wrong. It did no such thing. The memory is 100% > the same, it's just that you cannot access it any more, and if you try, > you'll get somebody elses memory. > > In other words, "kfree()" can be const. [...] > So never believe that "const" is some guarantee that the memory under the > pointer doesn't change. That is *never* true. It has never been true in > C, since there can be arbitrary pointer aliases to that memory that aren't > actually const. If you think "const *p" means that the memory behind "p" > is immutable, you're simply wrong. Okay, I understood that now. A const qualifier just forbids modifying the underlying memory _through this particular pointer_, right? In the case of slub's kfree(), which takes a const pointer, you pass it on to slab_free() which actually _DOES_ modification of the underlying memory when linking the object to the freelist (as far as I understood the code). So if I got it right and you actually modify the memory you only got a const pointer to, you reach a level where you _have to_ break this policy and cast to a non-const pointer, as it is currently done in kfree(). No? Hannes --
From: Linus Torvalds Subject: Re: Why is the kfree() argument const? Date: Jan 16, 4:16 pm 2008 On Wed, 16 Jan 2008, Johannes Weiner wrote: > > Okay, I understood that now. A const qualifier just forbids modifying > the underlying memory _through this particular pointer_, right? Yes, exactly. > In the case of slub's kfree(), which takes a const pointer, you pass it > on to slab_free() which actually _DOES_ modification of the underlying > memory when linking the object to the freelist (as far as I understood > the code). First off, the whole concept of "underlying memory" is all about the fact that pointers point not to separate objects with its own existence, but point to a shared resource ("memory") that can be accessed many different ways. So immediately when you talk about "underlying memory", you need to realize that now you're not talking about "that pointer" any more, but you are talking about the stuff that *other* pointers can access. And the whole (and *only*) point of a memory allocator is turning that amorphous notion of "infinitely aliasable underlying memory" model into more of a "C pointer to separate objects" model. That *is* what a memory allocator does. > So if I got it right and you actually modify the memory you only got a > const pointer to No. YOU DO NOT. You *invalidate* the pointer. Then, the memory allocator releases that portion of the "infinitely aliasable underlying memory", but that means that THAT POINTER NO LONGER EXISTS ON A C LEVEL. So no, you don't "modify the memory you only got a const pointer to". What you do is that the memory allocator - keeps track of the NON-const memory that it always had - it sees the const pointer you gave it, and uses that pointer to look up ITS OWN DATA STRUCTURES. - it then accesses those memory locations using its own pointers. The "const pointer" you passed to kfree() simply no longer exists. The object it pointed to has gone away. That particular pointer simply isn't valid any more. But the memory management code, which maintains its own structures for what it has allocated and not allocated, can (and will) generally use the knowledge *it* has about its own pointers to modify its own data structures. The fact that they (obviously) alias in memory is irrelevant. It has no meaning for the "const void *" you passed in. That pointer is not usable for the caller any more. And no, this is not just a semantic argument. It's a real issue. The pointer you pass in to kfree() is obviously used to *find* the data structures that the memory allocator maintains, and there is almost inevitably a rather direct mapping between the passed-in pointers and the data structures that a memory manager maintains itself, but they really are different. So to take a kfree() a pointer, the memory manager will use the "binary value" of that pointer to find its own data structures, and sometimes the values are so closely related that it ends up being a direct cast (possibly with an offset), but the end result of that direct cast really is now the memory management internal data, not the original pointer. A real example of this is the actual kfree() implementation in the kernel (let's take the one from SLAB): struct kmem_cache *c; .. c = virt_to_cache(objp); which then actually ends up doing a struct page *page = virt_to_head_page(obj); return page_get_cache(page); which literally does math on the binary representation of the pointer to look up the backing store values (because the memory manager by definition is the thing that knows how the low-level representation of pointers actually works). So now the kernel has turned that user-supplied pointer into ITS OWN data structures, that contain the NON-CONST aliases for the data. (It will actually use the const pointer to further get offsets into those things, of course, so it's several levels of indirection off that pointer) And yes, then it will write to those non-const aliases, but that's really no different from the same old thing: the pointer *you* passed kmalloc() may be "const", but the memory manager itself has its own data structures to keep track of *its* aliases to the same memory, and those are the ones that are actually used to re-use the memory for others (and poison it etc). So the particular pointer you passed in is "const". But that never *ever* guarantees that there aren't other non-const pointers around to be used that can modify the memory. And the memory manegement definitely has all those aliased pointers around - it's the whole *point* of memory management. Linus --
From: Christoph Lameter Subject: Re: Why is the kfree() argument const? Date: Jan 16, 3:20 pm 2008 On Wed, 16 Jan 2008, Johannes Weiner wrote: > So if I got it right and you actually modify the memory you only got a > const pointer to, you reach a level where you _have to_ break this > policy and cast to a non-const pointer, as it is currently done in > kfree(). No? Correct and we have gcc 4.2 currently spitting out warnings because of casting to non const. Any idea how to convince gcc that this is okay? --
From: Linus Torvalds Subject: Re: Why is the kfree() argument const? Date: Jan 16, 4:18 pm 2008 On Wed, 16 Jan 2008, Christoph Lameter wrote: > > Correct and we have gcc 4.2 currently spitting out warnings because of > casting to non const. Any idea how to convince gcc that this is okay? Either don't use a broken compiler (casting a const pointer to a non-const is definitely not a bug), or cast to "unsigned long" (if it still complains, now the compiler is not just stupid, it's broken). The whole point of memory management is that we know how pointers work, and understand that they have a *bit* representation, not just the C semantics. Linus --

It must be a bug

1052 (not verified)
on
January 17, 2008 - 4:33pm

I cannot reproduce the warning with gcc (GCC) 4.1.2 (Ubuntu 4.1.2-0ubuntu4) or a recent revision of GCC 4.3.

-W flags

Mr_Z
on
January 17, 2008 - 7:08pm

Are you using any -W flags? I believe by default GCC does not warn for casting away const, but the kernel turns on many warning flags, and one of them is probably the culprit.

--
Program Intellivision and play Space Patrol!

A compiler warning is not

Jose_X (not verified)
on
January 20, 2008 - 1:24am

A compiler warning is not how a compiler says that a program is not following the standard. To do so, it would generate an error message or not produce the compiled output (that's basically what a compiler must do according to C99, IIRC).

GCC seems to have it correct here.

As for the C semantics ...

Anonymous (not verified)
on
January 18, 2008 - 2:15am

... or cast to "unsigned long" (if it still complains, now the compiler is not just stupid, it's broken).

According to the C specification, you can only cast an object pointer to an integer using either intptr_t or uintptr_t and back again without losing information (the original pointer is guaranteed to be the same). However, these types are optional (see 7.18.1.4).

I know, this is not exactly what Linus was talking about, but I'm reminding you user-land programmers that you cannot (or at the very least *should not*) cast pointers to integers and vice versa unless you know exactly what you're doing.

>> According to the C

Jose_X (not verified)
on
January 20, 2008 - 9:49am

>> According to the C specification ...

According to C99, I would say that Linus was wrong when he said,
>> "const" has *never* been about the thing not being modified. Forget all that claptrap. C does not have such a notion.

http://std.dkuug.dk/jtc1/sc22/wg14/www/docs/n843.htm Section 6.7.3 constraint #3: "If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined." "Object" refers to the value/thing held in memory or on a register etc, ie, the content. Generally (IIRC), undefined behavior means you are not following the spec (the app is not "conforming").

If Linus was not talking about C99 when he said "C," he should have been more specific I think (he may have been describing some older C standard). Certainly, the kernel has to do what it has to do.

Anyway, I was referring to C99 (a draft of it). There is no rule that any app must conform to C99. Maybe this is what Linus meant or would have meant if he were an expert in C99 (I don't pretend to be such an expert, btw.. and I am guessing that Linus is not such an expert either and has plenty to keep him busy).

Some more details for the very interested

Jose_X (not verified)
on
January 20, 2008 - 11:44am

"6.5.16.1 Simple assignment
...
-- both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;"

*Basically* the first part means that when the two expressions, one on each side of "=", are pointer to X and pointer to Y, then X and Y must be identical (I won't discuss the ways in which they can sort of differ). In particular, X and Y must have "const" in the exact same locations.

The second part says that for the actual pointers (not what they point at), the left one must have "const" "volatile" and "restrict" (ie, the qualifiers) if the right one has any of those (and it can also have any of these even if they are missing on the right).

[Keep in mind that the language of the C99 standard is not simple, and it is difficult to explain accurately without quoting a huge amount of stuff. I am trying.]

That same section then continues with an example just to show that the thing being pointed to must match (basically) exactly, ie, X and Y must be qualified identically.

If given:
"const char **cpp;
char *p;"

Then further down in the application, the following would be INcorrect:
"cpp = &p // constraint violation"

The expression on the right "&p" is a pointer to "char*" which is not the same as "const char*" which is what the left side points to. Thus we violate the first part of the rule at the top by not having the pointer on the left point to something with the same type as that which the pointer on the right points.

Now another example.

It would be fine to then add the following:
"char*const*r = &p;"

since each of these points to plain old "char*". In this case the left pointer is const while the right one is not, but that is ok according to the second part of the rule at the top.

What you can't have according to the second part of the rule at the top is a nonconst pointer (on the left) accepting the value of a const pointer (on the right) as this shows:

char *const*xxx = ....; //ie, set xxx to something legitimate and this will be xxx's value forever.
char **yyy;
//yyy = xxx //bad because const pointer on right but not on left.

And do note that yyy and xxx point to compatible types "char*". In this example we only violated the second part of the rule not the first.

So you want the things being pointed to to be the same and you want the left pointer to be const if the right one is const.

More pseudocode examples:
const char ***const** = const char ***const** //fine, they are identical in all respects
const char ***const*const* = const char ***const** //fine because right need not be a const pointer
const char ***const*const* = const char ***const*const* //fine, they are identical in all respects
const char ***const** = const char ***const*const* //bad because left must be const pointer if right is const pointer
const char ***const** = char ***const** //bad because the type pointed to on the left does not match the one pointed at the right (const char ... vs char ...)
const char ***const** = const char **const** //bad because the types pointed to don't match. Note that the right is one "*" short.
const char ***const** = const char **const*** //bad again since they still don't match.

Don't forget the role of casts.

Mr_Z
on
January 21, 2008 - 8:58am

What you quoted forbids this:

    const char *const_string_ptr;
    char       *string_ptr;

    /*...*/

    string_ptr = const_string_ptr;   /*ERROR!*/

but it does not forbid this:

    const char *const_string_ptr;
    char       *string_ptr;

    /*...*/

    string_ptr = (char *)const_string_ptr;   /* OK! */

The l-value (string_ptr) has all the qualifiers of the r-value (the value of the expression (char *)const_string_ptr), since we casted away the const.

--
Program Intellivision and play Space Patrol!

I was not as precise as I

Jose_X (not verified)
on
January 22, 2008 - 12:02am

I was not as precise as I could have been in order to make the discussion readable to a larger audience. For example, I don't think "variable" is ever used normatively in the standard. The references are to identifiers (some of which can be "variables"), l-values, constructs that aren't either of these, etc.

Nevertheless, I took some care to avoid cast examples altogether and did specifically say "pseudocode" for the portion at the end that might be ambiguous. I also remember using terms like "operand" and such just to avoid these issues. The "operand" would be the cast and all. Thus if you cast away, then you don't have the const *qualifying the type* on the right side.

So were you just pointing to casts as a fyi, or were you saying that I goofed?

Please point out goofs, so I can correct them (or come up with an excuse :-) ).

I was just clarifying....

Mr_Z
on
January 22, 2008 - 10:36am

My point is that saying const char * does not define a const-qualified object, and so casting const char * to char * is not necessarily a semantic error by the two sections you quoted. Saying const char foo[] = "abc" does, however, define a const-qualified object.

A pointer-to-const can just as easily point to a const-qualified object as it can a non-const-qualified object, which a major reason why casting away const is even worth entertaining.

The malloc/free situation that started this discussion is a different (but also important) one. Memory allocation is much more inherently specific to the details of the implementation.

--
Program Intellivision and play Space Patrol!

"Pointer to const" != "object defined with const qualified type"

Mr_Z
on
January 21, 2008 - 8:53am

If I say:

    const char my_const_string[] = "Hello world";

then my_const_string refers to an object (the text "Hello world") defined with a const-qualified type. Definition refers to providing the body of the object, e.g. its initializer. Since the const-ness of the object is known at compile time, the compiler and/or linker can put that object into read-only storage, or do other optimizations such as string-packing.

Compare that to:

   const char *const_string_ptr;

Here, const_string_ptr itself is not const (the pointer can be made to point anywhere at runtime), but whatever it points to should be treated as const when referred to by that pointer variable. It says nothing else about other pointers that might point to an object that that pointer points to. Suppose then I have the following:

   const char *const_string_ptr;
   char       *string_ptr;
   char        my_writeable_string[] = "Hello world";

   /* ... */

   string_ptr       = my_writeable_string;
   const_string_ptr = my_writeable_string;

This is perfectly fine. And, if I write *string_ptr = 'J', that will change the string to read "Jello world", which is just fine, and completely legal. The existence of a pointer-to-const that happens to point to this string doesn't make it illegal. If I were to go a step further and say:

    const_string_ptr = my_writeable_string;
    /* ... */
    string_ptr       = (char *)const_string_ptr;
    *string_ptr      = 'M';

That's where things look iffier, but only because we've exceeded the ability of C's type system to catch programming errors. In this case, since const_string_ptr was initialized with a pointer to writeable storage, it happens to be legal, although one could argue it's a violation of interface. (Rather than casting away const, a proper interface might instead keep both const and non-const versions of the pointer around.) In this specific example, the code correctly changes the string to read "Mello world".

If const_string_ptr actually was initialized with a pointer to an actual const-defined object (such as my_const_string in my very first example), then this is an error that will lead to undefined results. The fact that the cast above makes it impossible for the compiler to distinguish the two is the reason I say we've exceeded the type system's ability to detect programming errors. In recent GCCs, -fwriteable-strings is deprecated, which implies that GCC makes an effort to move these const strings into read-only storage. That also implies that you could get whacked with a segmentation fault if you try to write to one.

What that also means is the old practice of writing something like this now gives you warnings or errors (if you turn your warning level up or request C99), because it's technically incorrect code:

    char *dodgy_string = "Hello world";

This does not allocate writeable storage for the string "Hello world", and in fact is throwing away const. That's why I wrote char my_writeable_string[] above, because that form does allocate writeable storage for the string.

--
Program Intellivision and play Space Patrol!

Not sure if I follow the last section

Jose_X (not verified)
on
January 22, 2008 - 1:47am

>> If const_string_ptr actually was initialized with a pointer to an actual const-defined object (such as my_const_string in my very first example), then this is an error that will lead to undefined results. The fact that the cast above makes it impossible for the compiler to distinguish the two is the reason I say we've exceeded the type system's ability to detect programming errors.

I'm not sure I know what you mean by "makes it impossible for the compiler to distinguish the two."

First, it's not clear to me that anything here would necessarily be impossible (perhaps instead inefficient or very complex). [Warning: I have limited experience with compiler internals.]

Second, for the two scenarios you described, one is defined and the other is undefined. I don't see how these must necessarily correlate in some specific way to "programming errors."

Anyway, assuming I made sense, don't be too tempted to waste too much brain energy on this. I am somewhat curious, but I'd hate to think this minor query would side-line you for a while (and I may even have lost my mind for enough minutes to have written up this reply but will repent once I get some sense back).

Interesting and fairly thorough post, btw.

>> In recent GCCs, -fwriteable-strings is deprecated, which implies that GCC makes an effort to move these const strings into read-only storage. That also implies that you could get whacked with a segmentation fault if you try to write to one.

If you follow the standard, then you should never get whacked for all correct, C99-defined code. Are you saying gcc is wrong, or are you saying that there is some incorrect code you can get away with today, but won't be able to later on?

>> What that also means is the old practice of writing something like this now gives you warnings or errors (if you turn your warning level up or request C99), because it's technically incorrect code:

I am not sure I know what code you are referring to. I thought the examples shown were correct, and the hypothetical case not explicitly shown was acknowledged as undefined.

>> char *dodgy_string = "Hello world";

>> This does not allocate writeable storage for the string "Hello world", and in fact is throwing away const.

Did you word this properly?

If you don't allocate writeable storage then it seems to me you would never be "throwing away" a const.

I guess I was slightly mistaken.

Mr_Z
on
January 22, 2008 - 10:25am

Taking your last point first: As I mentioned in a reply below, the type for a string literal such as "Hello world" is not actually const-qualified. I guess I've been brain-damaged by the GCC extension -Wwrite-strings that treats it as such. (The warning GCC generates suggests that a const qualification gets dropped by the assignment.) By the word of the standard, a string literal has elements of type char, not const char, even though the literal may be allocated in unwriteable storage and may not be guaranteed to be unique.

This seems to be a bit of weasel wording in the standard to codify existing practice and smooth over hiccups rather than make the type system reflect the reality that a literal such as "Hello world" is, in fact, effectively const.

Now going back to your first point:

I'm not sure I know what you mean by "makes it impossible for the compiler to distinguish the two."

First, it's not clear to me that anything here would necessarily be impossible (perhaps instead inefficient or very complex). [Warning: I have limited experience with compiler internals.]

If given a translation unit in isolation, the compiler can not determine if the object passed in is in writeable storage or unwriteable storage when it sees something like this:

char *foo(const char *bar)
{
    return (char *)bar;
}

It simply does not have sufficient information to make that determination. Yes, it likely is possible to construct a compiler that, with full view of the program, determines that all invocations of foo() do indeed pass in pointers to storage that is indeed writeable. I would argue that, short of actually simulating the program to work out all cases of coupling across all translation units, it's not possible to do this in the general case. I would also argue that it's not worth constructing such a compiler. :-)

Second, for the two scenarios you described, one is defined and the other is undefined. I don't see how these must necessarily correlate in some specific way to "programming errors."

What I'm saying is that C doesn't offer a distinction between "this object is immutable" and "this object is not directly modifiable via this access path" in its type system. Writing incorrect code is an error on the part of the programmer. Mixing up the two concepts is such an error.

C's types (particularly its type qualifiers) exist in part to help catch when the programmer makes mistakes. That one can easily obtain a pointer that permits writing (or at least attempting to write) to immutable storage without triggering a diagnostic is a case where the C type system does not help catch the programmer's mistakes. Casts and other mechanisms for discarding a const qualification on a pointer to immutable storage (or failing to establish a const qualification in the beginning, as in char *dodgy = "foo") are places where the programmer shuts off C's safety mechanisms and says "trust me," and thus are places where it's easy for the programmer to make mistakes that the compiler can't warn about in the general case.

(Clearly, mechanisms such as -Wwrite-strings show it can warn about specific cases that look suspect.)

And yes, I consider code that results in undefined behavior to be incorrect. If an implementation defines a particular behavior for code that is left undefined by the standard, then code that relies on that definition is likely unportable across all standard implementations, although it could be considered correct on compatible implementations. Realistically, most code relies a bit here and there on some behavior that's undefined in the standard, but defined consistently across many implementations. (Signed integer overflow, anyone? In very many contexts it is perfectly safe and does what you expect across the bulk of implementations, and yet it's undefined! And guess what, it can and does lead to surprises occasionally, but that doesn't end its popularity or diminish its usefulness.)

--
Program Intellivision and play Space Patrol!

About portability and behavior

foo (not verified)
on
January 23, 2008 - 2:31am

You mention signed integer overflow as an example of undefined behavior, but where the actual behavior is mostly what you would expect.

What worries me is that people writing such code often does it unintentionally, unaware of what they're actually doing. They write some code, compile it, get no warnings, and the code does what they expect it to do. They then conclude that "yes, this code is correct and it works" when in reality it is wrong and may blow up in your face.

Take, for instance, the following code:

if (c >= 'a' && c <= 'z')
   /* c contain a lowercase letter */

I'm pretty sure you've seen this, or something similar, before. While this may seem okay, the C language does not guarantee that this will work.

The code makes assumptions about the ordering of characters, which will break if you try to run it on an EBCDIC (http://en.wikipedia.org/wiki/EBCDIC) system, for instance.

The above code should have been written as:

#include <ctype.h>

if (islower (c))
   /* c contain a lowercase letter */

Note that "if (c >= '0' && c <= '9') ..." is perfectly fine (it is guaranteed that '0' + 1 is '1' and so on), though I'd use "if (isdigit (c)) ..." for readability.

Another problem I often see is making assumptions about the size and representation of integers. While int usually has 31 value bits and 1 sign bit, it is only guaranteed to be able to represent numbers in the range -32767 and 32767 (see <limits.h>). You should use a long for values outside of this range. People also assume that a char is 8 bits, which is not guaranteed to be true (but is true in most cases). They also often assume an unqualified char is signed (which is true for the rest of the integers, but not necessarily for char).

More often than not (for the average user-land programmer) you don't need exact-width integers. If you do, there's always <stdint.h> for intN_t in C99).

It all boils down to how much you care about your code and how much of a pedant you are. Sometimes the rules have to be broken.

Anyway, back to work. Happy coding ;-)

"Throw away const" ?

Anonymous (not verified)
on
January 22, 2008 - 8:17am
char *dodgy_string = "Hello world";

This does not allocate writeable storage for the string "Hello world", and in fact is throwing away const.

What do you mean it is "throwing away const"? The (immutable) string literal "foo" has type char [4]. In a value context, as you're most certainly aware of, an array object "decays" into a pointer to its initial element (a char * pointing to f in this case). No const is being thrown away, since there never was a const here.

Having said that, I do believe that it's a really good idea to avoid the above and write it like this instead

const char *pointer_to_immutable_string_literal = "read only";

However, both forms are perfectly legal. The latter just makes it harder to modify the string literal (which may be placed in read-only memory).

Ah, standards weasel wording vs. GCC extensions

Mr_Z
on
January 22, 2008 - 9:42am

Hmmm. I was under the impression that when you define a string literal such as "Hello world", its type is const char[N] (where N is determined by the length of the string, as you note), not char[N]. In a pointer context, it would then decay to const char *, with the pointer pointing to the first element of the array (first character of the string in this case). So, if you say char *foo = "Hello world", you'd be assigning a const char* to a char *. How would this not be throwing away const without a cast?

When you write char foo[] = "Hello world", the string literal "Hello world" is merely an initializer for a char array of unspecified length. (The length, again, gets determined by the length of the string.) The difference between the two forms is that the latter form allocates a writeable array to copy the string literal into, and the former just yields a pointer to the potentially-unwriteable string literal.

Now, all that said, it appears that according to this that the type isn't actually const qualified. Rather, the standard merely says that attempts to modify a string literal result in undefined behavior. *sigh* So, I guess I was technically incorrect by the terms of the standard. In terms of what mental model you should have in mind when coding, though, char *dodgy_string = "I should be treated as const char" is the right model.

I guess it's a GCC extension to optionally treat string literals as being arrays of const char. If you compile with -Wwrite-strings (which seems like a good idea), GCC gives you a warning suggesting const is being discarded when presented with the above construct:

$ cat c.c
char *dodgy_string = "I should be treated as const char";
$ gcc -Wwrite-strings c.c
c.c:1: warning: initialization discards qualifiers from pointer target type

--
Program Intellivision and play Space Patrol!

Still not throwing ...

foo (not verified)
on
January 22, 2008 - 11:21am

So, if you say char *foo = "Hello world", you'd be assigning a const char* to a char *. How would this not be throwing away const without a cast?

It would if "foo" where in fact a const char [4]. To the best of my knowledge, it is not. It is a char [4] (as your reference to the standard also seem to indicate, though the standard is not exactly easy reading ;-) ).

If it where in fact a const char [4], compilers should warn about things like char *s = "foo"; discarding qualifiers (const).

In terms of what mental model you should have in mind when coding, though, char *dodgy_string = "I should be treated as const char" is the right model.

I absolutely agree, which is why I advice people to write that as const char *ro = "read-only";. If you want a modifiable string, initialize an array with a string literal, e.g. char rw[] = "read-write";.

However, it does not help when people write code like strtok ("foo bar baz", " "); and expect this to work. GCC (4.1.3) does not complain about it (unless you add the -Wwrite-strings option, which I also think is a good idea).

I said I was wrong. :-)

Mr_Z
on
January 22, 2008 - 4:01pm

I should have put my mea culpa in bold. You missed where I had said: ". . . the type isn't actually const qualified. Rather, the standard merely says that attempts to modify a string literal result in undefined behavior. *sigh* So, I guess I was technically incorrect by the terms of the standard."

:-)

-Wwrite-strings is a good thing. When I added it to jzIntv's warning flags, it actually caught a case where I had a thinko where a string literal could get passed to free(). *doh* I only noticed when when I started const-ifying things. (In this case, I replaced "literal" with strdup("literal"), to match the semantics of other pointers that could end up in that variable.)

The other thing I was saying is that -Wwrite-strings is what makes string literals have const char[] type. Here's what the GCC docs have to say:

`-Wwrite-strings'                                                               
     When compiling C, give string constants the type `const                    
     char[LENGTH]' so that copying the address of one into a                    
     non-`const' `char *' pointer will get a warning; when compiling            
     C++, warn about the deprecated conversion from string literals to          
     `char *'.  This warning, by default, is enabled for C++ programs.          
     These warnings will help you find at compile time code that can            
     try to write into a string constant, but only if you have been             
     very careful about using `const' in declarations and prototypes.           
     Otherwise, it will just be a nuisance; this is why we did not make         
     `-Wall' request these warnings.    

Matches my philosophy on const too. How convenient. :-)

--
Program Intellivision and play Space Patrol!

Did not miss

foo (not verified)
on
January 23, 2008 - 1:13am

I should have put my mea culpa in bold. You missed where I had said: "[...] So, I guess I was technically incorrect by the terms of the standard."

I did not miss that part. I read your entire post, as I did with the first one. But before you said that you where wrong, you asked "How is that not throwing away const?" (or something to that effect), to which I had my go at why that was not so ;-)

I replaced "literal" with strdup("literal")

For the pedants, and those who care fanatically about portability, I just want to mention that strdup is an extension to the C library (it is not part of C).

My CFLAGS contain at the very least -W -Wall -ansi -pedantic (and usually a lot more, sometimes sans -ansi depending on what I'm doing).

As for the -Wall option, that should really be called -Wmany, or something that does not mean all, because it does not enable all warnings. Those who care should read the relevant {man,info}pages.

End Of Rant. Happy coding ;-)

Heheheh

Mr_Z
on
January 23, 2008 - 2:06am

I tried to say was "if my understanding were in fact true, then how would that not be casting away const? Of course, that said, my understanding was wrong." It turns out my understanding is true in C++ and with my favorite warning flags. Some of the old C practices just make me want to shudder. :-)

I'm with you on -Wall being a misleading name. My current default flags for jzIntv are -Wall -W -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-qual. Before my next release, I want to get it clean or mostly clean with -W -Wall -ansi -Wbad-function-cast -Wcast-align -Wcast-qual -Wchar-subscripts -Winline -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wredundant-decls -Wshadow -Wstrict-prototypes -Wwrite-strings. Now how's that for flipping all the compiler's bucky bits? Right now, that warning slate screams bloody murder on my code, but I intend to clean it up.

As for strdup, that's why I have this in my plat_lib.c file:

/* ------------------------------------------------------------------------ */
/*  STRDUP           -- Copy a string into freshly malloc'd storage.        */
/*                                                                          */
/*  Unfortunately, strdup() is not specified by ANSI.  *sigh*               */
/* ------------------------------------------------------------------------ */
#ifdef NO_STRDUP

char * strdup(const char *s)
{
    int len = strlen(s) + 1;
    char *new_str = malloc(len);

    if (new_str) strcpy(new_str, s);

    return new_str;
}

#endif /* NO_STRDUP */

I have similar nonsense for stricmp / strcasecmp. I like portability. :-)

Happy coding! ;-)

--
Program Intellivision and play Space Patrol!

Just a tiny nit ...

foo (not verified)
on
January 23, 2008 - 2:36am
int len = strlen(s) + 1;

Boo! strlen returns a size_t, not an int. Yes, I'm slightly pedantic ;-)

UB

Anonymous (not verified)
on
January 22, 2008 - 2:34am

Generally (IIRC), undefined behavior means you are not following the spec

When you invoke undefined behavior, it means just that; the behavior of what you're doing is not defined. The result of evaluating a[i++] = i++; can be having your program print 42 or delete all files in the current directory. Both are equally valid results (though not very likely, I'll admit ;-) ).

Implementation defined behavior, however, generally means you're just losing portability. The behavior is defined by the implementation, and is therefore subject to change between implementations (even between versions of the same implementation, so it's a really bad idea to rely on implementation defined behavior).

However, things do seem to work a bit differently in kernel land than in user land, and for many there are more important things in life than following the C specification to the letter ;-)

>> When you invoke undefined

Jose_X (not verified)
on
January 23, 2008 - 2:11pm

>> When you invoke undefined behavior, it means just that; the behavior of what you're doing is not defined.

I wasn't sure the moment I posted the parent comment, but after looking back, I believe strictly conforming programs do not rely on undefined behavior (at least not without conditional preprocessing blocks, where that is possible). (Ordinary) conforming programs can, however, at least if they are accepted by at least one conforming implementation (essentially C99, plus extensions as allowed by C99).

So yes, you can be legal but only in the more limited of the two possibilities for conformity (universal portability among conforming implementations/platforms would be sacrificed).

>> Both are equally valid results (though not very likely, I'll admit ;-) ).
>> Implementation defined behavior, however, generally means you're just losing portability.

Relying on undefined behavior is generally at best no more portable than relying on implementation-defined bahavior. At least with implementation defined behavior, you know you can expect the behavior to be documented across all conforming environments.

...Or so I think.

IIRC, there are three flavors

Mr_Z
on
January 23, 2008 - 11:01pm

If I recall, there are effectively three flavors:

  1. Strictly conforming programs rely on no implementation defined or undefined behavior. Such programs ought to run on all conforming implementations. They also tend to be next to impossible to write, and probably aren't very interesting on any scale much larger than a couple standalone functions.
  2. Conforming programs rely on the behaviors defined by a conforming implementation. This means they do not rely on any behaviors left undefined by either the standard or the implementation.
  3. Everything else.

A conforming implementation must give definitions to behavior the standard leaves as implementation defined. A conforming implementation may give definitions to behavior left undefined by the standard, since conforming implementations are allowed to define extensions.

Fortunately for us, most of the popular conforming implementations these days make the same choices for most of the implementation defined details, such as integer divide rounding to 0 instead of -oo, 2s complement arithmetic, 8-bit signed char, 16-bit short and 32-bit int, and so on.

--
Program Intellivision and play Space Patrol!

Portable

Anonymous (not verified)
on
January 24, 2008 - 1:13am

Relying on undefined behavior is generally at best no more portable than relying on implementation-defined bahavior.

Dereferencing a NULL or an uninitialized pointer is generally a lot worse than, say, assuming that a char is signed. The former is undefined behavior, the latter implementation-defined.

I don't think you can argue undefined behavior in terms of portability. Undefined behavior is something you generally want to avoid, completely. Why? Because what you're doing has no defined behavior. The code is simply wrong.

But undefined by whom?

Mr_Z
on
January 24, 2008 - 2:33am

A given platform may choose to define the behavior that the standard leaves undefined, and it may have good reasons for doing so.

For example, writing to one member in a union and reading the result from a different member in the same union is undefined. However, this is the very technique some programs use for detecting endianness, for example. I use it with bitfields to decode instructions in jzIntv.

Technically it's undefined, but many, many implementations have chosen to define how unions and packed bitfields work in a compatible manner such that jzIntv has been ported to at least 5 different compilers across several platforms. Compilers: GCC, TI's cl6x, Metrowerks C, the Intel C compiler, and Sun's Sparcworks compiler. Platforms: Linux (32 bit and 64 bit), Solaris, DOS (w/32-bit extender), Win32, MacOS 9, MacOS X (PPC and x86), FreeBSD (32 bit and 64 bit), GP2X, PSP, and a standalone DSP board. With that kind of portability record, it's hard to argue that something left undefined by the standard is by definition unportable and therefore neither useful nor usable.

That said, most undefined things really are problems waiting to happen. So, you really do need a sense of what's undefined by the standard just because they're trying to include some esoteric platforms that most people are unlikely to encounter, and what's undefined by the standard because it's really a very good idea to leave it undefined.

For example, most of the prohibitions on pointer wrangling deal with the fact that pointers may not have a nice, linear representation on some supported systems (small vs. medium vs. huge model under 16-bit DOS, anyone?). This is a place where the standard bent over backwards to be inclusive. But so many systems offer nice linear-addressing pointers now that it's acceptable to write your malloc/free in the C offered by the compiler rather than escaping to assembly language, and still have portability across a wide range of systems.

--
Program Intellivision and play Space Patrol!

Undefined by ISO

Anonymous (not verified)
on
January 24, 2008 - 5:59am

I use it with bitfields to decode instructions in jzIntv.

I'm pretty sure you could have done the same job just as well using an unsigned integer (or an array of such) and #define or enum to create symbolic names for your bit-masks to fetch the data you wanted (possibly using a set of macros or functions). If not that, then something else that has well defined behavior, now and in the future.

People are usually very creative when it comes to finding excuses that allows them to "break the law" ;-)

[long list of supported platforms and compilers] With that kind of portability record, it's hard to argue that something left undefined by the standard is by definition unportable and therefore neither useful nor usable.

I agree, but are you sure you can say the same in, say, 5 or 10 years? 15 years? Things change, and compilers are there to hide those changes and the dirty details so we don't have to worry too much about it (unless you used a dirty hack that no longer works).

That said, there are always exceptions (I'm not sure if your union hack is one). Sometimes the law has to be broken, and sometimes it's silly not to break it because the rule is just stupid ;-)

..thinking twice

Sergiu (not verified)
on
April 17, 2008 - 2:10am

I believe that C99 "feature / behavior" you described above does not represent an "evolution / improving" of the C standard / functionality, but an adaptation to help / protect the more growing number of 'programmers' who do not understand the real(logical) purpose of const. The explicit affirmation "the behavior is undefined" states it clearly from my point of view that it is more of an 'crafty - job' than an well designed mechanism. P.S. Of course that i'm not 'such an expert' either but i have a feeling that if you deny this term to Linus you definitely cannot qualify the iso c99 boys for it

WTF?

Pascal Troll (not verified)
on
January 18, 2008 - 5:29am

> "const" has *never* been about the thing not being
> modified. Forget all that claptrap. C does not
> have such a notion.

I never thought I would be giving C classes to Linus, but... WTF? Of course C has the notion of "the thing not being modified". It's just that the declaration _in question_ means the pointer, not its contents, is unmodifiable. If it was declared "const *const p" then the contents would also be unmodifiable.

Wrong

Anonymous (not verified)
on
January 18, 2008 - 7:22am

If you're saying that "const char *foo" means the pointer itself is unmodifiable, you're wrong. That would be "char *const foo". The qualifier always qualifies the thing immediately to the right of it (more or less). In the case of "const char*" or "char const *" it qualifies the memory pointed to, in the case of "char *const foo" it qualifies 'foo' the pointer itself.

In any case...

Emopig (not verified)
on
January 18, 2008 - 9:35am

Grandparent got it right, you misread him.

In any case, passing a non-inline function a "some_type *const some_var" parameter is often meaningless, since the pointer itself is local and changing it cannot possibly effect anything outside of the said function.

If you really wanted to you could pass all pointers as "*const" and use them as read only bases for any pointer manipulations you wanted to carry out using other local stack variables.

As for casting away "const", well that should **IMHO** only be valid on pointers other fundamental/primitive types for which working with some reference (Be it C++ references or pointers) is just as expensive in terms of memory and CPU time as taking a copy.

Linus is absolutely correct that "const" has no meaning when it comes to differentiating "invalidating" and "modifying" the pointer target, as used in a free() function.

to const or not to const

Anonymous (not verified)
on
January 18, 2008 - 7:26am

If it was declared "const *const p" then the contents would also be unmodifiable.

What if I then write T *q = (T *)p; (T is whatever basic type p is). Now p is modifiable through q, is it not? However, the specification says that const objects can be placed in read-only memory, thus trying to modify such an object is not a great idea (like trying to modify string literals such as "foo"[0] = 'b'; etc.)

At any rate, C and const has some weirdness in it. For instance, you cannot do things like const int n = 10; int a[n];. Why not? Is n not an integer constant expression? Obviously not.

I use const mostly for documentation and to have the compiler throw warnings when I accidentally attempt to modify things I should not modify.

size_t strlen (const char *s); tells me that s (or, rather, what s points at) will not be modified by strlen. It tells me it's safe to do strlen ("foobar");.

On the other hand, char *strtok (char *s, const char *d); tells me that strtok will modify s but not d. It tells me it is not safe to do strtok ("foo bar baz", " ");.

dynamic memory allocation

strcmp
on
January 18, 2008 - 8:36am

const is sometimes incompatible with dynamic memory allocation. some structure may be constant throughout most of your program, but at one point you have to initialize the memory your const* points to. you can achive that by allocating a pointer to non const first and only assign the initialized contents, but depending on your data structures or program logic (e.g. memory allocation of a generic structure vs. initialization for a special purpose; allocations from a free list that contained pre-allocated but uninitialized structures) this may be impossible without tricks like having a special version of the structure without const annotated internal pointers (best generated with preprocessor trickery...). and of course you have to free the structures after use. how do you NULL out your 'this must never be changed' const*const pointer after having freed the data it points to (i hope you don't like dangling pointers), how did you free the data in the first place?

another problem is demonstrated by char *strchr(const char *s, int c); : strchr("hello", 'h') returns a non-const-pointer to read-only memory, i.e. casts const char* to char*. if you want to change that, you can't use the function for purposes where you want to change the string, e.g. to implement something like strtok(...). do you want 2 versions of the function, one with const result, one without? how should the non-const version check if you called it with a string that should be const? should functions never return pointers but only size_t offsets to their arguments, which makes the handling more complicated and un-C-like?

const is an important hint for error prevention and optimization, but it must be possible to override it in controlled situations, else you can't use it at all.

"const is an important hint

Emopig (not verified)
on
January 18, 2008 - 9:37am

"const is an important hint for error prevention and optimization, but it must be possible to override it in controlled situations, else you can't use it at all."

... which is probably why C++ introduced const_cast<> :)

Not a problem

Anonymous (not verified)
on
January 22, 2008 - 2:11am

... another problem is demonstrated by char *strchr(const char *s, int c); : strchr("hello", 'h') returns a non-const-pointer to read-only memory

That's just because you passed it read-only memory. strchr returning char * is the correct thing to do. The const in the function argument list is just a promise by strchr that it will not modify what s points at, nothing more.

Man

Anonymous (not verified)
on
January 18, 2008 - 8:29am

Linus sure does come off rather dogmatic there. Is he on the threshold of burning out again or is this just letting off steam? Saying that something has "*no* meaning" is rather strong, and some of these comments could be taken as "const is teh worthlessz0r, ha ha, C sux".

Could it be, then, that Linus is one of those people who fights the compiler at every turn? Despite his twenty years of experience?

More on topic, there's quite a good reason for why kfree() would take "const void *" as parameter: if one wants to free pointers to const stuff, then this saves a couple of casts. However this convention runs contrary to the idea that const things should not be modified _or_ _invalidated_ through const pointers. Given how much data lifecycle hackery exists in Linux, I would not be surprised that quite sane conventions such as this would be left aside for a little bit more performance.

wise words

strcmp
on
January 18, 2008 - 9:06am

as someone who has maintained an in-house malloc()/free() implementation before i can only say that his words about memory managers are true and wise. const has to lose its meaning inside the infrastructure. the memory manager just has to guarantee that no two allocated blocks overlap each other, apart from that it has to work with the pointers in an abstract way and see them just as offsets into a global memory pool where all memory is equal. the correct boundary for 'const has a meaning outside, has no meaning inside' is indeed the actual call to the malloc()/free() functions. as you can't (or "shouldn't", as in 'preprocessor trickery') declare a function that has const arguments everywhere but in its actual implementation, you have to cast the const away in the function body. nothing special here.

apart from that, if you have char const*a; char *b;, initialized to some appropriate values (e.g. char const*a = "hello"; char *b = NULL;), you can always modify a's contents by accessing b[a-b], no compiler enforced guarantee here, you might get a segmentation fault at runtime, though.

The difference here seems to

Anonymous (not verified)
on
January 18, 2008 - 5:15pm

The difference here seems to be that Torvalds (and apparently you) doesn't regard invalidation as equal to modification. Conversely storing known values into a previously uninitialized chunk of memory makes that bit of memory's contents known, i.e. validates that memory, and that is certainly modification.

It's a matter of convention, see? When a new reference-counted object is created, it is returned with exactly one reference, that's also a convention. The convention with regard to a free() call used by Linux is just weirder than common sense when C is used as an application programming language.

invalidation as equal to modification

strcmp
on
January 19, 2008 - 8:53am

i see invalidation as 'you are not allowed to use that address range any more', and if you must not use a chunk of memory, it does not matter if the contents are constant or not. the chunk gets valid when you malloc() it. in the time before malloc and after free the address range belongs to other people and is taboo for you. nobody would declare the uninitialized memory returned by malloc() as const without initializing it first, but you could do that. in terms of reference counting: malloc() returns an object with exactly one reference (and the count cannot be increased) and free() removes this reference. after the last reference got abandoned it doesn't matter if the memory is const or not (it just lives on in another world where this words have no meaning...), so you may as well pass const memory to free(), because the memory _may_ be const and then just for the time of the actual function call it is more correct and saves you from cluttering your code and your brain (on understanding the code) with superfluous casts.

if you want the memory manager itself to treat const memory as constant, you effectively forbid dynamic memory allocation for const objects or declaration of dynamic objects as constant, which serves no one but your principles.

your concept of validation/invalidation seems to be taken from another context (code verification? caching strategies?), not memory management. if you write to a memory cell, the cache line is yours and should be valid (but dirty) and the cell itself is initialized, which makes the program deterministic, if it solely depends on the value of this cell; but in terms of memory management you may have committed random corruption, if you didn't own this memory in the first place.

exactly

Daniael (not verified)
on
January 19, 2008 - 3:09pm

int main()
{

/* 3 itzie bitzie declarations */
const char* frequent; /* char is unmodifiable - const qualifier refers to what is pointed at */
char* const rare; /* pointer itself is unmodifiable - const qualifier refers to pointer itself */
const char* const infrequent; /* cannot change anything - const qualifier refers to both */

/*** now when these 3 const qualifier semantics are arguments passed to functions, things get
a little bit more subtle, mostly because in fact, C, can ONLY pass parameters by VALUE!
so, when you pass a pointer (address of something), the callee makes a copy of that
address and operates on the copy! so, the only two qualifiers which actually apply
are frequent (function cannot change the content of what is pointed at) and infrequent
which is equvalent with frequent, with som degree of dumb, semantic redundancy...
and, of course, if want to change the value of the pointer itself, you cannot do it with C,
unless you make the address the value of another pointer (double start or indirection)
that's why, to insert elements in lists (e.g.) requires **head params, etc
if you have certain difficulties of understanding what this is all about, try to use
fortran, quick basic, c#, etc ;-) it's better not to use at all what you "kinda" understand!
*/

/*** now (again) when in subsystems like memory managers the job is to dynamically manage
memory, well, you cannot really count on any kind of const-ness what so ever and this is
what Linux said, trying to be warn pathetic dreamers about what this is all about
*/

return 0^0;

}

splitting hairs...

Daniael (not verified)
on
January 19, 2008 - 5:07pm

this whole thing about what const should mean in terms of invalidation, etc is exactly like saying:

i am debugging an instance of a class. so, give the "holly" rules of c++, i should not have access to the private section of that class, no matter what, right? so, if my debugger (e.g. gdb) allows me to inspect (or even worst, change the value of) a private data member then gdb must be violating the c++ standard and it should be removed from the system (blaspheming the letter of the law is not allowed, right)... :-) LOL!

what's the big deal to understand that certain rules have practical semantics for the ones they have been designed for... infrastructure (rule-enforcement) needs to go beyond the rules in order to enforce them...

and, having to cast perfectly acceptable things make the casting unnecessary and annoying (it's like having to keep the garbage in your living room, because otherwise you would not be a good citizen, in terms of global worming :-)

Linu"x" is right!

Daniael

The point is, all free or

Anonymous (not verified)
on
January 21, 2008 - 4:24am

The point is, all free or kfree does - considering only the interface and not the implementation (which can vary greatly) is invalidating the pointer. It does not modify the thing the pointer points to. Of course, most free() implementations do modify the memory somehow, but that needs not concern the caller.

There is a simple contract between caller and free -> Caller gives free a pointer and free marks it "invalid".
That's all. There could be a free() implementation that uses reference counting and so the free call doesn't really modify anything but a counter.

For example: if the pointer points to ROM, free may choose to do nothing at all.

*Never ever confuse contracts with implementation*

IIRC, C99 defines some

Jose_X (not verified)
on
January 20, 2008 - 1:47am

IIRC, C99 defines some library function semantics along the lines of what Linus stated for kfree(): that the pointer is no longer valid upon return (eg, free); however, kfree is not part of C99. ..but you can always legally "break" C99 semantics if you enclose the section in #ifdef/etc.

I haven't looked at the standard in a while or at the relevant kernel code, but it's possible that uses of kfree should be blocked off that way (eg, #ifdef KERNEL).

Anyway, keep in mind that the kernel is very special C code. Many things that C99 specifies (and which help portability) would be violated by the code used to build something like a kernel (unless they were properly blocked off). Anyway, it may be that if you block kfree() definition/declaration you would have to do it in all such cases. I don't know how gcc should behave in those cases.

And who says that the kernel code absolutely needs to follow C99, anyway? [something deep down :) ]

assert.h broken again?

Daniael // (not verified)
on
January 20, 2008 - 5:45am

nah, just kidding :)

few early morning 0xD/*eep*/D/*own*/ public asserts:

firmware == test case for ko(s) /* manufacturers insist on adding to their proprietary gizmos */
user land == excellent test case for ___THE ___kernel
C89/99/09/19/x9/etc == ansi test cases for gcc __extensions /* actually very deep :) */
C++ == VMT centric test case for C // with BCPL style comments and fixed ideas about encapsulation
STL == standard evidence that encapsulation decapsulates algorithms from data structs /* C */

have a nice day
d

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.