Re: GCC Standard Predefined Macros
Re: GCC Standard Predefined Macros
- Subject: Re: GCC Standard Predefined Macros
- From: John Engelhart <email@hidden>
- Date: Sat, 8 Dec 2007 07:58:49 -0500
On Dec 7, 2007, at 11:52 AM, John Stiles wrote:
You are passing a const char* to a function which takes a char*.
This is true.
This was never legal C code.
The statement 'This was never legal C code,' however, is not correct.
It is perfectly legal C code. One can reason that if it were not
legal C code, it would generate an error, not a warning. From various
references:
Section 6.7.3.5 of the current C99 standard (WG14/N1256 ISO/IEC
9899:TC3):
"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."
page 187, K&R C Programming Language, 2nd/ANSI ed:
"A const object may be initialized, but not thereafter assigned
to. ... The purpose of const is to announce objects that may be placed
in read-only memory, and perhaps to increase opportunities for
optimization. ... Except that it should diagnose explicit attempts to
change const objects, a compiler may ignore these qualifiers."
See also 'Expert C Programing - Deep C Secrets' by Peter van der
Linden. This is an excellent book on the more esoteric points of the C
language. The book offers the following 'Handy Heuristic':
"The keyword const doesn't turn a variable into a constant! A symbol
with the const qualifier merely means that the symbol cannot be used
for assignment. This makes the value read-only through that symbol; it
does not prevent the value from being modified through some other
means internal (or even external) to the program. ... As Ken Thompson
pointed out, "The const keyword only confuses library interfaces with
the hope of catching some rare errors." In retrospect, the const
keyword would have been better named readonly."
A function with a prototype foo(const char *bar) says "bar is a
pointer to a char type that is read only." The prototype makes it
explicit that the function will not modify the characters at the
address pointed to by bar. Without the const qualifier, the foo()
function may modify the characters at the address bar, but is in no
way required to modify them.
As section 6.7.3.5 says, the behavior is undefined in the case where a
const qualified type somehow becomes an lvalue, such as in the case of
the original example:
int foo(char *bar);
{
const char *constp = "read-only";
foo(constp); // Warning: assignment discards qualifiers from
pointer target type
// This means foo may not honor the expected read-only
behavior,
// but ultimately this is not an error if foo does
not, in fact,
// modify the characters pointed to by constp.
}
int foo(char *bar) { bar[0] = 0; /* Undefined when called with constp,
and no warning or error given. */ }
compared to:
int foo(char *bar) { return(bar[0] == 0 ? 0 : 1); /* constp is not
modified, and the assignment warning does not indicate an actual
problem. */ }
However, the following would be an error:
int foo(const char *bar);
{
const char *constp = "read-only";
foo(constp); // No warning given.
}
int foo(const char *bar) { bar[0] = 0; /* Error: assignment of read-
only location */ }
Naturally, one can pass types that are not const qualified (read-
write) to functions where the prototype declares they will be treated
as const (read-only).
If one investigates the warning and is assured that basename(char
*name) (can|does|will) not modify the characters pointed to by the
argument name (which in practice is more than likely and is almost
always just a harmless warning, as Ken Thompson eludes to), one can
suppress the warning message by casting to (char *), such as:
char *file = basename((char *)__FILE__);
What's interesting in Richards example though, and the 'simplified'
part obviously plays a big part in this, is __FILE__ is a pre-
processor macro which will be re-written as "SOURCE_FILE" when the
compiler proper finally sees it. A string literal such as that is not
const qualified, and is perfectly legal to write to. This is also the
reason why the compiler can't coalesce identical string literals in to
just one entry in the object file by default.
Then there's the less often seen:
char *file = basename(char * const name);
Which qualifies the argument name as const, but not the type pointed
to be name. Thus, name[0] = 0 is fine, but name++ is not. The more
commonly seen const char *name qualifies the type pointed by name is
const, making name++ legal and name[0] = 0 an error. And lets not
forget const char * const name, making it illegal to modify the symbol
name or what it points to.
strcpy __FILE__ into your own string before passing it into basename.
This would be required iff, after investigating the warning, one
determines that basename (or any functions that it calls) is in fact
modifying a const qualified type. In practice, this is rarely the
case. Another potential false warning is when a pointer to a non-
const qualified type is tainted and 'promoted' in some way to const,
such as through an assignment to a const qualified symbol, or via a
function prototype.
Sorry for the long post. The usage of 'const' is just one of the
lesser traveled paths in the C language, and this post is hopefully
educational to those who've not had the.. privlige? of getting to know
'const'. Peter van der Linden gives several pages to the topic, and
his Expert C Programming book comes highly recommended. It covers
many topics that I haven't seen covered else where, and is a very easy
and enjoyable read.
And 'const' is a piece of cake compared to 'restrict', believe it or
not. :(_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden