I'm having some trouble understanding how pointer dereferencing in C works. Let's look at this simple example:
struct Value {
int x = 0;
void Inc() { x ; }
};
int main(int argc, char* argv[]) {
Value* v = new Value();
v->Inc();
std::cout << v->x << std::endl; // prints 1, as I would expect
(*v).Inc();
std::cout << v->x << std::endl; // prints 2, but I would have expected it to print 1,
// as I thought (*v) would create a local copy of
// the original `Value` object.
Value v2 = *v;
v2.Inc();
std::cout << v->x << std::endl; // prints 2, as I would expect
I'm a bit confused here. I would assume that the 2nd and 3rd calls to Inc()
would be equivalent. Namely, that (*v).Inc()
would unfold into a temporary variable holding a copy of v
on the stack, and that Inc()
would then increment that copy on the stack of v
instead of the original v
. Why is that not the case?
Thanks
CodePudding user response:
In the (*v).Inc();
statement, the LHS of the .
operator is the result of the indirection of the v
pointer. This will be an lvalue expression referring to the object to which v
points. From this Draft C Standard (emphasis mine):
8.5.2.1 Unary operators [expr.unary.op]
1 The unary
*
operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
So, in this first case, no temporary object need be created and the Inc()
function is called on the original Value
object created by the new
operation.
However, in this statement: Value v2 = *v;
, you are declaring a separate Value
object and initialising it with a copy of the Value
pointed to by v
. Thus, any subsequent modifications to v2
will not affect the object referred to by v
.
CodePudding user response:
*pointer
just returns an object the pointer
points to, quoting [expr.unary.op]/1:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points
Value v2 = *v
is a form of initialisation, so it actually calls a constructor. This would be equivalent to Value v2{ *v }
(for this particular class).
For the part why *pointer
doesn't create a temporary, there are well-defined rules on when temporaries are created:
Temporary objects are created when a prvalue is materialized so that it can be used as a glvalue, which occurs (since C 17) in the following situations:
- binding a reference to a prvalue
- initializing an object of type std::initializer_list from a braced-init-list (since C 11)
- returning a prvalue from a function
- conversion that creates a prvalue (including T(a,b,c) and T{})
- lambda expression (since C 11)
- copy-initialization that requires conversion of the initializer,
- reference-initialization to a different but convertible type or to a bitfield.
plus some others scenarios for C 17. For this particular case the most important part is that indirection returns an lvalue, so there is no rule applicable to it if the expression doesn't partake in any other expression.