17

Code

#include <iostream>
using namespace std;

int main() {
    const int N = 22;
    int * pN = const_cast<int*>(&N);
    *pN = 33;
    cout << N << '\t' << &N << endl;
    cout << *pN << '\t' << pN << endl;
}

Output

22 0x22ff74

33 0x22ff74

Why are there two different values at the same address?

Michael Mrozek
  • 169,610
  • 28
  • 168
  • 175
L.Lawliet
  • 319
  • 3
  • 10
  • 3
    There are languages out there where executing `5 = 3` is a very dangerous thing. In C++ you must write `const_cast(static_cast(5)) = 3`. It expresses the same absurdity, and it's just as dangerous, but at least it throws an obstacle in the way. Never use `const_cast`. – Potatoswatter Aug 29 '10 at 06:55
  • The intended use of `const_cast` is to remove the const-ness from things which are not actually `const`. https://stackoverflow.com/a/19554871/18192 has some simple examples. – Brian Feb 21 '19 at 15:55

7 Answers7

27

Why are there two different datas at the same address?

There aren't. The compiler is allowed to optimize any mention of a const to be as though you had written its compile-time value in there.

Note that the compiler is also allowed to generate code that erases your hard disk when you run it if you do nasty tricks like writing to memory reserved for consts.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Fear mongering. The compiler is hardly more likely to erase your hard disk than to teleport your hard disk to the moon. The comment regarding erasure of the hard disk is an old chestnut. It is mostly nonsense. People should stop repeating it. Though abstractly correct, it runs contrary to experience and to the hardware-oriented spirit of C++. If such comments intimidate a newcomer against *experimentation* with undefined behavior, then how is the newcomer ever supposed to come to grasp registers, stacks and so forth? I insist that this chestnut makes bad advice. – thb Feb 21 '19 at 15:27
  • 2
    @thb: The point of the chestnut is not to describe a likely scenario but rather to indicate the *magnitude* of the possible undefined behavior; it's a convenient way to say that *really, literally anything can happen*. Would you prefer, say, "undefined behavior is allowed to write the contents of your private variables to screen memory*? Because I *have* written programs where overwriting constant memory by accident did that. – Eric Lippert Feb 21 '19 at 15:34
  • 5
    @thb: Or, "undefined behavior can cause your web browser security policy to stop working and allow arbitrary hostile code to run in the browser", because I've caused that bug too. We need some way to characterize **just how bad it can get**. What would you prefer? – Eric Lippert Feb 21 '19 at 15:36
  • I am pleased to see that, after years, you can still so quickly respond. Bravo. You are not wrong but consider: as far back as the 1980s, deliberately poking undefined behavior, I learned how my BASIC interpreter used hardware. Just today, I experimented with undefined to investigate the stack mechanics of an unfamiliar C++ feature. I have done so hundreds of times. Haven't you? My view is that the chestnut, so oft repeated, is apt to be misunderstood. That's all. If someone had warned me in 1982 not to risk smoking my father's eight-bit PC via undefined, I'd be the poorer for it today. – thb Feb 21 '19 at 16:17
  • 1
    My last comment failed to answer your question. Answer: I would prefer that you give exactly the answer you have given, since it is your answer. If you and I were however in a room together with a C++ beginner, I might add to your advice my perspective that experimentation with undefined behavior (UB) can edify the curious programmer in an environment not exposed in real time to hostile actors over the network. The C++ standard hardly says why a segfault (as opposed to other UB) might occur, for example, yet the exploration of segfaults is educational nevertheless. Just not in production. – thb Feb 21 '19 at 18:17
  • 3
    @thb: Oh, absolutely experimenting around can yield insights into implementation details. But it's possible to learn too much from that experience; I see questions here asking basically "why didn't C++ segfault when I did..." Knowing that one implementation crashes when you do bad things should not give an unearned sense of safety about other implementations. Overgeneralizing that the *worst* thing that happens when you do something bad is a crash, as is the case in C# or Java, leads to underestimating the possible consequences. – Eric Lippert Feb 21 '19 at 18:23
  • 2
    @thb: Also, you note that C is "hardware oriented", and I agree, but thinking that all C implementations are going to have *straightforward, unsurprising* consequences of undefined behaviour is assuming that optimization strategies stopped in the 1970s. My favourite article about the consequences of UB in modern compilers is https://blogs.msdn.microsoft.com/oldnewthing/20140627-00/?p=633 -- Raymond points out that UB can cause *effects to move backwards in time*. That's a *possible* behaviour and therefore not prohibited by UB. – Eric Lippert Feb 21 '19 at 18:27
14

You get undefined behavior on the line *pN = 33;, because you're modifying a const value. Anything can happen. Don't do it.


Likely, though, your compiler simply optimized. In the line:

cout << N << '\t' << &N << endl;

It knows N is a constant expression with the value 22, so just changes the line to:

cout << 22 << '\t' << &N << endl;

And on your next line, you fetch the value at the address of N, which you "set" to 33. (But really, all you did was remove any guarantees about the state of your program.)

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • I agree that this is the likely cause. Looking at the generated machine code would verify that. There is likely a `push 0x16` instruction being used for the `<<` operator call where L.Lawliet is expecting a `push [ebp-xxx]` instruction to be used instead. – Remy Lebeau Aug 29 '10 at 05:55
7

By stating that N is const, you have promised that you won't modify it. And then you go and modify it. This breaks one of the assumptions the compiler is making, and as a result, the program behaves incorrectly.

This is referred to as "undefined behavior" - after violating an assumption in the language, the behavior of the program is completely undefined. It need not have produced that output - it could've produced 33 for both, or 42, or crashed, or erased your hard drive, or summoned demons through your nasal passages. So, don't modify const values :)

bdonlan
  • 224,562
  • 31
  • 268
  • 324
4
int * pN = const_cast<int*>(&N);
*pN = 33;

Your code invokes Undefined Behavior1 because you are modifying the content of a const qualified variable/object.

1) Undefined Behavior: Behavior, such as might arise upon use of an erroneous program construct or of erroneous data, for which the Standard imposes no requirements.[Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
1

You can declare N as volatile, to force the compiler to fetch the current value from the variable's memory location.

volatile const int N = 22;

  • Yeah! Thanks to your answer I understood compiler thinks *const* wouldn't change and doesn't check the current value again. – Hareen Laks Apr 14 '19 at 16:42
0

const_cast in your code, just hands over a pointer 'Pn' to 'N' through which 'N' can be modified. The address of 'N' remains the same as the handed over pointer 'Pn'

Chubsdad
  • 24,777
  • 4
  • 73
  • 129
  • Downvoting because you do not answer the original question asking why the compiler outputs two different values when the same memory address is read using different means. – Remy Lebeau Aug 29 '10 at 05:51
0

I had the same question (Why am I not able to modify the contents of const int even with const_cast<int*>?) . I think answered here wonderfully by everyone. Just adding the assembly output from compiler

This is my original code

const int y = 7;
int* a = new int;
a = const_cast<int*>(&y); 
*a = 8;
std::cout << (int)*(&y) << std::endl;

This is the assembly output

std::cout << (int)*(&y) << std::endl;
00381CB6  push        offset std::endl<char,std::char_traits<char> > (03813C5h)  
**00381CBB  push        7**  
00381CBD  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (03900ACh)]  
00381CC3  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (03900B8h)]  
00381CC9  mov         ecx,eax  
00381CCB  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (03900BCh)]  

So the compiler will just replace the const variables with its actual value during compile time.

DharinS
  • 85
  • 1
  • 5