I have a big confusion about how a c++ compiler treating with a const variable
const int constfunc()
{
return 7;
}
const int u1 = constfunc();
int* pu1 = const_cast<int*>(&u1);
*pu1 = 10;
cout << u1 << endl;
cout << *pu1 <<endl;
The result of precending code is:
10
10
While I try to assign a literal value to u1 variable neither than a const function, I mean:
const int u1 = 7;
The result will be changed:
7
10
I have really two confusions:
1- I made a little change but the result different?
2- As I know a const_cast remove a const restriction on a const pointers which point to none const variables, but in my sample1 I can modify value of const variable "u1"?
int* pu1 = const_cast<int*>(&u1);
removes the const restriction.
*pu1 = 10;
calls for undefined behavior.
Thus the result of
cout << u1 << endl;
cout << *pu1 <<endl;
won't be predictable by the means of c++ standards.
Here's a lil' illustration how it goes:
So just write clean code and don't try to undermine compiler errors using cast expressions, unless you're a 100% sure what you are doing.
This confusion is related to a compiler's optimization, i.e:
const int u1 = 7;
This tell compiler the u1 variable can't be change and can be evaluated at compile time, so you can replace each u1 in the code by it's value (7), i.e printing code will be modified by the compiler as:
cout << 7 << endl;
When you call an address of the u1 variable:
int* pu1 = const_cast<int*>(&u1);
The compiler will automatically create a storage for const variable and put it's info in a symbol table, so the result is 7 10.
Another case, when you write:
const int constfunc()
{
return 7;
}
const int u1 = constfunc();
This tells the compiler the u1 variable can't be change and u1 will be evaluated at runtime time neither than compile time, because it's rvalue is result of the constfunc execution, so no optimization here and the u1 variable will allocate a memory storage and printing u1 code will still as it to fetch its value from a memory:
cout << u1 << endl;
Note1: Put constexpr specifier to the constfunc function will enforce the compiler to evaluate the u1 variable at compile time neither run time so the result will be changed as 7 10.
Note2: Removing const restriction by const_cast may be undefined behavior
Related
After coming across something similar in a co-worker's code, I'm having trouble understanding why/how this code executes without compiler warnings or errors.
#include <iostream>
int main (void)
{
unsigned int u = 42;
const int& s = u;
std::cout << "u=" << u << " s=" << s << "\n";
u = 6 * 9;
std::cout << "u=" << u << " s=" << s << "\n";
}
Output:
u=42 s=42
u=54 s=42
First, I expect the compiler to issue some kind of diagnostic when I mix signed/unsigned integers like this. Certainly it does if I attempt to compare with <. That's one thing that confuses me.
Second, I'm not sure how the second line of output is generated. I expected the value of s to be 54. How does this work? Is the compiler creating an anonymous, automatic signed integer variable, assigning the value of u, and pointing the reference s at that value? Or is it doing something else, like changing s from a reference to a plain integer variable?
References can't bind to objects with different type directly. Given const int& s = u;, u is implicitly converted to int firstly, which is a temporary, a brand-new object and then s binds to the temporary int. (Lvalue-references to const (and rvalue-references) could bind to temporaries.) The lifetime of the temporary is prolonged to the lifetime of s, i.e. it'll be destroyed when get out of main.
I'm curious about why my research result is strange
#include <iostream>
int test()
{
return 0;
}
int main()
{
/*include either the next line or the one after*/
const int a = test(); //the result is 1:1
const int a = 0; //the result is 0:1
int* ra = (int*)((void*)&a);
*ra = 1;
std::cout << a << ":" << *ra << std::endl;
return 0;
}
why the constant var initialize while runtime can completely change, but initialize while compile will only changes pointer's var?
The function isn't really that relevant here. In principle you could get same output (0:1) for this code:
int main() {
const int a = 0;
int* ra = (int*)((void*)&a);
*ra = 1;
std::cout << a << ":" << *ra;
}
a is a const int not an int. You can do all sorts of senseless c-casts, but modifiying a const object invokes undefined behavior.
By the way in the above example even for std::cout << a << ":" << a; the compiler would be allowed to emit code that prints 1:0 (or 42:3.1415927). When your code has undefinded behavior anything can happen.
PS: the function and the different outcomes is relevant if you want to study internals of your compiler and what it does to code that is not valid c++ code. For that you best look at the output of the compiler and how it differs in the two cases (https://godbolt.org/).
It is undefined behavior to cast a const variable and change it's value. If you try, anything can happen.
Anyway, what seems to happen, is that the compiler sees const int a = 0;, and, depending on optimization, puts it on the stack, but replaces all usages with 0 (since a will not change!). For *ra = 1;, the value stored in memory actually changes (the stack is read-write), and outputs that value.
For const int a = test();, dependent on optimization, the program actually calls the function test() and puts the value on the stack, where it is modified by *ra = 1, which also changes a.
From another post, I test the example regarding const X& x in function. This link What does “const X& x” mean? says "It means x aliases an X object, but you can’t change that X object via x." But below example shows we can modify X object? Why?
#include <iostream>
using namespace std;
int a = 1;
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
cout << "n: " << n << endl;
//f(); // Might change the value of whatever n refers to
}
int main()
{
DoWork(a);
cout << "a: " << a << endl;
}
Follow up question:
For example, we have a function return const reference, why can it be assigned to non-const reference?
const X& tmp = get();
X& other = tmp; //this works, but why? this get rids of const X& in tmp?
I think you have confusion about const and references.
Firstly what is reference `
it's another name(alias) for an object.
About what's written in function parameters means that n is a const reference. It means you can't change a through n.(or in the simple sense, n can't be on the left side of the assignment operator. It also means that n is bounded to the object permanently.
n = n+2; // This is an error as n is const and hence you can't use n to modify the object it is pointing.
a = n+2; // here you are not using `n` to modify. So this is ok.
int x = 10 ;
n = x; // error as n is bounded to `a` so it can't be bounded again to any other variable x. You can see `n` is on left which is an error.
Now if you do this
cout<<a<<"\t"<<n<<endl; // Output 3; (1+2)
About follow up question. (Read it carefully)
Let us suppose a general scenario.
const int ci = 1024;
int &r2 = ci; // error: non const reference to a const object
The same is the case with your function question. You must be wondering why we can't do such a thing.
See here c1 is const so we can't change it. Now suppose(JUST SUPPOSE) the following code would have been a legal code. Then r2 will be a reference to c1.
Now, As i stated reference is other name of an object. It means if we do arithmetic on r2 it will be done on ci.
r = r+2; // it means ci = ci + 2 ;
but see ci was const(it shouldn't be altered but we altered it.)
So we need to do something like.
const int ci = 1024;
const int &r2 = ci; // error: non-const reference to a const object
This ensures that we can't do
r = r +2; which ensures/preserves the constness of ci.
get() is returning const so to preserve it's constness the variable used to receive from function must be const. So, the part which you said is working is an error as per standard, may be it's your compiler extension.
Keep in mind that const X& essentially means that the compiler imposes restriction that you cannot mutate the value reference via the reference (as the page you linked says). The actual value referenced by the reference is purely determined at runtime and the compiler cannot infer that you are mutating the value referenced by x when it sees the line a = n * 2;.
A small change to your code can help what is going on,
#include <iostream>
using namespace std;
int a = 1;
int b = 1; // new variable
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
cout << "n: " << n << endl;
//f(); // Might change the value of whatever n refers to
}
int main()
{
// Whether `x` in `DoWork` refers to `b` or `a` is purely determined at runtime.
if (rand() % 2 == 0)
DoWork(a);
else
DoWork(b);
cout << "a: " << a << endl;
}
In other words, the compiler cannot be sure of what values references will point to. In C++, references can even point to invalid memory,
int *a = 0;
int &ref = *a;
cout << ref; // runtime error
Hope this helps.
Edit:
Regarding the followup question, what compiler are you using? Any standard compiler would refuse to compile the code.
How is it possible that the value of *p and the value of DIM are different but the have the same address in memory?
const int DIM=9;
const int *p = &DIM;
* (int *) p = 18; //like const_cast
cout<< &DIM <<" "<< p << '\n';
cout << DIM << " " << *p << '\n';
You're changing the value of a const variable, which is undefined behavior. Literally anything could happen when you do this, including your program crashing, your computer exploding, ...
If a variable is supposed to change, don't make it const. The compiler is free to optimise away accesses to const variables, so even if you found a successful way to change the value in memory, your code might not even be accessing the original memory location.
It is a compiler optimization. Given that DIM is a constant, the compiler could have substituted its known value.
The code below does what you meant to do... as mentioned in other posts, if you mean to change the value of an variable, do not define it as const
#include <stdio.h>
int main()
{
int d= 9;
int *p_d=&d;
*p_d=18;
printf("d=%d\np_d=%d\n",d,*p_d);
return 0;
}
This code prints
d=18
p_d=18
My problem is, why first part of the code does not work while the second one works. Non-const pointer should modify the const value using the const_cast previously but with integers this trick does not work. Could you explain why that happens?
const int i = 5;
cout << i << endl; //output: 5
int *ptr_i = const_cast<int*>(&i);
*ptr_i = 100;
cout << i << endl; //output : 5
const double d = 5;
cout << d << endl; //output : 5
double *ptr_d = const_cast<double*>(&d);
*ptr_d = 100.;
cout << d << endl; //output : 100
Modifying a const variable is undefined behaviour:
n4296 §7.1.6.1/4
Except that any class member declared mutable (7.1.1) can be modified,
any attempt to modify a const object during its lifetime (3.8) results
in undefined behavior.
const_cast is generally for communicating with non-const-correct APIs or casting away volatile qualifiers; it shouldn't be used like this.
Non-const pointer should modify the const value using the const_cast previously but with integers this trick does not work.
No, non-const pointer modifying the const value is undefined behavior. It is not supposed to work.
Could you explain why that happens?
Since this is UB, the compiler is free to do anything at all here, and "anything at all" means the code will happen to work in the case of int only (at least, for your compiler).
TLDR: undefined behavior is undefined.