0

This answer at this question explains how changing a constant variable via dereferencing a pointer to it's address works (apparently by creating a new variable).

const int i = 10;
*(int *)&i = 5;

std::cout << &i << "\t" << i << "\n";                   // 0x7fff6b325244   10

std::cout << &*(int *)&i << "\t" << *(int *)&i << "\n"; // 0x7fff6b325244   5

With &*(int *)&i I was trying to get the address of the new variable that the previous answer was talking about. How do I find where this new variable is stored?

*(int *)&i is showing a different value, so there has to be a new variable.

Compiled on g++ 5.4.0, Ubuntu 16.04

Community
  • 1
  • 1
tabs_over_spaces
  • 352
  • 1
  • 3
  • 14
  • 2
    Undefined behaviour. –  Mar 15 '17 at 18:17
  • 1
    That's a lot of de- and re-referencing. Be careful. – erip Mar 15 '17 at 18:17
  • I know it has to be undefined behavior and it'll depend on the compiler as well, but why does the `*(int *)&i` show the changed value? – tabs_over_spaces Mar 15 '17 at 18:19
  • 1
    @AdityaGulavani That's the definition of undefined behavior -- behavior that you cannot predict. – erip Mar 15 '17 at 18:20
  • @AdityaGulavani because compiler does not bother to look into memory and uses `const int` value directly. – Slava Mar 15 '17 at 18:20
  • It could be the compiler detecting your iniquity and emit another variable, so *i* remains unchanged. – The Techel Mar 15 '17 at 18:21
  • 3
    It's a waste of time to reason about undefined behaviour. –  Mar 15 '17 at 18:21
  • 5
    *"is showing a different value, so there has to be a new variable."* -- No, there doesn't. Since you lied to the compiler and told it that the value of `i` would never change from 10, it is free to simply use the constant value 10 wherever you request the value of `i`. It doesn't need a variable for this. – Benjamin Lindley Mar 15 '17 at 18:21
  • Try disassembling the code to see what the compiler _actually_ generates for this – Craig Estey Mar 15 '17 at 18:25
  • I am currently exploring effects of declaring it as volatile as well. Would be great if you can add you two cents to that thought – tabs_over_spaces Mar 15 '17 at 18:32
  • Under what programming use case would you ever want to do this? – sizzzzlerz Mar 15 '17 at 18:33
  • Check this out http://stackoverflow.com/questions/39106137/change-constant-value – Sniper Mar 15 '17 at 18:35

2 Answers2

6

The behavior when changing the value of a variable declared const is undefined. The compiler doesn't have to do anything that makes sense.

In this case, the compiler doesn't bother to look into memory when printing the const value i, since it knows that value couldn't have possibly been altered by any conforming code. When you print i, it generates this assembly:

mov     esi, 10
mov     rdi, rax
call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

Notice that it just hard-codes 10 in that call.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • I am currently exploring effects of declaring it as volatile as well. Would be great if you can add you two cents to that thought – tabs_over_spaces Mar 15 '17 at 18:32
  • @AdityaGulavani: The `const` specifier means you are telling the compiler that the value in the identifier will not change. The `volatile` specifier means that the value of a variable my change by means outside of program execution (other threads, hardware registers, etc). You can't have a `volatile` `const`, as it doesn't make sense. – Thomas Matthews Mar 15 '17 at 19:15
  • Note that the constants may end up in a read only section inside your executable and even if you try to write at the address of a const variable (assuming there is one), the OS will kill your process with an access violation fault because you attempted to write a read only page. – icebp Mar 15 '17 at 19:15
  • 1
    @ThomasMatthews The standard has no prohibition against declaring a `const volatile T`, but after combing through the document a bit I still couldn't tell you what such a construct is supposed to mean. My best guess is that it means "its value may change, but you can't change it". – Miles Budnek Mar 15 '17 at 19:21
  • As far as "volatile const" goes: volatile const i = 0; volatile int *p = &i; *p = 2; both MSVC 2015 and GCC 5 will warn you about it. Both will generate code that writes at i's address (with optimizations disabled). Oddly enough, i will be placed in a read write section inside the PE. But this means nothing, as with other versions / settings the generated executable would probably be a lot different. And it will work and change i's value. – icebp Mar 15 '17 at 19:26
3

As the original answer points out correctly:

It's "undefined behaviour", meaning that based on the standard you can't predict what will happen when you try this. It may do different things depending on the particular machine, compiler, and state of the program.

Anything is fair game, including what you observe, which is probably just the result of a compiler optimization. You shouldn't spend to much time trying to interpret undefined behavior.

Edit: To be more clear regarding the recommendation: DO NOT DO THAT.

Zulan
  • 21,896
  • 6
  • 49
  • 109
  • Yes, I never do that. I just wanted to know the underlying compiler behaviour when something like this comes up – tabs_over_spaces Mar 15 '17 at 18:29
  • @AdityaGulavani the compiler behaviour might not be consistent even between two compilations of same program. And it almost surely would differ between different compilers. One thing you can do is to make a list of most likely behaviour in simple situations, but it wouldn't be really useful. P.S. Did you get a _attempt to write into write-protected page_ error already in your experiments? – Revolver_Ocelot Mar 15 '17 at 18:40
  • Not yet, now that I've heard of it, I would like to explore it :) – tabs_over_spaces Mar 15 '17 at 18:42