mikeash.com pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html commentshttp://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsmikeash.com Recent CommentsThu, 28 Mar 2024 21:18:54 GMTPyRSS2Gen-1.0.0http://blogs.law.harvard.edu/tech/rssmikeash - 2009-09-17 23:48:20http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsAnd of course I screw up right in a critical portion. Where I wrote this: <br /> <br />"Here you have a const object but no const objects anywhere in sight." <br /> <br />This should of course say: <br /> <br />"Here you have a const pointer but no const objects anywhere in sight." <br /> <br />I hope this does not impede understanding of my larger point.8411f49def9e796e7929c4913e61681eThu, 17 Sep 2009 23:48:20 GMTmikeash - 2009-09-17 23:47:23http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsOnce again you confuse const objects with const types. The passage you quote is talking about const <i>objects</i>. It is disallowed to modify a const <i>object</i> at runtime. A pointer to a const type is completely unrelated to const objects, though, except for the fact that if you take an address of a const object, the type of the result is a pointer to a const type. <br /> <br />Right from your quote, emphasis added: "...assuming const-qualified <i>objects</i> are never modified." This means data qualified with const. It does <i>not</i> mean non-const objects pointed to by a const pointer. Your passage does not support this idea at all. <br /> <br />To illustrate the difference, consider this code snippet: <br /> <br /><code>int x; <br />const int *y = &amp;x;</code> <br /> <br />Here you have a const object but <i>no const objects anywhere in sight</i>. If these are the only variables you declare in your program, then your program contains no const objects. <br /> <br />Now, consider this: <br /> <br /><code>const int x; <br />int *y = (int *)&amp;x;</code> <br /> <br />Now you have a const object, but no const pointer. This is perfectly legal as long as you don't use the pointer to modify the const object. <br /> <br />In short, and pardon the repetition but it seems that you need it: anything that talks about const <i>objects</i> is completely irrelevant when discussing the sementics of const <i>pointers</i>. <br /> <br />In particular, regarding re-reading through a const pointer when the underlying value is modified, I quote the C standard, section 6.7.3, paragraph 3: <br /> <br /><div class="blogcommentquote"><div class="blogcommentquoteinner">The properties associated with qualified types are meaningful only for expressions that are lvalues.</div></div> <br /> <br />In other words, reading from a const value is just like reading from a regular value. As always, the "as if" rule is king: the compiler does not have to read a const value, but only if the behavior is guaranteed to be "as if" it had. If the object where the value is being read from is not in fact a const object, and it could have been modified, then the compiler must re-read it. In other words, this code must always print "12": <br /> <br /><code>int x = 1; <br />const int *y = &amp;x; <br />printf("%d", *y); <br />x = 2; <br />printf("%d", *y);</code> <br /> <br />If you still think I'm wrong, feel free to find the portion of the standard which says so. There's a draft of C99 online here: <br /> <br /><a href="http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336.pdf">http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1336.pdf</a> <br /> <br />And my understanding is that there are no substantial differences between that and the final one. <br /> <br />As for your code example, I'm afraid I don't see the point of it. Its output is exactly what I would expect. Perhaps I misunderstood the purpose of it. Could you explain exactly how this code sample illustrates your point, as far as what output it should be giving if I were correct, and how your position causes it to print the output that it does?4e7a91d7c03d7259c3615ce14c4eb54cThu, 17 Sep 2009 23:47:23 GMTjohne - 2009-09-17 22:14:41http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsMike, <br /> <br />From "The New C Standard" (re: const): <br /> <br /><div class="blogcommentquote"><div class="blogcommentquoteinner">However, a translator is not required to perform any checks at translation or execution time to ensure that the object is not modified (via some pointer to it). <br /> <br />A translator may reduce optimization overhead by assuming const-qualified objects are never modified. This could result in values held in registers being reused, after the object held in storage had been modified.</div></div> <br /> <br />I'm sorry to say that you're wrong. The compiler has no obligation to "re-read" the values like you claim. The statement "const int *xp;" has the exact same semantics as "const int x;", and the compiler is free to treat it as such. The fact that the values are access via a pointer changes nothing- the type for "const int *xp;" is 'pointer', and the type that it points to is 'const qualified int', which is exactly the same as 'const int x;'. The best way to demonstrate this is with code: <br /> <br />// Based on <a href="http://www.compileroptimizations.com/category/alias_optimization_const_qualifier.htm">http://www.compileroptimizations.com/category/alias_optimization_const_qualifier.htm</a> <br /> <br />const int *const_array; <br /> <br />void g(const int *x, const int *y) { printf("g: x: %d, y: %d\n", *x, *y); } <br /> <br />void f (int *p, int i) { <br />&nbsp;&nbsp;int x=0, y=0; <br />&nbsp;&nbsp;const int *q = &amp;const_array[i]; <br />&nbsp;&nbsp;x = *q; <br />&nbsp;&nbsp;*p = 9; <br />&nbsp;&nbsp;g (&amp;x, q); <br />&nbsp;&nbsp;y = *q; <br />&nbsp;&nbsp;g (&amp;x, &amp;y); <br />} <br /> <br />int main(int argc, char *argv[]) { <br />&nbsp;&nbsp;int x1 = 123, i, int_array[] = {1,2,3}; <br />&nbsp;&nbsp;const_array = int_array; <br /> <br />&nbsp;&nbsp;printf("x1: %4d, const_array: %d, %d, %d\n", x1, const_array[0], const_array[1], const_array[2]); <br />&nbsp;&nbsp;f(&amp;x1, 0); <br />&nbsp;&nbsp;printf("x1: %4d, const_array: %d, %d, %d\n", x1, const_array[0], const_array[1], const_array[2]); <br />&nbsp;&nbsp;f(&amp;const_array[0], 1); <br />&nbsp;&nbsp;printf("x1: %4d, const_array: %d, %d, %d\n", x1, const_array[0], const_array[1], const_array[2]); <br /> <br />&nbsp;&nbsp;return(0); <br />} <br /> <br />outputs: <br /> <br />x1: 123, const_array: 1, 2, 3 <br />g: x: 1, y: 1 <br />g: x: 1, y: 1 <br />x1: 9, const_array: 1, 2, 3 <br />g: x: 2, y: 2 <br />g: x: 2, y: 2 <br />x1: 9, const_array: 9, 2, 3 <br />c0d0051b0931e44ef7e115e7f384e84fThu, 17 Sep 2009 22:14:41 GMTmikeash - 2009-09-17 17:10:43http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#comments<b>johne:</b> Well, you're confused about several things here.... <br /> <br />Your statement that the compiler is free to put const data in read-only sections is completely true, but also completely irrelevant. The declaration <code>const NSString *immutableNSStringPointer;</code> does not create or otherwise involve const data. There is a <i>huge</i> difference between a const object and a pointer to a const type. Your stuff about read-only memory is completely unrelated to a pointer to a const type. <br /> <br />Likewise your stuff about optimizations. The comiler <i>cannot</i> and <i>will not</i> assume that the data pointed to by a pointer to a const type is unchanging. By way of illustration: <br /> <br /><code>const int x = ...; <br />printf("%d\n", x); <br />const int *xp = &amp;x; <br />SomeFunction(xp); <br />printf("%d\n", x);</code> <br /> <br />Here the compiler is free to assume that the value of <code>x</code> could not be changed by the call to <code>SomeFunction</code>. Now consider this: <br /> <br /><code>const int *xp = ...; <br />printf("%d\n", *xp);</code> <br />SomeFunction(xp); <br />printf("%d\n", *xp); <br /> <br />Here the compiler <i>cannot assume</i> that <code>*xp</code> is the same value that it was before. It must re-read it. The <code>const</code> in this case does not have anything like the same semantics that it does in the first case. <br /> <br />As for "semantic contents", it means that an immutable NSString which contains any given unichar sequence, as queried through its public API, will always contain that same unichar sequence for the lifetime of the object. As for the rest of that paragraph, well, I wasn't talking about <code>immutableNSStringPointer</code> so it's irrelevant once again. <br /> <br />And as for "useless warnings", once again you misunderstand. They are useless because the purpose of declaring a pointer to be to a const type is to warn about mutations, but in this case the type of mutation they warn about is not the type of mutation you care about.0ed272033521fd51c326e28dc1010a9dThu, 17 Sep 2009 17:10:43 GMTjohne - 2009-09-17 12:35:19http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#comments<div class="blogcommentquote"><div class="blogcommentquoteinner"> <br />const NSString *immutableNSStringPointer; <br /> <br />What const means here is that you can't use immutableNSStringPointer to modify the memory at that location. <br /></div></div> <br /> <br />This isn't quite correct. What the statement means is "a pointer to a const qualified NSString". A const qualified type is unchanging and immutable. In fact, the compiler is free to place const qualified data in read-only sections, such as MMU enforced read-only RAM, or even ROM. It's not so much that it means that you can't use immutableNSStringPointer to modify the memory at that location, it's that const changes the properties of the memory at that location so that modifying it is non-sensical. <br /> <br />This might seem pedantic, but it does have some important ramifications. For example, the optimizer will often take advantage of the fact that const qualified data can not change. This can lead to subtle, hard to debug bugs if the const qualified data does mutate. <br /> <br /><div class="blogcommentquote"><div class="blogcommentquoteinner"> <br />Nothing says that the memory of an NSString can't be modified, only that the semantic contents of the NSString can't be modified. <br /></div></div> <br /> <br />I don't know what "semantic contents" means in this context, but the first part of the sentence is definitely wrong for immutableNSStringPointer. In fact, the standard explicitly says that modifying "the memory of an NSString", that is to say the memory pointed to by immutableNSStringPointer, will result in undefined behavior. <br /> <br /><div class="blogcommentquote"><div class="blogcommentquoteinner"> <br />More concretely, if you declare a variable like this and then try to use it anywhere, you'll get a huge number of useless warnings about violating the constness of your variable. <br /></div></div> <br /> <br />The warnings aren't useless, they're letting you know that something is wrong. In this case it is probably because you're passing a const qualified variable as an argument where the declared argument type is not similarly const qualified. This can potentially cause problems in optimized code if the function or method modifies the pointed to memory, for example. These warnings are almost always worth investigating and correcting (if possible, sometimes the problem is out of your control, like CFString/NSString toll-free bridging).be5c8ec0966705deb04de546768856dfThu, 17 Sep 2009 12:35:19 GMTJohn Gallagher - 2009-07-12 19:16:45http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsMike <br /> <br />Thanks a lot for your explanation. Very helpful. Loving the Q and A series - as a newbie Cocoa guy, it's really useful. <br /> <br />John95dda9ff03328174c38f1143d743003eSun, 12 Jul 2009 19:16:45 GMTmikeash - 2009-07-12 11:29:56http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#comments<code>const char *</code> and <code>char const *</code> are not "pretty much the same thing". They are <i>identical</i>. They produce an identical type on the variable when compiled. The C language does not care about the ordering of type qualifiers, storage specifiers, and types when they're intermixed like this. <br /> <br />As for pointer constness versus pointed-to constness, I covered that in a more specific fashion in the section that talks about how to make constant NSStrings.c7424593fe7e283d41728a71f287fddfSun, 12 Jul 2009 11:29:56 GMTslf - 2009-07-12 11:04:56http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsI'm surprised go didn't talk about the 'double const' ie: char const* const <br /> <br />const char* and char const* are pretty much the same thing: they allow you a pointer to a collection of characters that will change. Doing a char const* const means that not only is the data constant, but the pointer is constant as well. <br /> <br />In summary, just because the data can't change, doesn't mean the pointer can't. To make that const, you need a second one. Unfortunately I am not as good at explaining things a you are, so I would be interested in reading your view. Thanks for the great blog!2baa1fd0b0cbb77149bda557a8b18ea5Sun, 12 Jul 2009 11:04:56 GMTmikeash - 2009-07-05 10:12:29http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsI imagine there are many good sites which explain extern. Let me give you the quick version, though. <br /> <br />In essence, an extern variable declaration is to a regular one what a function prototype is to an actual function. It tells the compiler that this variable exists, but does not actually make it exist. It only makes sense on globals. <br /> <br />For example, both of these declare the existence of a symbol, but do not cause that symbol to be created: <br /> <br /><code>extern int globalVariable; <br />void function(void);</code> <br /> <br />And then these actually create the symbols above: <br /> <br /><code>int globalVariable; <br />void function(void) {}</code> <br /> <br />And just like functions and function prototypes, you should put the extern version in the header, and the non-extern version in the implementation.25bdced03cdae5c252e414303f9a3037Sun, 05 Jul 2009 10:12:29 GMTJohn Gallagher - 2009-07-05 09:00:00http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsMike <br /> <br />Thankyou so much for an incredibly useful article. I'm too tired to fully digest it right now, but I've been hunting for a proper detailed article on this for ages, being a complete Cocoa newbie and seeing const everywhere, and using it whilst not really understanding what I was doing. <br /> <br />One thing I don't see mentioned here is the extern keyword. Have you covered this elsewhere, or is there another Q and A to be had on the extern keyword and what this is? <br /> <br />John13c8f7e2898bf4f5294648cf0f527a9eSun, 05 Jul 2009 09:00:00 GMTmikeash - 2009-07-04 00:37:59http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsThe #define simply substitutes the literal string "42" anywhere you write "kMeaning". The other one defines an actual variable, one you're not allowed to modify. As for practical differences, Ahruman covered one big one: the const int is not actually considered to be a constant expression by the compiler, which influences where you can use it. Another one is that it is legal to take the address of the const int, but obviously not of the #define (&amp;42 is not a legal expression).ae9c5ee9491632aa33144e587a00016cSat, 04 Jul 2009 00:37:59 GMTsam - 2009-07-03 23:36:12http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsIn global scope what's the difference between cont int kMeaning = 42; and #define kMeaning 42 ?00c1ba604ca43d8a295d51f0dd20d8dfFri, 03 Jul 2009 23:36:12 GMTmikeash - 2009-07-03 22:56:03http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsThat's correct. Leading qualifiers, specifiers, and types can be mixed up however you like. static volatile const int whatever; is the same as int volatile static const whatever;.1a3025c68baed1eb22b2bc97b7b8d176Fri, 03 Jul 2009 22:56:03 GMTdz - 2009-07-03 22:25:16http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsAnd there is no difference between <br /> <br />const int kMeaning = 42; <br /> <br />and <br /> <br />int const kMeaning = 42; <br /> <br />, right?307448edfb122576e9f795dcdf1444c5Fri, 03 Jul 2009 22:25:16 GMTmikeash - 2009-06-28 07:47:45http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsI'd argue that it does give you a constant, in that it's a value which cannot be (legally) changed. What it does not do is give you a constant <i>expression</i> when you use the thing. This is peculiar, and annoying, and takes away from some of the utility of this approach, but I still think it's right to call the thing a "constant". This is, of course, hair-splitting. <br /> <br />As for non-ASCII comments, I spent a long time fighting Python and MySQL and finally decided that it was simply impossible to make them agree on anything character-set-wise. Someday when I redo the blog system in SQLite it will all work, but until them, well, I'm stuck with ASCII.05099c762f68880e1251ea97f33f7adeSun, 28 Jun 2009 07:47:45 GMTAhruman - 2009-06-28 07:23:50http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsOh, and your comment form crashes if it's fed non-ASCII. :-)5d48264e520515c4e0b35a96198c4b9dSun, 28 Jun 2009 07:23:50 GMTAhruman - 2009-06-28 07:23:17http://www.mikeash.com/?page=pyblog/friday-qa-2009-06-26-type-qualifiers-in-c-part-1.html#commentsconst int kMeaning = 42; <i>does not</i> declare a constant in C. It declares (and defines) a variable which is read-only. (In C++, a const variable is a true constant if its definition is visible.) <br /> <br />The distinction can be seen in code such as the following (in global scope): <br />const int kMeaning = 42; <br />char buffer[kMeaning]; <br />const int kDerivedConstant = kMeaning + 1; <br /> <br />error: variable-size type declared outside of any function <br />error: initializer element is not constant <br /> <br />This is why enums are commonly used for integer constants which are not actually used as enumerants - they are actual constant expressions which can be used in calculations.d6f061a32defb7d01d2343e99bd36823Sun, 28 Jun 2009 07:23:17 GMT