const really constant?

M

Mantorok Redgormor

Is const really constant?

And on an OT note: how can I post with a modified e-mail address so I
don't get so much spam?
 
P

Pieter Droogendijk

On 19 Sep 2003 13:38:37 -0700
Is const really constant?

No, const is a keyword. A variable defined to be const may not change value
after definition.
And on an OT note: how can I post with a modified e-mail address so I
don't get so much spam?

Read your newsreader's documentation and improvise.
 
A

Alex

Mantorok Redgormor said:
Is const really constant?

Like Chris Torek always says, 'const' in C means 'read-only'. It
does not act as a constant when a constant expression is expected,
nor does it completely preclude modifications to the variable.

Consider:

const int x = 10;

int array[x]; /* ilegal, not constant expression */

and

const int x = 10;
int *p = (int *)&x;

*p = 2; /* ooops, just changed x */

Alex
 
N

Nick Austin

const int x = 10;
int *p = (int *)&x;

*p = 2; /* ooops, just changed x */

Is that not UB?

N869:
"J.2 Undefined behavior

[snip]

-- An attempt is made to modify an object defined with a
const-qualified type through use of an lvalue with non-
const-qualified type."

Nick.
 
I

Ivan Vecerina

Hi,
Alex said:
Is that not UB?
N869:
"J.2 Undefined behavior

-- An attempt is made to modify an object defined with a
const-qualified type through use of an lvalue with non-
const-qualified type."

Looks like you're right. Sorry.

Yes, however the following is not UB and demonstrates the
same issue:

int f( int* a, int const* b )
{
*a += *b;
return *b; // must re-read *b, may have changed
}

int main()
{
int i = 10;
printf( "%d\n", f(&i,&i) );
return 0;
}

This program must print 20, and the compiler may
not optimize the two accesses to *b within f().
Const guarantees that the value cannot be modified
through the variable/pointer itself. But the
compiler cannot assume that the value will remain
unchanged.

This is why a variable can be both const and volatile.

This is also related to why 'restrict' was introduced
in the C99 standard ...


Kind regards,
Ivan
 
P

Peter Nilsson

Nick Austin said:
const int x = 10;
int *p = (int *)&x;

*p = 2; /* ooops, just changed x */

Is that not UB?

N869:
"J.2 Undefined behavior

[snip]

-- 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 normative reference is 6.7.3p5

"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. ..."
 
J

John L

Alex wrote
Consider:

const int x = 10;

int array[x]; /* ilegal, not constant expression */

and

const int x = 10;
int *p = (int *)&x;

Sorry to digress, but is the cast above necessary? Isn't

int *p = &x;

correct?

Thanks!
 
M

Michael Winter

const int x = 10;
Sorry to digress, but is the cast above necessary? Isn't

int *p = &x;

correct?

I believe the reason is that &x will return a pointer that is of type "const
int *". As p doesn't have a const modifier, the assignment will be illegal.
The cast attempts to remove that modifier before assignment. As mentioned
by others, this is undefined in C, but quite legal as I understand (at least
with Microsoft's implementation) in C++.

Mike
 
A

Arthur J. O'Dwyer

I believe the reason is that &x will return a pointer that is of type "const
int *". As p doesn't have a const modifier, the assignment will be illegal.

That's correct. The address of an 'int' is a pointer to 'int'; the
address of a 'const int' is a pointer to 'const int'.
The cast attempts to remove that modifier before assignment. As mentioned
by others, this is undefined in C,

Not quite. The initialization

const int x = 42;
int *p = (int *)&x;

is absolutely legal in C. What's *not* legal is following that
up with

(*p) = 43;

because that tries to modify a const-qualified value. And
since it would be burdensome to make the compiler catch all
such errors, the standard simply calls the result of the
modification "undefined behavior" and leaves it at that.
but quite legal as I understand (at least
with Microsoft's implementation) in C++.

I seriously doubt this. My impression is that C++ is *more*
strict about type-safety than C, not less.

-Arthur
 
M

Michael Winter

illegal.

That's correct. The address of an 'int' is a pointer to 'int'; the
address of a 'const int' is a pointer to 'const int'.


Not quite. The initialization

const int x = 42;
int *p = (int *)&x;

is absolutely legal in C. What's *not* legal is following that
up with

(*p) = 43;

because that tries to modify a const-qualified value. And
since it would be burdensome to make the compiler catch all
such errors, the standard simply calls the result of the
modification "undefined behavior" and leaves it at that.

I glossed over that. As I said, others mentioned what would occur if you
tried to modify a const-qualified variable through a pointer.


[OT from here...]
I seriously doubt this. My impression is that C++ is *more*
strict about type-safety than C, not less.

That's a matter of debate. In addition to the C-style cast, C++ includes
four other casting operators.

dynamic_cast *is* strict - a run-time type check is performed on the
variable being cast [what checks, I'll omit]. If the cast fails, a bad_cast
exception is thrown.

static_cast checks the validity of the cast based on the information that
the developer provides (original and desired type). There was no mention of
exceptions in the case of a failure here, but there is the possibility of
undefined behaviour.

reinterpret_cast allows you to convert any pointer into any other pointer -
there are no checks what-so-ever.

const_cast allows you to strip the const, volatile and __unaligned
attributes. Here, a write through a cast pointer (including one that was
previously const) *might* be undefined, but it depends on the type of
object. I don't have any more information on that: Microsoft's
documentation stops there (there was no "Microsoft-specific" marking, so I
assume that it is standard C++).

I do have to admit that I forgot the "might be undefined" part (I usually
stick to C-style casts, and I've never cast away a const-qualifier anyway).
However, it's not quite so clear cut as others present it in C, nor is C++
any more type-safe than C, unless the developer chooses to make it so with
the more reliable casts.

Mike
 
D

Dave Thompson

And just to be clear, any other storage or computation (assignment,
passing or returning, addtion/subtraction within an array) on the
deconstified pointer is legal; it is only dereference, or subscripting
which includes dereference, that is UB.
I glossed over that. As I said, others mentioned what would occur if you
tried to modify a const-qualified variable through a pointer.
If the pointer is to const, it is a constraint violation and diagnosed
(in C; in C++ it is a violation whose diagnosis is not waived, same
result). If the pointer has had const cast away, it is UB.
[OT from here...]
I seriously doubt this. My impression is that C++ is *more*
strict about type-safety than C, not less.

That's a matter of debate. In addition to the C-style cast, C++ includes
four other casting operators. <snipped except>
If the [dynamic_cast] fails, a bad_cast exception is thrown.
For a reference; for a pointer it gives a null pointer.
const_cast allows you to strip the const, volatile and __unaligned
attributes. Here, a write through a cast pointer (including one that was
previously const) *might* be undefined, but it depends on the type of
object. I don't have any more information on that: Microsoft's
documentation stops there (there was no "Microsoft-specific" marking, so I
assume that it is standard C++).
It's standard, except for __unaligned as you might guess. Storing to
an object that was *defined* as const (and not mutable) is UB, as in
C: 7.1.5.1[dcl.type.cv]p4, as referenced by 5.2.11[expr.const.cast]p7,
and extended to deconstructed memory in 3.8[basic.life]p9; in C the
lifetime of an object is from allocation to deallocation, but in C++
for a nontrivial class type only from construction to destruction, so
this makes the const rule apply from allocation to deallocation.

If you create a const pointer to an object that is actually nonconst,
as can easily be done implicitly, then cast away const and store
through the result, that is well-defined and works in both C and C++.

Also, in C++ but not C a const variable of integer or enumeration type
initialized by a constant expression can be used in a constant
expression. This means that if you do illegally store to such a
variable, after/by forming a nonconst pointer to it, and the
implementation doesn't trap and actually does the store -- one
permitted option under UB -- it is rather likely that the stored value
will not be used in what appear to be subsequent fetches of it. A C
compiler may do this same optimization if it likes, under the (very)
broad aegis of UB, but it isn't encouraged the way C++ is.
I do have to admit that I forgot the "might be undefined" part (I usually
stick to C-style casts, and I've never cast away a const-qualifier anyway).
However, it's not quite so clear cut as others present it in C, nor is C++
any more type-safe than C, unless the developer chooses to make it so with
the more reliable casts.
It is in several places.

C++ does not allow implicit conversion from base to derived, which is
a feature that does not exist in C, but extends this principle to
prohibit implicit conversion of void* to any other data*, which C
allows; this is a FDiscussedFeature(?) on clc.

C++ considers enum types (and their values) distinct and does not
allow implicit conversion from an integer. C considers enums just
integers of some implementation-dependent size.

Optional arguments, and to some extent templates, allow some functions
with varying signatures to be written typesafely that would require
less-safe varargs in C, although that option still exists in C++.

C++ outlawed implicit int (and implicit function declaration) first,
but C99 has matched it. C++ has only the prototype syntax for
functions, not the less-safe K&R1 syntax still allowed in C.

C++ prohibited unvalued return for nonvoid functions first, but C99
has matched it; C++ also makes it UB immediately when falling off the
end of a nonvoid function, instead of trying to use the indeterminate
return value as in C, and this is often easier to diagnose though not
required. C++ also allows a return "value" of void type in a void
function, which is useful for templates, but not really needed in C.

C++ retains C's array/pointer duality and pointer arithmetic, which
are in practice impractical to make safe, but offers the option of
std::vector or other array-like classes and "smart" pointers, and in
particular std::string or other string classes for the area that has
proven most frequently (IMJ) troublesome in C.

But yes, the new more specific -- and more visible in source -- casts
are an important contributor to typesafety in C++.

- David.Thompson1 at worldnet.att.net
 

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,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top