casting const away

J

James Kuyper

[...]
It was only labeled as a warning; it was not a mandatory diagnostic. A
compiler can choose to warn you about anything it wants. It could warn
you not to compile programs on Friday the 13th.

But nobody does, except for an Easter egg.
Why is that? Is it because they want to
stay in the compiler writing business?
The fact that a warning
was issued is not relevant to the question of whether or not this code
has defined behavior.

It is a question---a question that two people evaded.
Why have the warning at all?

Because it's risky code. It's not undefined in this case, but the safety
of this code is fragile, and in many cases cannot be checked at compile
time - what if foo() were called with the address of an object that WAS
declared const, in some other translation unit? The problem caused by
such a call would be detectable no earlier than link time, and many C
compilers would be unable to determine whether or not it was safe.
Giving a warning is a lot easier than tracking down whether the code was
actually safe.
 
M

Michael Press

Ian Collins said:
Undefined behaviour or not, the warning is still valuable. Consider a
slight variation on the original example:

#include <stdio.h>

static const int a = 'A';

void foo(const int *c)
{
*((int *)c) = 'B';
}

int main(void)
{
foo(&a);
putchar(a);
return 0;
}

Running this on many systems will cause a crash. is that well defined
behaviour?

You jumped into the middle of this sub-thread without
reading or comprehending what I have been saying and
asking. Please review that material, and reply as
necessary.
 
I

Ian Collins

You jumped into the middle of this sub-thread without
reading or comprehending what I have been saying and
asking. Please review that material, and reply as
necessary.

Jumping into the middle of a sub-thread I started, um, interesting
concept! I suggest you follow your own advice.
 
M

Michael Press

James Kuyper said:
[...]
For another thing you excised the compiler warning.

It was only labeled as a warning; it was not a mandatory diagnostic. A
compiler can choose to warn you about anything it wants. It could warn
you not to compile programs on Friday the 13th.

But nobody does, except for an Easter egg.
Why is that? Is it because they want to
stay in the compiler writing business?
The fact that a warning
was issued is not relevant to the question of whether or not this code
has defined behavior.

It is a question---a question that two people evaded.
Why have the warning at all?

Because it's risky code.

Yes, I implied that is my view all along.
It's not undefined in this case, but the safety
of this code is fragile, and in many cases cannot be checked at compile
time - what if foo() were called with the address of an object that WAS
declared const, in some other translation unit? The problem caused by
such a call would be detectable no earlier than link time, and many C
compilers would be unable to determine whether or not it was safe.
Giving a warning is a lot easier than tracking down whether the code was
actually safe.

Did you notice that the warning does not arise with
cc -Wall -Wextra -pedantic? Do writers of production
code typically turn on -Wcast-qual? It appears to me
that while the code is just plain wrong, it is accepted
in practice; that is what I gather from what is being
said in this thread.

I need to reiterate something I said earlier. When I
call foo(const int *) I expect the compiler to tag
the writer of foo for abrogating his agreement with me.

The following code gets an _error_
int main(void)
{
const int n = 10;
n = 9;
return 0;
}

but casting away const only gets a warning if the
code writer goes out of his way to turn on the warning.
 
A

Alan Curry

Did you notice that the warning does not arise with
cc -Wall -Wextra -pedantic? Do writers of production
code typically turn on -Wcast-qual? It appears to me

I always compile with those warnings (and more) enabled, even when
compiling other people's code that wasn't written with them in mind, and
the warning about casting away const appears rarely enough that I
consider it worthwhile to look at the code and see if there's a
legitimate problem.

Far more frequent is "comparison between signed and unsigned", which is
included in -Wextra, and almost never turns out to be an actual bug.

And then there's -Wshadow. People, please stop putting
"struct sockaddr_in sin;" and "#include <math.h>" in the same file. It's
annoying.
 
J

James Kuyper

Yes, I implied that is my view all along.

That may have been your intent, but you seemed to be weighing in on the
side of those who describe such code as a "lie". I believe that, in the
context of standard C, the only assumptions that an implementation is
allowed to make about the intended meaning of a programmer's code are
those assumptions which could only be violated by code with undefined
behavior. In other words, I don't consider it a lie to the compiler
unless it renders the behavior of the code undefined, which is a much
stronger statement than merely saying that the code is risky.
I need to reiterate something I said earlier. When I
call foo(const int *) I expect the compiler to tag
the writer of foo for abrogating his agreement with me.

If you have an agreement with him that goes beyond the writing of code
with no syntax errors, no constraint violations, and no undefined
behavior, the details of that agreement are a matter for negotiation.
The compiler cannot reasonably be expected to know the terms of that
agreement. it's only responsible for diagnosing syntax errors and
constraint violations; this is neither.
The following code gets an _error_
int main(void)
{
const int n = 10;
n = 9;
return 0;
}

but casting away const only gets a warning if the
code writer goes out of his way to turn on the warning.

As a general rule, safe conversions can be made to occur automatically,
without a cast; conversions that must be specifically requested by use
of a cast-expression are the dangerous ones. The use of a cast should
always be taken as a sign the code it occurs in should be reviewed with
more than the usual amount of caution. When you use a cast expression,
you are, in effect, telling the compiler "I know what I'm doing, don't
bother me about this."

Using -cast-qual essentially says "I was lying when I said I know what
I'm doing - please remind me of the cases where you think I might have
been mistaken". If you're sloppy about your use of casts, that can be an
important piece of information to give to the compiler.
 
A

Alan Curry

Using -cast-qual essentially says "I was lying when I said I know what
I'm doing - please remind me of the cases where you think I might have
been mistaken". If you're sloppy about your use of casts, that can be an
important piece of information to give to the compiler.

Or it means "I'm not the author of the code I'm compiling, and I suspect the
author used casts too much".

Or you want a reminder that a cast from "int *" to "unsigned char *" needs to
be updated when the "int *" variable later gets changed to "const int *" when
the function gains the ability to work on readonly data.

Never mind how the cast-away-const gets into the code. Ask yourself why you'd
ever want one to stay there.
 
M

Michael Press

Ian Collins said:
Jumping into the middle of a sub-thread I started, um, interesting
concept! I suggest you follow your own advice.

I am destined to be followed around by Ian Collins
repeating back to me what I say.
 
T

Tim Rentsch

Kelsey Bjarnason said:
[snips]

AIUI, the compiler is free to believe what it's told - that the
parameter is, indeed, const and thus won't be modified. [snip
elaboration]
This understanding is mistaken.

On the one hand, you may well be right. On the other, in 20-odd years of
C coding, I don't think I've ever found it necessary to lie to the
compiler this way. On the third hand, I'd tend to suggest any code which
uses such constructs needs to be re-thought,

My comment was about what the Standard allows and/or requires,
not about stylistic choices or "good coding" practices.
rather than diddling over
relatively obscure portions of the standard - and hoping the implementor
got the intention spot-on. :)

Obscure? Hardly. Unknown to you, perhaps, but certainly not
to implementors -- any implementation that got this point wrong
would soon be laughed at and then avoided like the plague.
 
T

Tim Rentsch

Michael Press said:
Tim Rentsch said:
[...]
That wasn't how I read it.

It would help if you could be a little more explicit in
saying just what interpretation you believe is actually
the case. I will start the ball rolling. The program
copx posted:

#include <stdio.h>

void foo(const int *c)
{
*((int *)c) = 'B';
}


int main(void)
{
int a = 'A';
foo(&a);
putchar(a);
return 0;
}

does _not_ have any undefined behavior. Do you dispute
that? If so, please cite those sections of the Standard
that support your view.

That code is in some kind of difficulty.
Suppose foo's caller wants and expects the value retained (*&a)?

$ cc -Wcast-qual try.c
try.c: In function 'foo':
try.c:5: warning: cast discards qualifiers from pointer target type

$ cat -n try.c
1 #include <stdio.h>
2
3 void foo(const int *c)
4 {
5 *((int *)c) = 'B';
6 }
7
8
9 int main(void)
10 {
11 int a = 'A';
12 foo(&a);
13 putchar(a);
14 return 0;
15 }

It seems you are confusing several different kinds of
circumstances, more specifically,

1. undefined behavior
2. constraint violations
3. warning messages
4. style-violation advisories

The presence of a warning message or a style-violation advisory
message does not necessarily imply either a constraint violation or
undefined behavior. Despite the gcc-produced warning message (and
whatever style rules one might think are violated) the code above
_must change_ the value of the variable 'a' as part of the function
call 'foo(&a)'; any other result means the implementation is not
conforming.

Stylistically I might agree that the code above is bad, but
my earlier comment was only about whether it is well-defined
(which it is, not counting the missing final newline on output).
If someone wants to change the topic to discuss whether this
code is bad style, may I suggest starting a new thread for
that topic?
 
K

Kelsey Bjarnason

[snips]

Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify - and just why would
an implementer, regarding such bone-headed code as positively dangerous
rather than something to be encouraged, be laughed at for protecting
innocents from boneheaded coders doing such insane things?

Either way, you'll excuse me if I fervently hope I never have to work on
code derived from your efforts; it obviously is not possible to trust any
aspect of it, as whatever contracts it may make can't be trusted to be
honoured, right?

Laughing at implementers who treat boneheaded nonsense code *as*
boneheaded nonsense code. Yeesh.
 
K

Keith Thompson

Kelsey Bjarnason said:
[snips]

Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify - and just why would
an implementer, regarding such bone-headed code as positively dangerous
rather than something to be encouraged, be laughed at for protecting
innocents from boneheaded coders doing such insane things?

Either way, you'll excuse me if I fervently hope I never have to work on
code derived from your efforts; it obviously is not possible to trust any
aspect of it, as whatever contracts it may make can't be trusted to be
honoured, right?

Laughing at implementers who treat boneheaded nonsense code *as*
boneheaded nonsense code. Yeesh.

No, criticizing (hypothetical) implementers who enforce their own
ideas about boneheaded code rather than following the explicit
requirements of the language standard.

It's easy to write horrendously bad code that is nevertheless
conforming, or even strictly conforming, with its behavior rigorously
defined by the standard. I wouldn't mind if my compiler warned me
about such code, but it's not permitted to reject it (at least not
if it claims to be conforming).

And just because someone says that compilers should accept a given
piece of code, that doesn't imply that he'd *write* such code.
 
B

Ben Pfaff

Kelsey Bjarnason said:
Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify - [...]

I write such code, not often but regularly. It's a common enough
practice that C++ invented a whole new keyword exactly for this
purpose.
 
I

Ian Collins

Kelsey Bjarnason said:
Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify - [...]

I write such code, not often but regularly. It's a common enough
practice that C++ invented a whole new keyword exactly for this
purpose.

I assume you are referring to mutable?

If so, comparing the use of mutable in a const member function to
casting away const is an apples to oranges comparison.
 
B

Ben Pfaff

Ian Collins said:
Kelsey Bjarnason said:
On Thu, 27 Jan 2011 15:50:25 -0800, Tim Rentsch wrote:

Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify - [...]

I write such code, not often but regularly. It's a common enough
practice that C++ invented a whole new keyword exactly for this
purpose.

I assume you are referring to mutable?

If so, comparing the use of mutable in a const member function to
casting away const is an apples to oranges comparison.

How so? The "mutable" keyword was invented to avoid the need to
cast away const. If I cast away const in C to modify part of a
structure, in a function whose purpose is to work with that
structure--which is what I do sometimes--it sounds to me like an
apples to apples comparison.

C:

struct foo {
...
int cached_key;
int cached_value;
};

int
lookup(const struct foo *foo, int key)
{
if (key != foo->cached_key) {
struct foo *foo_rw = (struct foo *) foo;

foo_rw->cached_key = key;
foo_rw->cached_value = really_lookup(foo, key);
}
return foo->cached_value;
}

C++ (I don't write C++ much so the details might be wrong:

class foo {
...
mutable int cached_key;
mutable int cached_value;

int really_lookup(int key) const;
public:
int lookup(int key) const;
};

int foo::lookup(int key) const
{
if (key != cached_key) {
cached_key = key;
cached_value = really_lookup(key);
}
return cached_value;
}
 
I

Ian Collins

Ian Collins said:
On Thu, 27 Jan 2011 15:50:25 -0800, Tim Rentsch wrote:

Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify - [...]

I write such code, not often but regularly. It's a common enough
practice that C++ invented a whole new keyword exactly for this
purpose.

I assume you are referring to mutable?

If so, comparing the use of mutable in a const member function to
casting away const is an apples to oranges comparison.

How so? The "mutable" keyword was invented to avoid the need to
cast away const. If I cast away const in C to modify part of a
structure, in a function whose purpose is to work with that
structure--which is what I do sometimes--it sounds to me like an
apples to apples comparison.

Applied that way then yes, it is an apples to apples comparison. I
guess the apples to oranges comparison is between casting away const for
a mutable member of a struct and casting away const for a single object
pointer.
 
T

Tim Rentsch

Kelsey Bjarnason said:
[discussing whether a (const T *) pointer can be used to modify
an object whose identifier is not defined as const-qualified.]

Obscure? Hardly. Unknown to you, perhaps, but certainly not to
implementors -- any implementation that got this point wrong would soon
be laughed at and then avoided like the plague.

Really? Just how often *do* you write code which violates its contract
by modifying the very things it claims to not modify

What you imagine "its contract" to be differs from what the
Standard says it is.
- and just why would
an implementer, regarding such bone-headed code as positively dangerous
rather than something to be encouraged, be laughed at for protecting
innocents from boneheaded coders doing such insane things?

You seem to be confusing what "appears sensible", or "following
good programming style", with adhering to what the Standard
requires of implementations. Implementors know that their job
is to implement the language as the Standard specifies it,
regardless of any personal opinions about "dangerous constructs".

Either way, you'll excuse me if I fervently hope I never have to work on
code derived from your efforts; it obviously is not possible to trust any
aspect of it, as whatever contracts it may make can't be trusted to be
honoured, right?

Just because you confuse good programming style with what the
Standard requires, don't presume that others make the same
mistake. A good programmer understands both.

Laughing at implementers who treat boneheaded nonsense code *as*
boneheaded nonsense code. Yeesh.

This statement is a mischaracterization. It is laughing at
implementors who treat well-defined C constructs as boneheaded
nonsense code. The point of having a Standard is so that all
implementations compile the same language, regardless of any
individual opinion about what is or is not "boneheaded".
 
M

Michael Press

James Kuyper said:
That may have been your intent, but you seemed to be weighing in on the
side of those who describe such code as a "lie".

Thank you for replying at length to my questions.
They are questions; even though I express myself
categorically to make myself plain. I am not taking
sides, and not calling anyone a liar---do not know
enough to do so.
I believe that, in the
context of standard C, the only assumptions that an implementation is
allowed to make about the intended meaning of a programmer's code are
those assumptions which could only be violated by code with undefined
behavior. In other words, I don't consider it a lie to the compiler
unless it renders the behavior of the code undefined, which is a much
stronger statement than merely saying that the code is risky.


If you have an agreement with him that goes beyond the writing of code
with no syntax errors, no constraint violations, and no undefined
behavior, the details of that agreement are a matter for negotiation.
The compiler cannot reasonably be expected to know the terms of that
agreement. it's only responsible for diagnosing syntax errors and
constraint violations; this is neither.


As a general rule, safe conversions can be made to occur automatically,
without a cast; conversions that must be specifically requested by use
of a cast-expression are the dangerous ones. The use of a cast should
always be taken as a sign the code it occurs in should be reviewed with
more than the usual amount of caution. When you use a cast expression,
you are, in effect, telling the compiler "I know what I'm doing, don't
bother me about this."

Using -cast-qual essentially says "I was lying when I said I know what
I'm doing - please remind me of the cases where you think I might have
been mistaken". If you're sloppy about your use of casts, that can be an
important piece of information to give to the compiler.

Again, thank you.
 
M

Michael Press

Tim Rentsch said:
Michael Press said:
Tim Rentsch said:
[...]

That wasn't how I read it.

It would help if you could be a little more explicit in
saying just what interpretation you believe is actually
the case. I will start the ball rolling. The program
copx posted:

#include <stdio.h>

void foo(const int *c)
{
*((int *)c) = 'B';
}


int main(void)
{
int a = 'A';
foo(&a);
putchar(a);
return 0;
}

does _not_ have any undefined behavior. Do you dispute
that? If so, please cite those sections of the Standard
that support your view.

That code is in some kind of difficulty.
Suppose foo's caller wants and expects the value retained (*&a)?

$ cc -Wcast-qual try.c
try.c: In function 'foo':
try.c:5: warning: cast discards qualifiers from pointer target type

$ cat -n try.c
1 #include <stdio.h>
2
3 void foo(const int *c)
4 {
5 *((int *)c) = 'B';
6 }
7
8
9 int main(void)
10 {
11 int a = 'A';
12 foo(&a);
13 putchar(a);
14 return 0;
15 }

It seems you are confusing several different kinds of
circumstances, more specifically,

1. undefined behavior
2. constraint violations
3. warning messages
4. style-violation advisories

The presence of a warning message or a style-violation advisory
message does not necessarily imply either a constraint violation or
undefined behavior. Despite the gcc-produced warning message (and
whatever style rules one might think are violated) the code above
_must change_ the value of the variable 'a' as part of the function
call 'foo(&a)'; any other result means the implementation is not
conforming.

Stylistically I might agree that the code above is bad, but
my earlier comment was only about whether it is well-defined
(which it is, not counting the missing final newline on output).
If someone wants to change the topic to discuss whether this
code is bad style, may I suggest starting a new thread for
that topic?

I dare not start a thread on style.

Thank you for explaining the technicalities,
and helping me think this through.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
474,083
Messages
2,570,591
Members
47,212
Latest member
RobynWiley

Latest Threads

Top