4

I've just started learning C++ a few days ago (coming from a C# background) and am going through the headache at the moment of getting to grips with pointers and references etc. (No, I've never used pointers in C# and I'm only using them now because it seems polymorphism depends on them in C++, joy.) I think I've worked it all out in my head now after reading some existing literature around SO and Wikipedia etc but I want to check if I've got it right here. Please correct me if anything below is incorrect.

I gather when applied as a prefix & gets the address of a variable, whereas * dereferences it (if the variable is a pointer). Dereferencing a variable which is not a pointer is illegal, thus the operators define transitions up and down a ladder of 'pointerness' starting at the raw object level and continuing indefinitely in the & direction. In this sense the operators are inverses in this context.

Now, I also see that when declaring variables we can equivalently write

int *newVariable;

and

int* newVariable;

and similarly with & (and indeed with any spacing around the * as it can equivalently resolve to either case).

The former is interpreted as the * operating on the variable as before: "Declare a variable 'newVariable' such that when it's dereferenced, it points to an int". This makes it an int pointer.

The latter is instead interpreted as the * operating on the type int, the meaning of which is defined as T* being a pointer to T: "Declare a variable 'newVariable' which is a pointer to an int".

This is suggestive that the same relations we have for them acting on a variable extend to when they act on a type. This has caused some confusion for me as it seems that unlike *, & is inconsistent and changes its behaviour here such that the former interpretation of the declaration above does not hold for & as well as *.

In this context, ie. operating on a type or on a variable in its declaration, it no longer appears to have the same meaning, and thus ceases to be an inverse operation to *. Here, & declares a variable as a reference to the type, which is orthogonal to the 'pointerness' ladder. You can have a reference to any type on the ladder, but you cannot have a pointer to a reference and you cannot have a reference to a reference. Thus the operator combination in a declaration must be a (possibly empty) string of *s possibly followed by a &.

The syntax for assigning a reference is the same as that for a raw object - if assigned to another reference then it is directed to point at the object the other reference points to. Also, a reference cannot be dereferenced with * - rather, every interaction with the reference is automatically dereferenced by the compiler so you can act on it as if it were the object itself.

Additionally, when passing variables through function arguments, the raw object is always shallow copied in memory to the new variable in the function scope if it's not a reference (in the case of a pointer, this just means an address is copied). If it is a reference, then the function just receives a new reference object pointing to the same thing as the reference it took in, so this works in much the same way as value types and reference types in .Net.

Does all of this sound roughly right?

Thanks! (I know this isn't exactly the first question on the topic...)

user2163043
  • 361
  • 1
  • 3
  • 14
  • 1
    `operator&` can be overloaded, which creates the rationale for `std::addressof()` – Ben Voigt Dec 29 '15 at 03:24
  • 2
    Also, **binary** `*` and `&` are unrelated, one's an arithmetic operator, the other is bitwise – Ben Voigt Dec 29 '15 at 03:25
  • 3
    The whitespace in the declaration is irrelevant. It's not even required; you could write `int*newVariable;` with precisely the same semantics as the other two. – rici Dec 29 '15 at 03:26
  • 1
    @rici: absolutely right, and `int* a, b;` means the same as `int *a,b;`, completely contrary to what the reasoning in the question would suggest. – Ben Voigt Dec 29 '15 at 03:27
  • When applied to regular function names, `*` and `&` are most peculiar. You could investigate: `#include ` — `int function(int n) { return n + 1; }` — `int main() { int (*funcptr1)(int) = &function; int (*funcptr2)(int) = function; int (*funcptr3)(int) = *function; int (*funcptr4)(int) = **function; assert(funcptr4 == funcptr1); return function(0) + (*funcptr1)(1) + (*function)(2) + funcptr4(3) + funcptr2(4) + funcptr3(5); }`. – Jonathan Leffler Dec 29 '15 at 03:34
  • In `int *newVariable;` , the `*` is not an operator. It's not operating on anything. Operators occur in expressions. This is a declaration. In both cases it declares a variable whose name is `newVariable` and whose type is `int *`. It does not make sense to talk about the "inverse operation" for something that is not an operator. – M.M Dec 29 '15 at 03:36
  • 2
    You can write `int x; cout << *&*&*&*&*&*&x;` but you can't write `int *&*&*&*&*&x;` . The symbols mean different things in a declaration than in an expression. – M.M Dec 29 '15 at 03:40
  • http://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in. – R Sahu Dec 29 '15 at 03:54

2 Answers2

3
int *newVariable;

and

int* newVariable;

Are just the same thing. so does:

int& newReference;
int &newReference;
int & newReference;

And once you declared the reference, you can think of it just like the actual variable itself. so you don't need particular operator to dereference it.

when passing variables through function arguments, the raw object is always shallow copied in memory to the new variable in the function scope if it's not a reference (in the case of a pointer, this just means an address is copied).

When passing a variable through function argument, it all depends how the function acquires that variable. i.e. the function definition:

so there's a difference between this:

void func(int parameter);

and this:

void func(int & parameter);

The first one just copies the parameter to function scope, so any change in the parameter, won't affect the actual variable you passed in. but in the case of second one, it's the reference which is acquired by func() so it's changes will affect your variable too.

Mehrdad
  • 1,186
  • 7
  • 15
  • *The first one just copies (deep copy) the parameter*... I dont think that the term *deep copy* is appropriate here. – A.S.H Dec 29 '15 at 04:16
  • @A.S.H I don't normally use that term, it supposed to show the OP, it's not just a shallow copy. it's an actual copy. but yes, I don't like it either. – Mehrdad Dec 29 '15 at 04:23
  • imho "shallow copy" and "deep copy" aren't useful terms when talking about C++ – M.M Dec 29 '15 at 04:53
  • Whether the copy is *shallow* or *deep* depends entirely on the implementation of the copy constructor for the object. The one supplied automatically by the compiler is a shallow copy, pointers are copied without copying the contents they point to. – Mark Ransom Dec 29 '15 at 14:49
  • @MarkRansom I think your comment is irrelevant here. Since the default (compiler-provided) copy constructor does copy everything as it is. and I think the source of this irrelevancy is because I added MyType instead of int. So I'll change it to remove this. – Mehrdad Dec 29 '15 at 15:22
2

Are the C++ & and * operators inverses in all contexts?

No, not at all. These operators have different meanings depending on context, and those meanings are not always inverses.

In an expression context * is multiply or dereference, while & is address-of. Dereference and address-of are inverses, and that's really the only case where * and & are invereses

In a declaration context, * is pointer and & is reference, and these are not at all inverses.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226