# const really constant?

Discussion in 'C Programming' started by Mantorok Redgormor, Sep 19, 2003.

1. ### Mantorok RedgormorGuest

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?

Mantorok Redgormor, Sep 19, 2003

2. ### Pieter DroogendijkGuest

On 19 Sep 2003 13:38:37 -0700
(Mantorok Redgormor) wrote:
> 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?

--
char*x(c,k,s)char*k,*s;{if(!k)return*s-36?x(0,0,s+1):s;if(s)if(*s)c=10+(c?(x(
c,k,0),x(c,k+=*s-c,s+1),*k)x(*s,k,s+1),0));else c=10;printf(&x(~0,0,k)[c-~-
c+"1"[~c<-c]],c);}main(){x(0,"^[kXc6]dn_eaoh\$%c","-34*1'.+(,03#;+,)/'///*");}

Pieter Droogendijk, Sep 19, 2003

3. ### AlexGuest

Mantorok Redgormor <> wrote:
> 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

Alex, Sep 19, 2003
4. ### Nick AustinGuest

On 19 Sep 2003 21:02:05 GMT, Alex <> wrote:

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

Nick Austin, Sep 19, 2003
5. ### AlexGuest

Nick Austin <> wrote:
> On 19 Sep 2003 21:02:05 GMT, Alex <> wrote:

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

Looks like you're right. Sorry.

Alex

Alex, Sep 19, 2003
6. ### Ivan VecerinaGuest

Hi,
"Alex" <> wrote in message
news:bkg0dq\$1el38\$-berlin.de...
> Nick Austin <> wrote:
> > On 19 Sep 2003 21:02:05 GMT, Alex <> wrote:

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

>
> 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
--
http://ivan.vecerina.com

Ivan Vecerina, Sep 20, 2003
7. ### Peter NilssonGuest

"Nick Austin" <> wrote in message
news:...
> On 19 Sep 2003 21:02:05 GMT, Alex <> wrote:
>
> > 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. ..."

--
Peter

Peter Nilsson, Sep 20, 2003
8. ### John LGuest

Alex wrote
> Mantorok Redgormor <> wrote:
> > Is const really constant?

> 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!

John L, Sep 20, 2003
9. ### Michael WinterGuest

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

>
> 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

--
Michael Winter

Michael Winter, Sep 20, 2003
10. ### Arthur J. O'DwyerGuest

On Sat, 20 Sep 2003, Michael Winter wrote:
>
> > > const int x = 10;
> > > int *p = (int *)&x;

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

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

Arthur J. O'Dwyer, Sep 20, 2003
11. ### Michael WinterGuest

On Sat, 20 Sep 2003, Arthur J. O'Dwyer wrote:
>
> On Sat, 20 Sep 2003, Michael Winter wrote:
> >
> > 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.

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...]

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

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
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

--
Michael Winter

Michael Winter, Sep 20, 2003
12. ### Dave ThompsonGuest

On Sat, 20 Sep 2003 16:56:08 GMT, "Michael Winter"
<M.Winter@[no-spam]blueyonder.co.uk> wrote:

> On Sat, 20 Sep 2003, Arthur J. O'Dwyer wrote:

<snip>
> > 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.

>

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...]

<snip>
> > 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
> 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

Dave Thompson, Sep 29, 2003