casting const away

C

copx

In a situation like this

void my_func(const FOO_T *);

*foo = 'b';
my_func(foo);
putchar(*foo);

Is the compiler allowed to replace the second foo->a with 'b'
i.e. allowed to assume that my_func does indeed not modify
foo? I mean in C you one cast "const" away within said function.
I wrote a test program who did just that, compiled with GCC
-Wall -Wextra -ansi -pedantic -O3 : not even a warning and
the compiler did not optimize the dereference away..
 
C

copx

"copx" wrote in message news:[email protected]...
[snip]
Is the compiler allowed to replace the second foo->a with 'b'
i.e. allowed to assume that my_func does indeed not modify
foo? I mean in C you one cast "const" away within said function.
I wrote a test program who did just that, compiled with GCC
-Wall -Wextra -ansi -pedantic -O3 : not even a warning and
the compiler did not optimize the dereference away..

Wow, clc isn't what it used to be. In the good ol' days these
group was so full of people who had memorized the C standard
like Grand Ayatollahs the Quran that such a simple standard
question was usually answered within minutes.

Whatever, so I grabbed a draft copy of the standard and
tried to figure it out myself. These documents really aren't
written with end-users in mind, I tell you. However, after
10 minutes or so I arrived at the conclusion that this works
and is guranteed to work because "const" is only really
meaningful when it's applied to the "object" itself. I.e. a const
pointer to something doesn't actually tell the compiler
that the object pointed to is constant, this type of "constness"
only exist to produce compiler warnings which may aid
the programmer. By expliticly casting to a non-const
pointer I get no warning because removing the "const" from
the pointer just means.. telling the compiler not to
produce a warning!
 
I

Ian Collins

In a situation like this

void my_func(const FOO_T *);

*foo = 'b';
my_func(foo);
putchar(*foo);

Is the compiler allowed to replace the second foo->a with 'b'

What second foo->a? You haven't shown a first, let alone a second.
Without code your post does not make sense.
 
C

copx

"Ian Collins" wrote in message

What second foo->a? You haven't shown a first, let alone a second.
Without code your post does not make sense.

Oops, I simplified the code after writing the text below and forgot
to update the text to reflect the change. "Second foo->a" should
"Second *foo".
 
C

copx

"copx" wrote in message
Here's some compile-ready code which illustrates the issue:

#include <stdio.h>

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


int main(void)
{
int a = 'A';

foo(&a);

putchar(a);

return 0;
}


====
Result: no warnings, prints 'B';

As I wrote in the other post I think I have figured it out
already.
 
I

Ike Naar

In a situation like this

void my_func(const FOO_T *);

*foo = 'b';
my_func(foo);
putchar(*foo);

Is the compiler allowed to replace the second foo->a with 'b'
i.e. allowed to assume that my_func does indeed not modify
foo? I mean in C you one cast "const" away within said function.
I wrote a test program who did just that, compiled with GCC
-Wall -Wextra -ansi -pedantic -O3 : not even a warning and
the compiler did not optimize the dereference away..

The code has undefined behaviour.

6.7.3
5 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.

The compiler is not obliged to give a warning for undefined behaviour.
 
L

lawrence.jones

copx said:
Here's some compile-ready code which illustrates the issue:

#include <stdio.h>

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


int main(void)
{
int a = 'A';

foo(&a);
putchar(a);
return 0;
}

====
Result: no warnings, prints 'B';

It might just as well have printed 'A'. You have lied to the compiler
by declaring that foo() does not modify what its argument points to but
going ahead and doing so anyway. The compiler is permitted to believe
the declaration but is not required to, so either result is acceptable.
The code is not strictly conforming, but does not violate any
constraints so no diagnostics are required. It also does not contain
undefined behavior, just unspecified behavior.
 
C

copx

"Ike Naar" wrote in message
The code has undefined behaviour.

6.7.3
5 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.

I read the same section of the standard and this doesn't support
your statement IMO. The standard talks about OBJECTS with
a const-qualified type NOT about const-qualified references to
non-const-qualified objects. I.e. this only means that modifying a
"const int" is undefined, const-qualified references to
non-const-qualified objects aren't mentioned here.

Digging further I could only find this:
==
const int *ptr_to_constant;
...
The contents of anyobject pointed to by ptr_to_constant shall
not be modiï¬ed through that pointer,
==

Then I had to reference an entirely different section of the
standard to figure out the technical definition of "shall not".
=====
If a ‘‘shall’’or‘‘shall not’’requirement that appears outside of
a constraint is violated, the behavior is undeï¬ned.
===

Ok,case finally solved, right? Unfortunately, no!

The standard only says "through that pointer" notice the "that".
I can copy the value of the const pointer to a non-const pointer
and then use this newly created pointer to modify the object thus
the object is never actually modified "through that pointer".

The exact, literal meaning of what the standard says validates the
behavior of GCC IMO, but the wording of the standard is so horribly
confusing here that I wouldn't be surprised if other compilers interpret
this mess in a different way.
 
I

Ian Collins

The standard only says "through that pointer" notice the "that".
I can copy the value of the const pointer to a non-const pointer
and then use this newly created pointer to modify the object thus
the object is never actually modified "through that pointer".

Yes you can, but you have to lie to the compiler (cast) to do so.
The exact, literal meaning of what the standard says validates the
behavior of GCC IMO, but the wording of the standard is so horribly
confusing here that I wouldn't be surprised if other compilers interpret
this mess in a different way.

The wording is clear: once you lie to the compiler you are in the land
of undefined behaviour.
 
S

sandeep

christian.bau said:
Use "const int* restrict" instead:

int i; const int* restrict p = &i; * (int *) p = 1; // Undefined
behaviour

I don't believe that this will be an undefined behavior because p and
(int*)p are both pointers to the same memory address hence there is no
aliasing here.
 
C

copx

"Ian Collins" wrote in message news:[email protected]...
Yes you can, but you have to lie to the compiler (cast) to do so.

Where is concept of "lying to the compiler" defined in the standard?
The wording is clear once you lie to the compiler you are in the land
of undefined behaviour.

I am pretty sure you are wrong, but I do not intend to enter an
endless discussion about that. As I said, the wording is so confusing
that one cannot rely on compiler writers all interpreting this in the
same way, so I have to stick to the safe interpretation anyway.
 
T

Tim Rentsch

It might just as well have printed 'A'. You have lied to the compiler
by declaring that foo() does not modify what its argument points to but
going ahead and doing so anyway. The compiler is permitted to believe
the declaration but is not required to, so either result is acceptable.
The code is not strictly conforming, but does not violate any
constraints so no diagnostics are required. It also does not contain
undefined behavior, just unspecified behavior.

With all due respect, this analysis is not correct. All of the
operations involved are well-defined, and none of the statements
or expressions rely on unspecified behavior (not counting sending
out just a single character to stdout without a trailing \n, but
that's not what is being discussed). As far as the Standard
goes, declaring a parameter to be of type (const int *) does not
impose any restrictions as to whether the function is allowed to
modify the pointed-to object. The cast to (int *) is guaranteed
to work (in this code), and no effective type rules are violated.
The program is not strictly conforming because of not sending out
a full line of output, but aside from that I don't see any reason
why it isn't.
 
T

Tim Rentsch

Ian Collins said:
Yes you can, but you have to lie to the compiler (cast) to do so.


The wording is clear: once you lie to the compiler you are in the land
of undefined behaviour.

It would be most helpful if you would post a response
supporting these statements, citing those sections
of the Standard that are pertinent.
 
I

Ian Collins

It would be most helpful if you would post a response
supporting these statements, citing those sections
of the Standard that are pertinent.

Maybe, but it's a lovely day and there's a beer with my name on it down
on the beach.

While I'm away, consider the case where the address passed is in read
only memory.
 
T

Tim Rentsch

Ian Collins said:
Maybe, but it's a lovely day and there's a beer with my name on it
down on the beach.

While I'm away, consider the case where the address passed is in read
only memory.

That circumstance hold in this case, as another posting from copx in
this thread makes clear. Certainly it is true that if a pointer to
an object that was defined as const-qualified is casted and used to
store then the resulting behavior is undefined. The question,
however, is what happens when a pointer to a non-const-qualified
object is converted to a pointer to a const-qualified type, which is
subsequently converted back to the original type and then used to
store. The behavior in such circumstances is well-defined, not
undefined. The reasoning about casting being lying to the compiler
is bogus; there is nothing wrong with this casting in the case
under consideration.
 
A

Alan Curry

#include <stdio.h>

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

Regardless of it being legal, this is a crappy thing to do, and there is a
gcc option to warn about it: -Wcast-qual
 
T

Thad Smith

Digging further I could only find this:
==
const int *ptr_to_constant;
...
The contents of anyobject pointed to by ptr_to_constant shall
not be modiï¬ed through that pointer,
==

Unfortunately, that wording is in an _example_ in section 6.7.9.1. Examples are
not normative.
 
B

Ben Bacarisse

Thad Smith said:
Unfortunately, that wording is in an _example_ in section 6.7.9.1.
Examples are not normative.

I don't think that matters. Normative text backs up that statement.

The key part being "that pointer". That pointer can't be used but other
pointers constructed from that pointer may permit modification of the
pointed-to object.
 
F

Francis Moreau

copx said:
"Ike Naar" wrote in message


I read the same section of the standard and this doesn't support
your statement IMO. The standard talks about OBJECTS with
a const-qualified type NOT about const-qualified references to
non-const-qualified objects. I.e. this only means that modifying a
"const int" is undefined, const-qualified references to
non-const-qualified objects aren't mentioned here.

Digging further I could only find this:
==
const int *ptr_to_constant;
..
The contents of anyobject pointed to by ptr_to_constant shall
not be modiï¬ed through that pointer,
==

Then I had to reference an entirely different section of the
standard to figure out the technical definition of "shall not".

This is one reason why HTML would be helpfull. Hyperlinks can be very
usefull here, since 'shall' word could have a different font, meaning
it's something special, and selecting (clicking or whatever) would bring
you to the definition instantaneous. And going back to your initial
reading is just a matter or clicking to 'Back'.

Is there any C drafts in HTML format ?

[...]
The exact, literal meaning of what the standard says validates the
behavior of GCC IMO, but the wording of the standard is so horribly
confusing here that I wouldn't be surprised if other compilers
interpret this mess in a different way.

Hey, welcome to the cruel C world.
 
F

Francis Moreau

[...]
For example:

int i; const int* p = &i; * (int *) p = 1;

is perfectly fine, but


const int i; const int* p = &i; * (int *) p = 1;

is undefined behaviour. The first doesn't modify a const object, the
second one does. Use "const int* restrict" instead:

int i; const int* restrict p = &i; * (int *) p = 1; // Undefined
behaviour

Could you argument your last example ?

I don't see why adding 'restrict' qualifier invokes undefined
behaviour. For me, the cast is defined and the effective rules are
respected.

The only thing it would do is to obfuscate your point.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top