I was messing around in C++ and wrote this code
#include <iostream>
int main() {
const int x = 10;
const int* ptr = &x;
*((int*)ptr) = 11;
std::cout << "ptr -> " << ptr << " -> " << *((int*)ptr) << std::endl;
std::cout << "x -> " << &x << " -> " << x << std::endl;
}
Interestingly, the output in the console is:
ptr -> 00CFF77C -> 11
x -> 00CFF77C -> 10
I checked the value at the memory address of ptr and the value is 11, so does this imply that const variables store the value in them, and not in memory? And if this were the case, why have a memory address storing the value?
Your code has Undefined Behavior.
x is a compile time constant, so the compiler expects it to not change value at runtime. As such, the compiler is allowed to optimize code by substituting uses of x's value with the literal value 10 at compile time.
So, at the point where << x is used, your compiler is emitting code for << 10 instead.
But, since your code is also using &x, the compiler is required to make sure that x has a memory address accessible at runtime. That means allocating a physical int and initializing its value to 10. Which you are then fiddling with in an unsafe manner, despite the fact that most compilers will likely place that int in read-only memory.
That is why you are seeing the output you see. But you are lucky your code is not crashing outright, trying to assign a new value to a constant at runtime.
The behavior of a program that writes to a const variable is undefined. It could print "10", it could print "11", it could print "3822", it could crash, or it could make demons fly out of your nose.
That said, in this case the behavior makes perfect sense. The compiler knows x is never allowed to change, so it transforms your std::cout << x to basically std::cout << 10.
Related
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?
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.
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.)
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 :)
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).
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;
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'
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.
Why does my code for overwriting const int variable work? Is it safe?
#include <iostream>
#include <cstring>
using namespace std;
int z = 5;
const int x = *(&z);
int main()
{
cout << "A:" << x << ", " << &x << endl;
int y = 7;
cout << "B:" << y << ", " << &y << endl;
memcpy((int*)&x, &y, sizeof(int));
cout << "C:" << x << ", " << &x << endl;
}
Output would be:
A:5, 0x600f94
B:7, 0x7a7efb68019c
C:7, 0x600f94
I am not sure if this has been asked before since I don't know what to search for in this situation.
Answering your questions:
It's not safe; should never be done, it's example of how not to use C.
It's based on undefined behaviour; that means specification doesn't give exact instruction how such attempt should be treated in the code.
Final answer to question why it works? The answer here is true for the GCC only as other compilers may optimise/treat const different way. You need to understand that technically const int x is a declaration of a variable with qualifier. That means (until it's optimised) it has it's place in the memory (in some circumstances in read-only section). When you removed the qualifier and give the address of the variable to the memcpy() (which is dummy library call unaware of memory protection) it makes attempt to write the new data to that address. If compiler puts that variable into read-only section (I faced that epic failure in the past) the execution on any Unix would end with segmentation fault caused by writing instruction violation memory protection of the read-only memory segment used by your program to hold constant data.
In C++ real constants are qualified by constexpr, however there are other implications.
I have an assignment that were supposed to evaluate some pointer manipulation expressions and memory leak situations in C/C++. There's one I'm stuck with:
unsigned int* pInt = (unsigned int*) 0x403004;
Right off the bat this is suspicious to me, but in the assignment this line is theoretically working, however running the program I'm get segfault right at this line.
The question is: Is this right or even is possible or the professor is just fooling us telling this is right? I've seen some examples and questions with string "hex" to int, but nothing regarding "pure hex" to int or int*
unsigned int* pInt = (unsigned int*) 0x403004;
Two things are suspicious here:
Unless, you are writing some specialized Software like device drivers or OS, or you are in some embedded or special system where memory is fixed, seeing memory address hardcoded is certainly suspicious. Your program will (at best) fail if it tries to access memory it doesn't have the access rights to.
On the right hand side, the compiler first deduces the value 0x403004 as in int and will correctly convert it to a pointer. Thus, your Segfault is probably as a result of the first point.
unsigned int* pInt = (unsigned int*) 0x403004;
Possible?: yes (compiles, builds just fine)
Is it right?: depends on what for. Evidently it is useful for illustration in a classroom assignment.
Is it recommended? no. It will invoke undefined behavior. You are creating a variable that points to a location in memory that you may or may not have rights to. If you never use it, fine. But if you do use it, the results are indeterminate.
it works fine only if that number represents an already allocated memory
eg:
#include <iostream>
int main()
{
int i = 7;
std::cout << "i: " << i << std::endl; // 7
std::cout << "&i: " << &i << std::endl; // in my case: 0018FF44
unsigned int* ptr = (unsigned int*)0x0018FF44; // it's ok
/*
this is just for explaining because the address of i may differ at anytime the program
launches thus 0018FF44 will no longer be assigned to i and consequently segfault.
the right thing is to make pointer points always to whatever i may take as an address.
to do so:
*/
//int* ptr = (unsigned int*)&i; // not i but the address of i
(*ptr)++;
std::cout << i << std::endl; // 8
// above we changed i through pointer ptr
int* pRandom = (int*)0xAB2FC0DE0; // this causes me segfault
*pRandom = 5; // segfault
std::cout << "*pRandom: " << *pRandom << std::endl; // segfault
std::cout << std::endl;
return 0;
}
Consider this code:
int& x=*new int;
Does RHS of the assignment actually dereference the newly-created pointer, leading to UB due to reading uninitialized variable? Or can this be legitimately used to later assign a value like x=5;?
As far as I can tell, none of what you've done involves undefined behavior.
It does, however, immediately create a risk of a memory leak. It can be quickly resolved (since &x would resolve to the address of the leaked memory, and can therefore be deleted) but if you were to leave scope, you would have no way of retrieving that pointer.
Edit: to the point, if you were to write
int& x=*new int;
x = 5;
std::cout << x << std::endl;
std::cin >> x;
std::cout << x << std::endl;
The code would behave as though you had simply declared x as int x;, except that the pointer would also be left dangling after the program exited scope.
You would achieve undefined behavior if you were to attempt to read the uninitialized variable before assigning it a value, but that wouldn't be untrue if x were stack allocated.
I would not advise doing this. Aliasing dynamic memory with a variable that looks static seems dangerous to me. Consider the following code
#include <iostream>
int* dynamicAlloc() {
int& x = *new int;
std::cout << "Inside dynamicAlloc: " << x << std::endl;
std::cout << "Modified: " << (x = 1) << std::endl;
return &x;
}
int main() {
int *dall = dynamicAlloc();;
std::cout << "dall (address): " << dall << std::endl;
std::cout << "dall -> " << *dall << std::endl; // Memory not cleaned
delete dall; // Don't forget to delete
return 0;
}
I get output like this:
Inside dynamicAlloc: -842150451
Modified: 1
dall (address): 00F642A0
dall -> 1
Note how dereferencing dall doesn't result in a segfault. It wasn't deallocated when x fell out of scope. Otherwise a segfault would have likely occurred.
In conclusion:
int& x makes x an alias for the dynamically allocated memory. You can legitimately modify that memory, but it will not cleanup as it falls out of scope automatically easily leading to potential memory leaks.
I have read, it is undefined in some C standards (perhaps 99?) what happens when a const is modified. But a student presented me with some code, which I modified.
I cannot see anything special about the address of the constant variable a. I verified that &a and b are the same, so the compiler is not subtly pointing to some other location.
Yet when I assign *b, the const value does not change.
I am not running with optimization. When I compile with the -g flag to debug and step into the code, I get the results I expect (the memory location of the variable a changes). Yet the code presented below does not reflect the updated value of a.
Is this that temps are now being placed in registers even in debug mode, with no optimization?
#include <iostream>
using namespace std;
int main(){
const int a = 15;
cout << a << '\n';
int * b= (int*)&a;
cout << &a << "\n";
cout << b << "\n";
*b = 20;
cout << *b << '\n';
cout << a << '\n';
int x = a;
cout << x << '\n';
x = *b;
cout << x << '\n';
return 1;
}
This is also undefined behavior in C++, we can see this by going to the draft C++ standard section 7.1.6.1 The cv-qualifiers paragraph 4 which says:
[...]any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
Undefined behavior means the results are unpredictable which effectively means any result is possible even ones that at first glance defy intuition.
A quick experiment with godbolt using -O0 so there is no optimization going on shows the compiler is just using the literal value 15 for a instead of retrieving it from memory and printing that out:
movl $15, %esi #,
So the compiler is performing constant folding since it assumes that since a is constant it can just use the value 15 anywhere it sees a. Which is completely reasonable since you told it a was constant.