I have the following code:
int main(void) {
const int a = 2;
int *p = (int *)&a;
++*p;
cout << a << endl << *p << endl;
cout << &a << endl << p << endl;
return 0;
}
pointer point to const int a but when I change *pointer. *p = 3 a = 2;
While p and a have the same address.
I don't know how it create to this result.
Can anyone explain for me. Thanks!
You're not allowed to modify const objects. Modifying a const object (through a non const pointer) has undefined behaviour. UB means that anything may happen. Having undefined behaviour is a programmers mistake.
While it's mostly pointless to reason about UB, in this case the observed behaviour is likely due to constant folding
The answer is optimization. More precisely, constant propagation. Since a is declared to be constant and initialized to 2, the compiler will simply hard-code 2 when calling operator<<(ostream&, int) since it will result in faster code than reading a's contents again.
And it's legal: Since you've invoked undefined behavior, the compiler is free to do as it deems best.
Related
Is there a difference or preferred way of altering a pointer in a function? Take this snippet for example
void change(int** ptr) {
**ptr = 50;
*ptr = nullptr;
}
void change(int*& ptr) {
*ptr = 50;
ptr = nullptr;
}
int main()
{
int a = 5;
int* ptr = &a;
int** ptr2 = &ptr;
std::cout << "value: " << a << std::endl;
std::cout << "value: " << ptr << std::endl;
std::cout << "value: " << ptr2 << std::endl;
change(ptr2);
//change(ptr);
std::cout << "value: " << a << std::endl;
std::cout << "value: " << ptr << std::endl;
std::cout << "value: " << ptr2 << std::endl;
system("pause");
}
It seems like both of the change functions can achieve what I'm asking, but I'm not sure on the difference, other than the reference function doesn't create a copy of the pointer?
You can have a null pointer, but not a null reference.
You can supply the first with nullptr, and it will compile1, because there is an implicit conversion from std::nullptr_t to int**. If you tried to supply the second with nullptr, it will fail. The best match would be the conversion std::nullptr_t to int*, but you can't bind a mutable reference to a temporary.
I go over various situations for different ways of parameter passing in this answer, which also includes object ownership considerations (with std::unique_ptr)
1. The behaviour will be undefined, because you dereference it.
Is there a difference ... between ptr** and ptr*& ...
Yes. The former is a pointer, to an object of type ptr*, while the latter is a reference to an object of type ptr*.
or preferred ...
void change(int** ptr) {
**ptr = 50;
*ptr = nullptr;
}
void change(int*& ptr) {
*ptr = 50;
ptr = nullptr;
}
Advantages of a reference
You'll find that the reference is implicitly indirected, which makes the syntax simpler and that usually helps readability. This is particularly important in case of reference / pointer to a pointer. Same advantage applies to getting a reference: You don't need to use the address-of operator.
Another advantage is that a reference cannot be reassigned to refer to another object. This makes it simpler to read programs that use references, since you don't need to figure out whether a reference is reassigned within a long algorithm; You know that it isn't.
Another advantage is that a reference cannot be null. Therefore you don't need to check for such eventuality within the function. If you pass null to the first example function, the behaviour would be undefined, which is a very bad thing.
Disadvantages of a reference
You'll find that the reference is implicitly indirected. This can be confusing to people who are familiar with only value types (C programmers).
Another disadvantage is that a reference cannot be reassigned to refer to another object. This appears to not be a problem for your function since you don't need to refer to more than one object, but in general, there are situations where it would be useful.
Another disadvantage is that a reference cannot be null. This appears to not be a problem for your function which is presumably never intended to handle such case. But in general, there are situations where you want to represent a referential relation to non-existing object.
Preferences are personal, but in my opinion, the advantages of the reference outweigh the disadvantages, except when you need one of the features that are not possible (nullability, re-assignment)
It's largely a matter of personal taste.
That said, when calling void change(int** ptr), because you'd tend to pass the address of something using &, it is clearer at the calling site that the "something" could be modified by the function. If you use the int*& ptr overload, it's not as clear as the calling syntax is identical for pass-by-value and pass-by-reference.
Aside from the above, I tend to use pointers as function parameters if nullptr is allowed, and references if not.
I have this piece of code:
const int x = 7;
const int &y = x;
int *z = (int*)&y;
*z = 8;
cout << x<< endl;
cout << y<< endl;
cout << *z<< endl;
Which produces the following output:
7
8
8
I know that one should not remove the const as I did above. However, why does x print out 7? I've only created a single var, so if the output shows 7 and 8, then then there should be 2 vars created. How come there be 2 different numbers printed.
I know that one should not remove the const as I did above.
Right. Doing so is undefined behaviour, which means anything can happen. (More precisely, creating z is not undefined behaviour; modifying x through it is.)
However, why does x print out 7? I've only created a single var, so if the output shows 7 and 8, then then there should be 2 vars created. How come there be 2 different numbers printed.
Reasoning about undefined behaviour is weird - but it can be useful to understand what is happening for debugging purposes. It this case, what has almost certainly happened, is that the compiler has optimized:
cout << x<< endl;
to
cout << 7 << endl;
because there is no way you can legitimately modify the value of x. You then illegitimately modify the value of x, and manage to print out the new value.
Important Note
Do not attempt to rely on this behaviour; as I said above, it is only useful for debugging purposes. If the compiler changes it might get better at optimization and just print 7 for all values (because *z = 8 is undefined behaviour), or it might put x in read-only memory (and cause a access violation), or it might end up printing 8 for all values, or anything else might happen.
However, why does x print out 7?
Modifying a const object has undefined behaviour.
Casting away const-ness and using the pointer to modify a const object has undefined behavior. You are seeing the result of that.
The standard even has almost the same example you posted.
[dcl.type.cv/4]
Except that any class member declared mutable can be modified, any
attempt to modify a const object during its lifetime results in
undefined behavior. [ Example:
const int ci = 3; // cv-qualified (initialized as required)
ci = 4; // ill-formed: attempt to modify const
int i = 2; // not cv-qualified
const int* cip; // pointer to const int
cip = &i; // OK: cv-qualified access path to unqualified
*cip = 4; // ill-formed: attempt to modify through ptr to const
int* ip;
ip = const_cast<int*>(cip); // cast needed to convert const int* to int*
*ip = 4; // defined: *ip points to i, a non-const object
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
For another example,
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
— end example ]
I'm still quite new to C++, but to me so far, there seems to be no difference between the two when it comes to applicability. The only difference that I'm aware of is that reference to const objects is bound to a temporary object whenever initialized with a literal or an expression.
But since the temporary exists as long as the reference that's bound to it exists, there seems to be no applicable differences between a reference to const and a const variable if they're both initialized by a literal/expression.
So is there any situation where you can only, or would rather, choose one over the other?
At function scope, there's no difference. The temporary bound to the reference will exist until the function returns.
However, for class members the reference will still be destroyed when the relevant function returns, and that relevant function is the constructor - not the destructor! The constructor does the initialization and therefore the temporary exists only in the constructor.
Things of course work correctly if you have a const int member.
const int &i means that i refers to another int, which cannot be modified through this reference. However that int could be modified elsewhere.
For example:
int j = 5;
const int &i = j;
const int k = 5;
cout << i << "," << k << endl; // same
j = 6;
cout << i << "," << k << endl; // different
You're right that the cast const int &i = 5; is very similar to const int i = 5;. However for class types the similarity fades.
There are no difference.
Imagine that reference is "another name for constant or variable".
In a response to my comment to some answer in another question somebody suggests that something like
void C::f() const
{
const_cast<C *>( this )->m_x = 1;
}
invokes undefined behaviour since a const object is modified. Is this true? If it isn't, please quote the C++ standard (please mention which standard you quote from) which permits this.
For what it's worth, I've always used this approach to avoid making a member variable mutable if just one or two methods need to write to it (since using mutable makes it writeable to all methods).
It is undefined behavior to (attempt to) modify a const object (7.1.6.1/4 in C++11).
So the important question is, what is a const object, and is m_x one? If it is, then you have UB. If it is not, then there's nothing here to indicate that it would be UB -- of course it might be UB for some other reason not indicated here (for example, a data race).
If the function f is called on a const instance of the class C, then m_x is a const object, and hence behavior is undefined (7.1.6.1/5):
const C c;
c.f(); // UB
If the function f is called on a non-const instance of the class C, then m_x is not a const object, and hence behavior is defined as far as we know:
C c;
const C *ptr = &c;
c->f(); // OK
So, if you write this function then you are at the mercy of your user not to create a const instance of C and call the function on it. Perhaps instances of C are created only by some factory, in which case you would be able to prevent that.
If you want a data member to be modifiable even if the complete object is const, then you should mark it mutable. That's what mutable is for, and it gives you defined behavior even if f is called on a const instance of C.
As of C++11, const member functions and operations on mutable data members should be thread-safe. Otherwise you violate guarantees provided by standard library, when your type is used with standard library functions and containers.
So in C++11 you would need to either make m_x an atomic type, or else synchronize the modification some other way, or as a last resort document that even though it is marked const, the function f is not thread-safe. If you don't do any of those things, then again you create an opportunity for a user to write code that they reasonably believe ought to work but that actually has UB.
There are two rules:
You cannot modify a const object.
You cannot modify an object through a const pointer or reference.
You break neither rule if the underlying object is not const. There is a common misunderstanding that the presence of a const pointer or const reference to an object somehow stops that object from changing or being changed. That is simply a misunderstanding. For example:
#include <iostream>
using namespace std;
// 'const' means *you* can't change the value through that reference
// It does not mean the value cannot change
void f(const int& x, int* y)
{
cout << "x = " << x << endl;
*y = 5;
cout << "x = " << x << endl;
}
int main()
{
int x = 10;
f(x, &x);
}
Notice no casts, nothing funny. Yet an object that a function has a const reference to is modified by that function. That is allowed. Your code is the same, it just does it by casting away constness.
However, if the underlying object is const, this is illegal. For example, this code segfaults on my machine:
#include <iostream>
using namespace std;
const int i = 5;
void cast(const int *j)
{
*const_cast<int *>(j) = 1;
}
int main(void)
{
cout << "i = " << i << endl;
cast(&i);
cout << "i = " << i << endl;
}
See section 3.4.3 (CV qualifiers) and 5.2.7 (casting away constness).
Without searching any further, § 1.9/4 in the C++11 Standard reads:
Certain other operations are described in this International Standard
as undefined (for example, the effect of attempting to modify a const
object).
And this is what you are trying to do here. It does not matter that you are casting away constness (if you didn't do it, the behaviour is well defined: your code would fail to compile). You are attempting to modify a const object, so you are running into undefined behaviour.
Your code will appear to work in many cases. But it won't if the object you are calling it on is really const and the runtime decided to store it in read-only memory. Casting away constness is dangerous unless you are really sure that this object was not const originally.
When I run this code on MS VS C++ 2010:
#include <iostream>
int main() {
const int a = 10;
const int *b = &a;
int *c = (int *)b;
*c = 10000;
std::cout << c << " " << &a << std::endl;
std::cout << *c << " " << a << " " << *(&a) << std::endl;
return 0;
}
The output is:
0037F784 0037F784
10000 10 10
The motivation for writing that code was this sentence from "The C++ Programming Language" by Stroustrup:
"It is possible to explicitly remove the restrictions on a pointer to const by explicit type conversion".
I know that trying to modify a constant is conceptually wrong, but I find this result quite weird. Can anyone explain the reason behind it?
Let's start with the obvious: some of this is platform and compiler dependent.
For starters, see this article on Explicit Type Conversion, and particularly:
A pointer to an object of a const type can be cast into a pointer to a
non-const type. The resulting pointer will refer to the original
object. An object of a const type or a reference to an object of a
const type can be cast into a reference to a non-const type. The
resulting reference will refer to the original object. The result of
attempting to modify that object through such a pointer or reference
will either cause an addressing exception or be the same as if the
original pointer or reference had referred a non-const object. It is
implementation dependent whether the addressing exception occurs.
So this, explains why it may let you modify the variable without bitching.
Note that you could achieve the same using the cast operators directly, as that's what the compiler will do for you as explained in this article on cast operators, with their order of precedence given.
However, the real trick here is in the memory model. A statically allocated variable like a const int a may actually never have any "physical" location in memory, and is just replaced in place at compile time. (I'm trying to put my finger on the actual reference for this, but so far the closest and best I could grab was this (very nice) SO answer to is memory allocated for a static variable that is never used? - If anyone finds the actual reference, please let us know.)
So here the compiler is simply humoring you, and trying to make some sense of your pointer arithmetic as much as it can, but in the end substitutes a actual values for the 2 last parts of your 2nd cout call.
The reason is that this is undefined behaviour.
The Stroustrup quote likely refers to the case where the object was not declared const but you have only a const pointer to it.
i.e. This is well-defined (using c-style cast as they appear in question):
int a{10};
const int* pa = &a;
int* b = (int*)pa;
*b = 5;
And this is undefined:
const int a{10};
const int* pa = &a;
int* b = (int*)pa;
*b = 5;
Attempting to modify an object declared const, however you get a non-const pointer to it, is UB.