14

I'm a bit confused what happened in the following code:


const int e = 2;

int* w = ( int* ) &e;          // (1) cast to remove const-ness
*w = 5;                        // (2)

cout &lt&lt *w &lt&lt endl;            // (3) outputs 5
cout &lt&lt e &lt&lt endl;             // (4) outputs 2

cout &lt&lt "w = " &lt&lt w &lt&lt endl;   // (5) w points to the address of e
cout &lt&lt "&e = " &lt&lt &e &lt&lt endl;

In (1), w points to the address of e. In (2), that value was changed to 5. However, when the values of *w and e were displayed, their values are different. But if you print value of w pointer and &e, they have the same value/address.

How come e still contained 2, even if it was changed to 5? Were they stored in a separate location? Or a temporary? But how come the value pointed by w is still the address of e?

jasonline
  • 8,646
  • 19
  • 59
  • 80
  • 15
    You are in undefined behaviour land - anything can happen. –  Mar 24 '10 at 14:48
  • My mind is too tired to see what is happening, but you can use const_cast for this. If you asked just to understand what is happening, you can disregard this comment. – erelender Mar 24 '10 at 14:49
  • 3
    @erelender: You could use `const_cast` for this but it wouldn't make the behaviour any less undefined. – CB Bailey Mar 24 '10 at 14:58

6 Answers6

17

As I said in my comment, once you modified the const value you are in undefined behaviour land, so it doesn't make much sense to talk about what is happening. But what the hell..

cout << *w << endl;            // (3) outputs 5
cout << e << endl;             // (4) outputs 2

At a guess, *w is being evaluated at runtime, but e is being treated as a compile time constant

  • Maybe... other possibility is that the compiler noticed "*w=5" just before and inserted 5 in the first line instead of *w – jpalecek Mar 24 '10 at 14:55
  • I agree. A const int initialized in scope will be a compile time constant. – Zan Lynx Mar 24 '10 at 14:57
  • Does this mean that there's just no use casting a const variable to a non-const pointer, since modifying through that pointer results to undefined behavior? – jasonline Mar 24 '10 at 15:02
  • 2
    Pretty much, the whole point of const is that you don't modify the value... you should never, ever cast away const. – Peter Alexander Mar 24 '10 at 15:05
  • @jasonline Modifying a const object is undefined. Using a const cast to get a non-const pointer, and then using that pointer to do something like call a non-const member function is OK, provided the member function does not change the original object. –  Mar 24 '10 at 15:06
  • @jpalecek: I think it would be a fairly obvious optimization and that in fact both are evaluated at compile time. It would be interesting to pass `w` to another (non inline) method to perform the printing and see what value is printed. – Matthieu M. Mar 24 '10 at 15:10
  • 1
    I agree with Neil, for a longer explanation `const X c = v`, where `X` is a enum or integral type, and `v` is an integral constant expression (i.e. `const int v = 5;`) defines a constant `c` that can be used as an integral constant expression itself. That means that the compiler can inline the value when compiling any use of `c`, which is what is most probably happening. The UB is that modifying that constant through a `const_cast` (the C style cast there is a `const_cast`) may do anything or nothing at all, including killing the application. In this particular case it is modifying the value. – David Rodríguez - dribeas Mar 24 '10 at 15:14
  • I just checked with a friend and, using GCC without any optimization, the result is 5 and 5 as expected, and the assembly is pretty much what you expect it to be. However, if you start using O1, you see that the compiler insert the immediate value on the stack to call printf, resulting in 5 and 2. – Gab Royer Mar 24 '10 at 15:17
  • @Gab Well, I just tried it with g++ 4.4.1 , no optimisations, and got 5 and 2. Just shows that when you are in UB land, anything can happen! –  Mar 24 '10 at 15:21
  • I see. Thanks for all your clarifications. I thought I was missing something. – jasonline Mar 24 '10 at 15:28
  • @Neil, yeah we just checked with g++ and we get the same results as you do. The way it was calling printf was kind of weird though as it wasn't pushing value on the stack but passing it through edi and esi. Anyways, undefined behavior it is! – Gab Royer Mar 24 '10 at 15:30
8

I suspect that you're tripping up the compiler. It doesn't expect you to play dirty tricks with e, so when it sees the line:

cout << e << endl;

It simply inserts the value 2 instead of looking for the actual value. You can verify (or disprove) this by looking at the disassembly of your program.

Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
4

I'm guessing that the compiler has optimised the value output. It sees that e is const (so, it can't change -- in theory) and changes cout << e << endl; to cout << 2 << endl;. However, e still has to exist because it's used by w, so w correctly takes its address and modifies its value, but you don't see that in the cout.

Moral of the story -- only declare things const when you actually want to be const. Casting away constness is not a good idea.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
3

The only thing I can think of is the compiler has some how optimised the code in such a way that any references to e are replaced with a value of 2 even though it assigns memory for e

so in effect (affect?) the line at comment (4) is 'optimized' to be

cout << "2" << endln;
thecoshman
  • 8,394
  • 8
  • 55
  • 77
2

I guess the compiler uses the constness to optimizes out the variable and insert a fixed value into the code.

Viesturs
  • 1,691
  • 4
  • 21
  • 25
1

This is covered by section [dcl.type.cv]/4 of the C++14 standard (earlier standards had similar text too):

Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.

e is a const object, and *w = 5; attempts to modify that object, therefore the result is undefined behavior.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365