Search code examples
c++constantsreinterpret-cast

Is it possible to change value of a constant variable via reinterpret_cast?


all. I have read a code snippet from a book where the author tries to set the value of a register via direct memory access (he simulates this process). He used reinterpret_cast<volatile uint8_t*> for this. So, after reading his code, out of curiosity I have tried to apply the same code for a constant variable, and I experienced a very interesting output. I inserted the code below which is very simple:

int main()
{
  const std::uint8_t a = 5;
  
  const std::uintptr_t address = reinterpret_cast<std::uintptr_t>(&a);
  
  *reinterpret_cast<volatile uint8_t*>(address) = 10;
  
  std::cout << unsigned(a) << std::endl;

  return 0;
}

So, my purpose is to change the value of constant variable via direct memory access. I have written this code in Visual Studio C++ 2019 and compiled and run it. There was no any error or warning, but the output was very interesting. The value of a is printed as 5. So, I switched to the debug mode in order to see at each step what is happening. I will insert the images below:

Step 1 enter image description here

Step 2 enter image description here

Step 3 enter image description here

Step 4 enter image description here

Step 5 enter image description here

I am sorry to include debugging output as images, but I thought that it would be better to include images, so I will not miss any important detail. The thing that is interesting for me, how the program output is 5, while debugger clearly indicates the value of a is 10? (I even printed the addresses of a before and after the reinterpret_cast and they were the same.) Thank you very much.


Solution

  • The thing that is interesting for me, how the program output is 5, while debugger clearly indicates the value of a is 10?

    This depends entirely on the compiler. It could output 10, 5, crash, ... because it is undefined behavior.

    If you want to know why the output of the binary created by a particular compiler has a certain result for undefined behavior, you have to look at the generated output of the compiler. This can be done using e.g. godbolt.org

    For your code the output gcc (11.2) generates is:

            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     BYTE PTR [rbp-9], 5
            lea     rax, [rbp-9]
            mov     QWORD PTR [rbp-8], rax
            mov     rax, QWORD PTR [rbp-8]
            mov     BYTE PTR [rax], 10
            mov     esi, 5
            mov     edi, OFFSET FLAT:_ZSt4cout
            call    std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
            mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
            mov     rdi, rax
            call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
            mov     eax, 0
            leave
            ret
    

    Here you can see that the compiler correctly assumes that the value of a will not change. And replaces std::cout << unsigned(a) << std::endl; with std::cout << unsigned(5) << std::endl;:

            mov     esi, 5
            mov     edi, OFFSET FLAT:_ZSt4cout
            call    std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)
    

    If you remove the const from a the output is:

            movzx   eax, BYTE PTR [rbp-9]
            movzx   eax, al
            mov     esi, eax
            mov     edi, OFFSET FLAT:_ZSt4cout
            call    std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned int)