0

Can you explain this behaviour to me, pls? Here the code:

int* b = new int;
const int MAX_AGE = 90;
b = (int*)&MAX_AGE;
std::cout << b << std::endl;
std::cout << &MAX_AGE << std::endl;
std::cout << *b << std::endl;
std::cout << MAX_AGE << std::endl;
std::cout << "........." << std::endl;
*b = 2; 
std::cout << *b << std::endl; // HERE I get 2, that's ok
std::cout << MAX_AGE << std::endl; // HERE I still get 90, why?
std::cout << b << std::endl; 
std::cout << &MAX_AGE << std::endl; 
Remo Laz
  • 3
  • 1
  • 1
  • 9
    Undefined behavior is undefined. If you tell the compiler a variable is going to be const and then try to change it by reaching behind the scenes with pointers, you're breaking the contract you have with the compiler. – scohe001 Feb 11 '20 at 20:56
  • 1
    https://en.cppreference.com/w/cpp/language/ub – Jesper Juhl Feb 11 '20 at 20:57
  • 2
    One of the oldest laws of computing is Garbage in produces garbage out. If you lie to the compiler, you're going to get a garbage program. – user4581301 Feb 11 '20 at 20:58
  • 1
    One of my [favorite answers](https://stackoverflow.com/questions/1239938/accessing-an-array-out-of-bounds-gives-no-error-why) dealing with what undefined behavior can lead to says "In general, whenever you encounter undefined behavior, anything might happen. The application may crash, it may freeze, it may eject your CD-ROM drive or make demons come out of your nose. It may format your harddrive or email all your porn to your grandmother. It may even, if you are really unlucky, appear to work correctly." Take that to heart and avoid UB at all costs. – fredrik Feb 11 '20 at 21:02
  • 1
    This is exactly why you should never use C style casts (as in `(int*)&MAX_AGE`). Rely on implicit conversions and `static_cast` when needed. Both would have given you an error message rather than compiling a program with undefined behavior without warning. – walnut Feb 11 '20 at 21:02
  • 2
    By the way, `b = (int*)&MAX_AGE;` leaked the `int` allocated at `int* b = new int;`. – user4581301 Feb 11 '20 at 21:05
  • Think about this scenario: The `const int` statement allows the compiler to place the number in the executable (usually read-only) or in a read-only device (like ROM or Flash). The expression `&MAX_AGE` says that the compiler can't place the value in a register because registers don't have addresses. So, what happens when you write to read-only memory???? – Thomas Matthews Feb 11 '20 at 22:06
  • If you want to get even more confused by what UB can do, try `*((int*)&MAX_AGE) = 2; int* b = (int*)&MAX_AGE;` [wat?!](https://godbolt.org/z/0TXjJw) – Ted Lyngmo Feb 11 '20 at 22:25
  • Verily, the number of ways you can make batsmurf crazy code compile is unmeasurable. – user4581301 Feb 11 '20 at 23:51

3 Answers3

5

The problem is that you lied to your compiler, and compilers are pretty good at exacting revenge on people who lie to them.

Specifically, on this line you told the compiler that MAX_AGE is changeable:

b = (int*)&MAX_AGE;

This is a lie, because you declared MAX_AGE to be a const. What happens next is called undefined behavior (UB): the compiler is free to produce any results, including complete nonsense, after your code triggers UB.

In your case, however, there is a pretty good explanation of what gets printed: knowing that MAX_AGE is another name for 90, the compiler has optimized std::cout << MAX_AGE << std::endl; to print 90, without looking up its value from memory.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    Demonstration: https://godbolt.org/z/jEjS5x . Go digging through the assembly output and you'll find a bunch of `mov esi, 90`s loading a literal 90 into the registers used by the calls to the `<<` operator. – user4581301 Feb 11 '20 at 21:09
  • "the compiler is free to produce any results, including complete nonsense, after your code triggers UB" - it has even more freedom than that. It's not just what comes after that is undefined. If a program contains UB *anywhere*, the *entire* program is undefined and the compiler is allowed to generate garbage for *any* parts of it. – Jesper Juhl Feb 11 '20 at 21:09
3

MAX_AGE is declared as const int. With your c-style cast you remove constness and then proceed to modify a const value. This is UB.

This is a prime example for why this is UB: Due to the constness of MAX_AGE the compiler knows that it won't change and can thus replace all occurences of it by the literal 90.

Sebastian Hoffmann
  • 2,815
  • 1
  • 12
  • 22
0

const tells the compiler that the variable MAX_AGE should be stored in the write protected region of the respective segment in memory. Armed with this knowledge the compiler can obviate the need to repeatedly read the same memory location. In other words the compiler might cache the constant value. That is why you see MAX_AGE showing up with the original value.

Anyway as has been already mentioned you shouldn't be confusing the compiler with your actual intentions. If you intend to store a variable in a write protected region then you shouldn't be modifying it.

pcodex
  • 1,812
  • 15
  • 16