17

I am trying to change the value of a variable which is defined as int const as below.

const int w = 10;
int* wp = const_cast <int*> (&w);
*wp = 20;

The value of w didn't change and was 10 even after the assignment, though it shows as if both w and wp are pointing to the same memory location. But I am able to the change the value of w, if defined as below while declaring

int i = 10;
const int w = i;

If I change the declaration of i to make it const like in

const int i = 10;

The value of w doesn't change.

In the first case, how come the value of w didn't change, even though w and wp point to the same memory location [ that was my impression I get when I print their addresses ]

What difference it's to the compiler that it treats both the cases differently?

Is there a way to make sure that w doesn't lose constness, irrespective of the way it is defined?

Paul Stephenson
  • 67,682
  • 9
  • 49
  • 51
Narendra N
  • 1,270
  • 1
  • 9
  • 14
  • 11
    If an object was initially created as `const`, then you can't (shouldn't) `const_cast` it to a non-`const`; that's undefined behaviour. If an object was initially created as non-`const`, you can `const_cast` back and forth as you please. – C. K. Young Jan 05 '10 at 13:12
  • 7
    On some implementations, writing to a `const` object can crash your program, if the object's contents resides in a read-only section (e.g., `.rodata` as opposed to `.data`, where non-`const` data sits). – C. K. Young Jan 05 '10 at 13:14
  • 6
    Nobody seems to have asked the obvious question - if you want to change the value, why are you marking the variable as `const`? – Graeme Perrow Jan 05 '10 at 14:12
  • 3
    @Graeme: No, the OP wants a way to make the `const` stick, i.e., to guarantee that people can't `const_cast` the `const`ness away from their objects. This is stated in the last line of the question. – C. K. Young Jan 05 '10 at 14:41
  • OK, but the first line of the question is "I am trying to change the value of a variable which is defined as int const as below." – Graeme Perrow Jan 05 '10 at 17:48
  • WHY! Why are you trying to do this. If it is marked const there is a reason. If you want to change the calue its not a const. – Martin York Jan 05 '10 at 18:26
  • 2
    It's not that I am trying to change the value, I am trying to understand the difference and also how can I disallow others using my program. I tried to put in a bigger comment, but it seems it is larger than what should be a comment. Will rephrase and comment. – Narendra N Jan 06 '10 at 08:47
  • One of my colleague mentioned that a variable remains constant only when the value is known at the compile time. When defined as int i = 10; int const w = i; here as i is not const, we would be able to change it's value before the defintion of w, by reading the value from the user. if the value to a const variable is assigned with a literal or const variable, the value will be known at compile time and remains constant. May be the purpose of the const_cast is as commented by Chris Jester, to const_cast back and forth those which are created as non-const and not the other way. – Narendra N Jan 06 '10 at 08:54
  • 1
    I'm linking this to another question as this one has never had an answer accepted , and most of the answers are "my compiler did this" without addressing the undefined behaviour, which isn't very useful. – M.M May 05 '16 at 13:38
  • The answer to "How do I stop someone causing undefined behaviour elsewhere in the program" is "you can't" – M.M May 05 '16 at 13:38

8 Answers8

16

This is one of the cases where a const cast is undefined, since the code was probably optimized such that w isn't really a variable and does not really exist in the compiled code.

Try the following:

const volatile int w = 10; 
int &wr = const_cast <int &> (w); 
wr = 20; 
std::cout << w << std::endl;

Anyhow, I would not advise abusing const_cast like that.

Paul Stephenson
  • 67,682
  • 9
  • 49
  • 51
rmn
  • 2,386
  • 1
  • 14
  • 21
  • 3
    The magic difference is the `volatile` keyword. Take that out and w=10 just like in Narendra's original pointer-based example. – Paul Stephenson Jan 05 '10 at 13:26
  • 1
    You are correct, the volatile qualifier is used to keep the compiler from optimizing w away. – rmn Jan 05 '10 at 14:10
  • I have tried the same on three different compilers [ Visual C++ Express Edition, g++ and Sun CC compiler ], on all three compilers the result was same. There was consistency in the results with all the compilers and hence the confusion. Btw, I tried with volatile and it worked as mentioned. – Narendra N Jan 06 '10 at 08:24
  • One of my colleague mentioned that a variable remains constant only when the value is known at the compile time. When defined as int i = 10; int const w = i; here as i is not const, we would be able to change it's value before the defintion of w, by reading the value from the user. if the value to a const variable is assigned with a literal or const variable, the value will be known at compile time and remains constant. May be the purpose of the const_cast is as commented by Chris Jester, to const_cast back and forth those which are created as non-const and not the other way – Narendra N Feb 01 '10 at 05:56
  • 1
    All writes of const objects are undefined. Not sure what the `volatile` example is supposed to show, it is still undefined. – M.M May 05 '16 at 13:28
8

The code in the above example translates into the following assembler:

    movl    $10, 28(%esp)  //const int i = 10; 
    leal    28(%esp), %eax //int* wp = const_cast <int*>(&i);
    movl    %eax, 24(%esp) //store the pointer on the stack
    movl    24(%esp), %eax //place the value of wp in eax
    movl    $20, (%eax) //*wp  = 20; -  so all good until here
    movl    $10, 4(%esp) //place constant value 10 onto the the stack for use in printf
    movl    $.LC0, (%esp) // load string
    call    printf //call printf

Because the original int i was declared constant, the compiler reserves the right to use the literal value instead of the value stored on the stack. This means that the value does not get changed and you are stuck with the original 10.

The moral of the story is compile time constants should remain constant because that is what you are telling the compiler. The moral of the story is that casting away constness in order to change a constant can lead to bad things.

Graeme Perrow
  • 56,086
  • 21
  • 82
  • 121
doron
  • 27,972
  • 12
  • 65
  • 103
5

const_cast doesn't take away the const-ness of a variable as defined. If you were to pass a non-const variable by reference in to a method taking a const reference like void foo(const int& x) then you could use const_cast to modify the value of x within foo, but only if the variable you actually passed in was not const in the first place.

Dave
  • 5,133
  • 21
  • 27
2

Why can't you just re-bind the constant? So instead of

const int w = 10;
int* wp = const_cast <int*> (&w);
*wp = 20;
// some code

just introduce the different constant with the same name

const int w = 10;
{
   const int w = 20;
   // the same code
}

If the "new" constant should depend on its own value, you should introduce another constant (const int _w = w; const int w = _w * 2;). The unnecessary assignments will be optimized out by compiler--because we've seen it has done such optimisation, as it's the reason why you asked your question.

P Shved
  • 96,026
  • 17
  • 121
  • 165
  • 1
    Ugly are your `const_cast`s and assembly code, while re-binding constants is a natural way to work with them. – P Shved Jan 05 '10 at 14:23
2

You should not being changing the const value. There is a reason it is const and trying to change it will most likely just result in errors. If the const is stored in a read only memory section then you will get access violations.

user230821
  • 1,093
  • 3
  • 12
  • 21
  • 1
    Fair enough, and would be my first reaction as well. But in the real world, sometimes you gotta. "There is a reason it is const," assumes, at minimum, that the programmer who originally made it const knew what he was doing. – John Dibling Jan 05 '10 at 17:59
  • @John: Even in the real world there is no reason to change a const value. Fix the underlying problem rather then mess around with it. – Martin York Jan 05 '10 at 18:25
1

Here's a refresher, it should be noted that this is in C. This is a deceptively tricky underpinnings of the usage of a variable or pointer using the const keyword. This highlights the difference between the pointer variable foo and how the meaning of it can change by using the said keyword.

char const *foo;

char * const foo;

const char *foo;

The first and last declarations, makes the data pointed to by ‘foo’ read-only, but, you can change the address pointed to by ‘foo’ e.g.

const *char foo; /* OR char const *foo */

char str[] = "Hello";

foo = &str[0]; /* OK! */

foo[1] = 'h'; /* BZZZZTTT! Compile Fails! */

The middle declaration in the above, makes the pointer read-only, i.e. you cannot change the address of the data pointed to by ‘foo’

char * const foo;

char str[] = "Hello";

foo = &str[0]; /* BZZZZTTT! Compile Fails! */
t0mm13b
  • 34,087
  • 8
  • 78
  • 110
1

Good question. I think the confusion comes from the fact that C++ uses the keyword ‘const’ for two different concepts depending on the context. These concepts are constant and read-only variables.

When a value of a ‘const’ variable can be calculated during the compilation, it creates a true constant. References to such constant are replaced with its value whenever it is used. That’s why there is no location in the memory that can be changed to affect all places where it is used. It is like using #define.

When value of a ‘const’ variable cannot be calculated during the compilation, it creates a read-only variable. It has a location in the memory that contains a value but compiler enforces a read-only behavior.

Dennis
  • 2,615
  • 2
  • 19
  • 20
0

My guess would be that declaring w const allows the compiler to perform more aggressive optimizations such as inlining w's value and reordering instructions. Wether w appears to change or not depends on which optimizations were applied in the precise case and is not under your control.

You can't force w to be totally const. The cons_cast should be a hint to the programmer that they might be doing something fishy.

Zen
  • 119
  • 7