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.
Related
After compilation, what does the reference become, an address, or a constant pointer?
I know the difference between pointers and references, but I want to know the difference between the underlying implementations.
int main()
{
int a = 1;
int &b = a;
int *ptr = &a;
cout << b << " " << *ptr << endl; // 1 1
cout << "&b: " << &b << endl; // 0x61fe0c
cout << "ptr: " << ptr << endl; // 0x61fe0c
return 0;
}
The pedantic answer is: Whatever the compiler feels like, all that matters is that it works as specified by the language's semantics.
To get the actual answer, you have to look at resulting assembly, or make heavy usage of Undefined Behavior. At that point, it becomes a compiler-specific question, not a "C++ in general" question
In practice, references that need to be stored essentially become pointers, while local references tend to get compiled out of existence. The later is generally the case because the guarantee that references never get reassigned means that if you can see it getting assigned, then you know full well what it refers to. However, you should not be relying on this for correctness purposes.
For the sake of completeness
It is possible to get some insight into what the compiler is doing from within valid code by memcpying the contents of a struct containing a reference into a char buffer:
#include <iostream>
#include <array>
#include <cstring>
struct X {
int& ref;
};
int main() {
constexpr std::size_t x_size = sizeof(X);
int val = 12;
X val_ref = {val};
std::array<unsigned char, x_size> raw ;
std::memcpy(&raw, &val_ref, x_size);
std::cout << &val << std::endl;
std::cout << "0x";
for(const unsigned char c : raw) {
std::cout << std::hex << (int)c;
}
std::cout << std::endl ;
}
When I ran this on my compiler, I got the (endian flipped) address of val stored within the struct.
it heavily depend on compiler maybe compiler decide to optimize the code therefore it will make it value or ..., but as far i know references will compiler like pointer i mean if you see their result assembly they are compiled like pointer.
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
I'm trying to compile the following piece of code, but I'm getting a C2440 (visual studio) error. I've tried looking at other resources for help, but I can't find a good explanation.
int main()
{
int a = 100;
SomeFunction(&a);
}
void SomeFunction(const int* value)
{
//This line of code gives me the error.
int* variable = value;
cout << "Value is " << *Variable << " end" << endl;
}
I know I can solve this problem by using int* variable = const_cast<int*> (value);, but I still don't understand why the above code is causing a problem.
The error's quite clear - a pointer conversion can't remove a const qualifier; otherwise, you could violate the constness:
int* variable = value; // Not allowed - but if it were...
*variable = 42; // BOOM! changed a constant.
If you don't want to be able to change the value being pointed at, then keep it const
const int* variable = value;
If you do want to change it, then don't make it const in the first place:
void SomeFunction(int* value)
I know I can solve this problem by using const_cast
That's a bad idea - you'll get undefined behaviour if you abuse const_cast and try to modify a constant object. You should use const where you can, but not when you want to modify something.
The const int* means that you have the address of an int that you are not allowed to change.
An int* can be used to change the int it points to. Clearly that violates the above statement that you aren't allowed to change it.
const_cast does not actually "solve the problem". It lets you request to change something which cannot be changed. Such an attempt can cause your program to fail in uncontrolled ways (formally, undefined behavior). In your particular example, the variable a in main is not const, and so const_cast will successfully change it. But that creates close coupling and contradicts the promise made by a function whose signature is const int*.
int* variable = value; is wrong.
It should be,
int variable = *value
and
cout << "Value is " << variable << " end" << endl;
const int * means that the function guarantees that value at this address will not change, but if you can do:
int * variable = value;
Then you can also do
*variable=30;
In doing so, the guarantee of function that is of const pointer will be harmed.
#include <iostream>
#include <tchar.h>
void output(int *param)
{
std::cout << "Value: " << *param << std::endl;
};
int _tmain(int argc, _TCHAR* argv[])
{
int i = 34;
output(&i);
return 0;
}
obviously writes "Value: 34" to the console.
But if I make the following changes
...
void output(int **param)
{
std::cout << "Value: " << **param << std::endl;
}
...
output(&(&i));
...
I get a compile error "'&' requires l-value".
By the way, I even tried to make the following change:
output(&34);
Indeed this feels wrong ... somehow.
My question is: Why is this not allowed to use & at an r-value? Is there some reason on assembler level?
You are trying to get a reference to a r-value and that is basically not defined, since it is always a temporary value and actually never has an address on the stack/heap. That is why C++11 introduced r-value references, but that is a totally different subject to your question.
To get your code to compile your need to do the following:
int i = 34;
int* pi = &i;
output(&ip);
By "grounding" your reference in pi, you give the compiler a real address on the stack that can be given to output.
You're trying to get the address of an address, and an address is not an l-value. (You can very roughly think about l-values as values that can stand on the left side of an operation. Variables, and "named values" are l-values, for example)
Store the first address somewhere.
int number = 4;
int* firstAddress = &number;
int** secondAddress = &firstAddress;
output(secondAddress);