In Visual C++ 2017, when experimenting with what happens when you break the rules, I found that if I cast a const int to an int *, and then reassign a value to the int *, the debugger will change the value of the const, but the runtime execution won't.
This happens whether or not I run it in Debug mode or as a released executable. I'm aware it's undefined, but am looking for insight as to where these values are held, as they appear to be identical locations.
const int j = 100;
//int *q = &j; //Compiler disallows
int *q = (int*)&j; //By some magic, now allowed
*q = 300; //After this line, j = 300 in debugger
cout << "j = " << j << endl; //300 in debugger, 100 in console
//^ What is happening here? Where are the two values stored?
cout << "*q = " << *q << endl; //300 in both
//Output:
// j = 100
// *q = 300
Where are the two values being stored? This is like having one bucket that is simultaneously filled with two different liquids.
I'm aware that it's Undefined Behavior, but I was wondering if anyone could shed light on what is happening, internally.
If an object is in
const
storage, a compiler may at its leisure replace it with two or more objects that have the same content if it can tell that the addresses are never compared. A compiler would not generally be able to do this if both objects' addresses get exposed to the outside world, but may do so in cases where one object is exposed but the other(s) are not.Consider, for example:
A compiler processing
test
would be able to see that the value ofHeyPtr
is never exposed to outside code in any way, and on some platforms might benefit from having thetest
function use its own copy of the string. On a platform where addresses are 64 bits, iftest
doesn't include its own copy of the string, then eight bytes would needed to contain the address ofHey
. The four bytes needed to store an extra copy of the string would cost less than the eight bytes needed to hold the address.There are a few situations where the Standard offers guarantees that are stronger than programmers generally need. For example, given:
Unless a program happens to compare
foo
(or an address derived from it) withbar
(likewise), using the same storage for both objects would save 16 bytes without affecting program semantics. The Standard, however, provides no means by which a programmer could indicate that code either won't compare those addresses, or would not be adversely affected if they happen to compare equal, so a compiler can only make such substitutions in cases where it can tell that a substituted object's address won't be exposed to code that might perform such comparisons.