-1

In c++ I have the next code

int main() {
    int i = 1;
    cout<<"i = "<<i<<endl; //prints "i = 1"

    int *iPtr = &i;
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 1"

    (*iPtr) = 12; //changing value through pointer

    cout<<"i = "<<i<<endl; //prints "i = 12"
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 12"

    system("pause");
    return 0;
}

Now the same code with constant integer i

int main() {
    const int i = 1;
    cout<<"i = "<<i<<endl; //prints "i = 1"

    int *iPtr = (int*)&i; //here I am usint a type conversion
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 1"

    (*iPtr) = 12; //changing value through pointer

    cout<<"i = "<<i<<endl; //prints "i = 1"
    cout<<"*iPtr = "<<*iPtr<<endl; //prints "*iPtr = 12"

    system("pause");
    return 0;
}

As you can see, in second case with constant integer, there are two different values for *iPtr and const i, but the pointer *iPtr shows to constant i. Please tell me what happens in the second case and why?

Vahag Chakhoyan
  • 873
  • 1
  • 10
  • 21
  • 8
    Undefined behaviour. – Quentin Nov 24 '19 at 17:31
  • `const` is to help you not making mistakes, if you insist to modify a const you still can, but it is not valid code – 463035818_is_not_an_ai Nov 24 '19 at 17:33
  • 3
    Don't use C-style casting and you can easily avoid running into such undefined behaviour. `static_cast` wouldn't compile, and you'd have to stop and think "why"? – Yksisarvinen Nov 24 '19 at 17:33
  • Please show your full output. It should show a warning at a minimum, plus all the print statements. I'd like to read the warning and view all prints together. – Gabriel Staples Nov 24 '19 at 17:34
  • 3
    There's a lot of stuff in C++ that makes it harder to hit yourself in the thumb with a hammer, but if you wind up and take aim at your thumb... You're probably going to hit your thumb. – user4581301 Nov 24 '19 at 17:35
  • 3
    If you lie to the compiler (`(int*)&i`), the compiler can do unexpected things. – Eljay Nov 24 '19 at 17:39
  • 1
    In case you wonder why the original value was printed after you changed it, the answer is probably that the compiler substituted any use of `i` with its value, which is `12`. Since it is a constant and no valid code is ever allowed to modify it, that simplification by the compiler is valid behaviour. Anyhow, in case that was still unclear, your code is broken and causes so-called "undefined behaviour" (search for that term!). – Ulrich Eckhardt Nov 24 '19 at 17:39
  • Does this answer your question? [C/C++ changing the value of a const](https://stackoverflow.com/questions/583076/c-c-changing-the-value-of-a-const) – machine_1 Nov 24 '19 at 17:41
  • Remember: Just because something *compiles* does *not* mean that it is valid code. – Jesper Juhl Nov 24 '19 at 17:44
  • 1
    I don't understand why anyone would downvote this question. It is well worded, well thought out, and shows the asker did work, and it includes a minimum runnable example exhibiting the behavior in question. Stackoverflowers who downvote this mind boggle me. – Gabriel Staples Nov 24 '19 at 17:53
  • 1
    @GabrielStaples these are people who think that I should have as much practice as they are. – Vahag Chakhoyan Nov 24 '19 at 17:57
  • 1
    /cc @GabrielStaples Food for thought for you as well there. In short: the site does not need this question _again_ and it would have been easy to find out why modifying `const` things produces weird results. (Though I did not downvote) – Lightness Races in Orbit Nov 24 '19 at 18:41

2 Answers2

4

Your second code has undefined behavior. You can't change const data via a pointer-to-non-const. You are lucky your code didn't simply crash outright when trying to modify a read-only value.

In any case, the result you are seeing is because the compiler knows that i is const and has a value that is known at compile time. So the compiler is able to optimize away i in the cout statement and use 1 directly instead. That is why you see 1 when printing i and see 12 when printing *iPtr.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 6
    "*You are lucky your code didn't simply crash*" Personally, I consider crashing as the most lucky UB manifestation. – Yksisarvinen Nov 24 '19 at 17:40
  • I'd leave out the discussion about "expecting a crash". I wouldn't: this notion of "storing things in read-only memory" is utterly archaic for situations like this; more likely the value was just "inlined" into the program instructions rather than being stored anywhere at all. Yes, the variable is later odr-used, but the program has undefined behaviour so that means next to nothing. Let's just focus on the contract violation and ignore low-level assumptions that we shouldn't be teaching people to make in the first place! – Lightness Races in Orbit Nov 24 '19 at 18:01
  • 1
    And you can also declare i as const volatile int, to avoid caching of value. – Vahag Chakhoyan Nov 24 '19 at 18:09
  • 2
    .... like that. No, Vahag, that is an assumption. You cannot "avoid" UB by doing clever tricks like that. You are not programming a computer: you are describing a program using an abstraction, and it has to be well-defined or all bets are off. Period. Full stop. – Lightness Races in Orbit Nov 24 '19 at 18:09
  • @LightnessRaceswithMonica it is of course still UB with volatile const but it does force a re-read on every read access so it will appear to work. This has a few major problems. Code that is UB but "works" is a potential future bug that's hard to find. The second issue is that the code disables some optimizations. – doug Nov 24 '19 at 18:37
  • @doug No, the program has undefined behaviour. Like Vahag, you are making assumptions. – Lightness Races in Orbit Nov 24 '19 at 18:38
  • @LightnessRaceswithMonica UB doesn't preclude it from "working" as it does on all the compilers I know of. It also doesn't mean that tomorrow it might not reformat my hard drive. Appearing to work, or even generating correct code for a specific compiler at a particular time is the worst kind of UB. – doug Nov 24 '19 at 18:42
  • @doug You stated that volatile const, presumably in _this_ program, "will force a re-read on every read access so it will appear to work". This is a false statement. A fake statement. A fallacy. A falsehood. Not sure how else to say it. _Could_ it make the program appear to be defined, by chance? Yes! But that's not what you said. You _cannot_ work around the undefinedness of a program by slapping a `volatile` on it and I cannot emphasise enough how important it is not to even accidentally persuade people that you can. – Lightness Races in Orbit Nov 24 '19 at 18:44
  • Also, the trivial-copyability of the class is part of its semantics, not an "optimisation". – Lightness Races in Orbit Nov 24 '19 at 18:45
  • @LightnessRaceswithMonica. I agree that slapping volatile in front of const doesn't change the fundamental UB ness. I hope I've been clear about that. What compilers generally do and why UB is a bigger problem when a program "works" was what I was trying to get across. – doug Nov 24 '19 at 19:05
2

You are trying to remove the const qualifier of your variable. In C++, you should use const_cast to do that.

However, const_cast can only be used in some precise circomstances: constness should only be removed from pointers/references to data which have been declared non-const at top level, otherwise the compiler may optimize the variable and modifying it through the pointer/reference would result in undefined behaviour.

For example, this is not legal :

const int i = 1;
const int *iPtr = &i;
int *iSuperPtr = const_cast<int*>(iPtr);
*iSuperPtr = 2; // Invalid : i is first declared const !!

But this is totally legal :

void modifyConstIntPtr(const int *iPtr) {
    int *iSuperPtr = const_cast<int*>(iPtr);
    *iSuperPtr = 2; // Valid : i is first declared non-const !!
}

void modifyConstIntRef(const int &iRef) {
    int &iSuperRef = const_cast<int&>(iRef);
    iSuperRef = 3; // Valid : i is first declared non-const !!
}
int main() {
    int i = 1;
    modifyConstIntPtr(&i);
    std::cout << i << std::endl;
    modifyConstIntRef(i);
    std::cout << i << std::endl;
}

This aspect of C++ is well detailed here: https://stackoverflow.com/a/357607/3412316)

Kiruahxh
  • 1,276
  • 13
  • 30