Is this legal ?

K

Kelvin Moss

Hi group,

In C++ it's undefined behavior if one tries to un-const the constness
of a const variable with const_cast<>.

I want to know if the same holds good in C too.
E.g.

const char *s = "abc";
Later, is trying to do (char *) s legal ?

Thanks.
 
P

Peter Ammon

Kelvin said:
Hi group,

In C++ it's undefined behavior if one tries to un-const the constness
of a const variable with const_cast<>.

I want to know if the same holds good in C too.
E.g.

const char *s = "abc";
Later, is trying to do (char *) s legal ?

Thanks.

Short answer: yes

Longer answer:

Hang on there. What you say you're doing and what you actually are
doing differ. In your code

const char* s = "abc";

s is NOT a const variable. s is a (non-const) pointer to a const char.
Except the object it points to is not const either. string literals
in C (and in C++) are not const, which is why code like this is legal:

char* ptr = "string";

(I'm sure you know, though, that attempting to modify a string literal
is undefined, even though they are not const. const has very little to
do with whether an object can be modified.)

So let's reformulate your question. You seem to be asking if referring
to a object defined as const via a non-const lvalue is legal. Here's
one way we might do this:

const int var = 10;
int* ptr;
ptr = (int*)&var;

Answer: yes, this is fine. If it were not, a lot of code that was
written before const was invented would become illegal. It's when you
do this:

*ptr = 11;

that the nasal demons make an appearance.

-Peter
 
F

Flash Gordon

Hi group,

In C++ it's undefined behavior if one tries to un-const the constness
of a const variable with const_cast<>.

I want to know if the same holds good in C too.
E.g.

const char *s = "abc";
Later, is trying to do (char *) s legal ?

The following is fine by C
const char *const_s = "abc";
char *s = (char *)const_s;
puts(s);

however, if you then do:
*s='e';
you are invoking undefined behaviour.

char *s = "abc";
*s='e';

also invokes undefined behaviour, since although string literals are not
const in C any attempt to modify them invokes undefined behaviour.

const char const_s[] = "abc";
char *s = (char *)const_s;
*s='e';

also invokes undefined behaviour when the const data is modified.

i.e. you can cast away the const without any problem, but actually
modifying either a string literal or const data is undefined behaviour.
On embedded systems it could be an attempt to write to ROM which
obviously fails, on hosted systems it could be an attempt to write to a
page marked read only and cause your program to abort. Also the
optimiser could do all sorts of strange things generating even stranger
behaviour such as it appearing to have worked near where the change is
made but appearing to have not worked when the "modified" data is
accessed by another routine.

So don't try to confuse the compiler, you will only cause problems for
yourself.
 
K

Kelvin Moss

const int var = 10;
int* ptr;
ptr = (int*)&var;


Answer: yes, this is fine. If it were not, a lot of code that was
written before const was invented would become illegal. It's when you
do this:

*ptr = 11;

Thanks, Peter.
So what should be the approach if one wants to pass a const char*
variable to an API expecting a char * ?
I think that even though a simple cast can do the job it's dangerous as
one could step into the realm of UB if in the calling code someone
tries to modify the string literal. So one should copy the contents of
const char * into another variable and pass that to API, what do you
say ?

Thanks.
 
M

Michael Mair

Kelvin said:
Thanks, Peter.
So what should be the approach if one wants to pass a const char*
variable to an API expecting a char * ?
I think that even though a simple cast can do the job it's dangerous as
one could step into the realm of UB if in the calling code someone
tries to modify the string literal. So one should copy the contents of
const char * into another variable and pass that to API, what do you
say ?

If you mean by "copy the contents of const char *" "copy the string the
const char * variable points to" and by "variable" "object", then yes.
Usually, copying the content of a const char * variable to, say, a
char * variable means that you now have a variable pointing to the same
object which cannot be modified by using the const char *.

On the other hand, you really do not need the "constant" string in
the first place, as you want it to be modified by said function.
As an aside: If you use
char str[] = "Hello";
instead of
char *str = "Hello";
you obviously do not have any problems with modifying str as it is an
array which due to the lacking const qualifier can be modified.


Cheers
Michael
 
C

CBFalconer

Kelvin said:
.... snip ...

So what should be the approach if one wants to pass a const char*
variable to an API expecting a char * ? I think that even though
a simple cast can do the job it's dangerous as one could step into
the realm of UB if in the calling code someone tries to modify the
string literal. So one should copy the contents of const char *
into another variable and pass that to API, what do you say ?

As usual, that depends. The safest way is to copy the const char*
into another variable and pass that. If you can establish that the
external routine doesn't modify the variable, possibly because it
has been mis-specified, or because it predates the introduction of
const into the C language, you could consider casting it. As
usual, the presence of a cast strongly indicates a programming
error.

Nit: C doesn't have an API, it can have external functions.
 
K

Kelvin Moss

Nit: C doesn't have an API, it can have external functions

I know this must be some pedantic stuff :) Could you explain a little
more to me about it ?

Thanks.
 
O

Old Wolf

Peter Ammon said:
const char* s = "abc";

s is NOT a const variable. s is a (non-const) pointer to a const char.
Except the object it points to is not const either. string literals
in C (and in C++) are not const, which is why code like this is legal:

A nitpick: in C++, string literals are arrays of const char.
However the implicit conversion "array-of-const-char to
pointer-to-char" is permitted, which is why you can do this:
 
S

S.Tobias

Peter Ammon said:
const int var = 10;
int* ptr;
ptr = (int*)&var;
Answer: yes, this is fine. If it were not, a lot of code that was
written before const was invented would become illegal. It's when you
do this:
*ptr = 11;
that the nasal demons make an appearance.


I have two questions:

1.
int i = 0;
const int *cp = &i;
int *p = (int*)cp;
*p = 1;

What happens to the value of `i'?
What happens to the value of `*cp'?

All three variables might be in different scopes.
Suppose I pass `&i' to a function which has a prototype
and accepts `const int*', does it mean that `i' object has
to be re-read from memory after the function returns?
Or could its value be cached?


2. I have a difficulty interpreting 6.5#7:

[#7] An object shall have its stored value accessed only by
an lvalue expression that has one of the following types:63)

-- a type compatible with the effective type of the
object,

-- a qualified version of a type compatible with the
effective type of the object,

[...]

I think it says that `int' can be accesed as `int' or `const int';
`const int' can be accessed as `const int', but not as `int'.

In your example the assignment to `ptr' would be okay, but mere
reading `*ptr' would be undefined.

Could you point me to where I'm wrong?
 
D

Dave Vandervies

I have two questions:

1.
int i = 0;
const int *cp = &i;
int *p = (int*)cp;
*p = 1;

What happens to the value of `i'?
What happens to the value of `*cp'?

Both become 1.
All three variables might be in different scopes.
Suppose I pass `&i' to a function which has a prototype
and accepts `const int*', does it mean that `i' object has
to be re-read from memory after the function returns?
Or could its value be cached?

The const qualifier in "pointer to const type" means "I promise not
to modify whatever this pointer is pointing at through this pointer"
(but even that is easy enough to subvert by casting it to a non-const but
otherwise equivalent pointer); it doesn't mean "I can expect what this
pointer points at to not change". So no, the value couldn't be cached
across, f'rexample, calls to unknown functions - the pointer would have
to be followed again if the compiler can't guarantee that nothing that
has happened since it was last read would have changed the value.


dave
 
S

S.Tobias

Dave Vandervies said:
The const qualifier in "pointer to const type" means "I promise not
to modify whatever this pointer is pointing at through this pointer"
(but even that is easy enough to subvert by casting it to a non-const but
otherwise equivalent pointer); it doesn't mean "I can expect what this
pointer points at to not change". So no, the value couldn't be cached
across, f'rexample, calls to unknown functions - the pointer would have
to be followed again if the compiler can't guarantee that nothing that
has happened since it was last read would have changed the value.

Thanks a lot!

It means that casting-out const is not as bad as I thought it was,
and the promise is rather a weak one - more like a wishful thinking!
I thought that the compiler may locally assume that after taking an
address of an object into a pointer to const type version, the object
won't change. I was wrong.

It seems to me that `const' in C is a device to generate warnings or
errors rather than to change semantics of a program. If we removed all
`const' quals from a program, the binary code output should not change,
should it?

[OT] Do you happen to know if `const' has the same semantics in C++?
(There are of course different constraints - C++ is more type sensitive,
different conversion rules etc. I'm only asking in context of what has
been said above.)
 
M

Michael Mair

S.Tobias said:
Thanks a lot!

It means that casting-out const is not as bad as I thought it was,
and the promise is rather a weak one - more like a wishful thinking!
I thought that the compiler may locally assume that after taking an
address of an object into a pointer to const type version, the object
won't change. I was wrong.

Yes, that is true -- however, anyone who modifies const-qualified
data should be hung from their heels and heavily flogged... umh...
should be considered a serious danger on a team of developers.
If we get const-qualified data via a parameter list, we promise
to behave and not modify the data. If it becomes necessary to modify
this data later on in this self-same function, the const qualifier
has to be erased in the current functions parameter list and, if
necessary, in the parameter lists of functions calling this functions
and so on.

It seems to me that `const' in C is a device to generate warnings or
errors rather than to change semantics of a program. If we removed all
`const' quals from a program, the binary code output should not change,
should it?

The same could be said for the C99 restrict qualifiers -- but code
may rely on the programmer doing things correctly.
Two different restrict-qualified pointers should not point to the same
object but you can of course abuse them nonetheless.

Even under the danger of bringing up the discussions once again:
C is not exactly a strongly typed language, as it did not start out
with "enough" types (see, for example,
http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ) to make this
necessary. The const qualifier was only an add-on which should not
break existing code.

However, the compiler might rely on your claim that you will never
ever access the respective memory. If you do differently in the
translation unit where a function is defined, there may be the need
to work around the problem at a late compiling or at the linking stage,
which may result in different binary code.

[OT] Do you happen to know if `const' has the same semantics in C++?
(There are of course different constraints - C++ is more type sensitive,
different conversion rules etc. I'm only asking in context of what has
been said above.)

IIRC, to get rid of a const qualifier you need a const_cast; const-
qualified variables are truly constants, so the const_cast works only
for objects which started out non-const. That said, I guess that the
c.l.c++ FAQ knows it much better than me.


Cheers
Michael
 
M

Michael Mair

S.Tobias said:
Thanks a lot!

It means that casting-out const is not as bad as I thought it was,
and the promise is rather a weak one - more like a wishful thinking!
I thought that the compiler may locally assume that after taking an
address of an object into a pointer to const type version, the object
won't change. I was wrong.

Yes, that is true -- however, anyone who modifies const-qualified
data should be hung from their heels and heavily flogged... umh...
should be considered a serious danger on a team of developers.
If we get const-qualified data via a parameter list, we promise
to behave and not modify the data. If it becomes necessary to modify
this data later on in this self-same function, the const qualifier
has to be erased in the current function's parameter list and, if
necessary, in the parameter lists of functions calling these functions
and so on.

It seems to me that `const' in C is a device to generate warnings or
errors rather than to change semantics of a program. If we removed all
`const' quals from a program, the binary code output should not change,
should it?

The same could be said for the C99 restrict qualifiers -- but the compiler
usually relies on the programmer doing things semantically correct
whenever the syntax is correct.
Two different restrict-qualified pointers should not point to the same
object but you can of course abuse them nonetheless.

Even under the danger of bringing up the discussions once again:
C is not exactly a strongly typed language, as it did not start out
with "enough" types (see, for example,
http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ) to make this
necessary. The const qualifier was only an add-on which should not
break existing code.

However, the compiler might rely on your claim that you will never
ever access the respective memory. If you do differently in the
translation unit where a function is actually defined, there may be the
need to work around the problem at a late compiling or at the linking
stage, which may result in different binary code.

[OT] Do you happen to know if `const' has the same semantics in C++?
(There are of course different constraints - C++ is more type sensitive,
different conversion rules etc. I'm only asking in context of what has
been said above.)

IIRC, to get rid of a const qualifier you need a const_cast; const-
qualified variables are truly constants, so the const_cast works only
for objects which started out non-const. That said, I guess that the
c.l.c++ FAQ knows it much better than me.


Cheers
Michael
 
D

Dave Vandervies

Dave Vandervies said:
Thanks a lot!

It means that casting-out const is not as bad as I thought it was,
and the promise is rather a weak one - more like a wishful thinking!
I thought that the compiler may locally assume that after taking an
address of an object into a pointer to const type version, the object
won't change. I was wrong.

Note that if an _object_ is defined with the const qualifier, it really
does mean "this object can not be changed by the program"; any attempts
to change it (by casting away const from a pointer to it, most likely)
invoke undefined behavior.
This means that you do need to be careful with casting away const, and
make sure that you don't do it unless you know you won't end up trying
to write to a const object through the de-const-ed pointer.

A lot of library functions (f'rexample, the string-searching ones) do
something like this; they take a const pointer (to indicate that they
don't modify the pointed-at data), do the appropriate operation, and
cast away the const qualifier before returning a non-const pointer into
that data. If you give them a const pointer, it's your responsibility
to put the result back into a const pointer so that you don't lose the
constness (but if you're reasonably careful with this then it will do
The Right Thing in either case, since converting a const pointer to a
non-const pointer (with the equivalent of a cast in the library function)
and then back to a const pointer (by assigning the result in your code)
is valid and well-defined.

("const pointer" in the above paragraph refers to "pointer to const
object" (const foo *ptr), which is not the same thing as declaring the
pointer itself const (foo * const ptr).)
It seems to me that `const' in C is a device to generate warnings or
errors rather than to change semantics of a program. If we removed all
`const' quals from a program, the binary code output should not change,
should it?

Pretty much, yes.

Though I suspect that along with `restrict' in C99 it may actually make
some optimization possible; f'rexample, a pointer argument with both
const and restrict qualifiers can be assumed to not be changed through
any other pointers given to the function (though I don't know whether
this covers, say, access to a global variable from a function called by
the function getting the const+restrict pointer).

[OT] Do you happen to know if `const' has the same semantics in C++?
(There are of course different constraints - C++ is more type sensitive,
different conversion rules etc. I'm only asking in context of what has
been said above.)

It doesn't; C++ has const semantics that (combined with its general
greater type sensitivity) are actually strong enough to be useful.
F'rexample, combined with being able to overload functions on different
types, this allows C++ to handle the aforementioned case of library
functions casting away const on pointers they're given by returning a
pointer with the same constness of the pointer they're given, so giving
it a const pointer and assigning the result to a non-const pointer
(having the same effect as casting away const, but without a visible
cast in your code) becomes an error the compiler is required to catch.


dave
 
S

S.Tobias

S.Tobias said:
Dave Vandervies <[email protected]> wrote:
[snipped discussion on "const" semantics]
[OT] Do you happen to know if `const' has the same semantics in C++?

http://www.gotw.ca/gotw/081.htm

This article is about C++, but I guess it applies to C in a large part
as well. It shows that direct or indirect object access semantics
wrt "const" is the same in both languages - what is really important
is the constness of the object itself (whether "natural" or by definition).
All other "const" issues (and differences) are in the type system
and don't affect object access as such.

The article gives reasons why a compiler does not assume anything about
a non-const object value when you pass outside a pointer to const object.
(IMHO there could be opportunities for optimization for local objects
when only ptrs-to-const are taken, but then it would require changes in
the language, eg. casting out const, or casting at all, would have to
be deemed as UB.)
 

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

Members online

Forum statistics

Threads
473,772
Messages
2,569,588
Members
45,100
Latest member
MelodeeFaj
Top