Am I understanding pointers and references correctly?

Aug 21, 2016 at 1:31am
I'm trying to see if I have a basic understanding of pointers and references, so my apologies if I don't.

In example 1 and 2, I'm passing a reference of num. So I'm not copying data, I'm passing the memory address of num (in both examples), which I'm assuming is great for large objects.

When I printed num after calling test(), I noticed a difference: example 2 does not let me change the value. Are there any other differences, because they seem very similar aside from that only detail?

1
2
3
4
5
6
7
8
9
10
11
  // Example 1
  void test(int &num) {
     num += 5  
  }

  int main() {
     int num = 5;
     test(num);
     std::cout << num << "\n"; // returns 10
     return 0;
  }


and...

1
2
3
4
5
6
7
8
9
10
11
  // Example 2
  void test(int *num) {
     num += 5  
  }

  int main() {
     int num = 5;
     test(&num);
     std::cout << num << "\n"; // returns 5
     return 0;
  }
Last edited on Aug 21, 2016 at 1:41am
Aug 21, 2016 at 1:42am
A reference is not a pointer (address). A reference is an alias.

When you change a reference to a value, you are changing the original value.
When you change a pointer to a value (the address), you are changing the pointer, not the pointed-to value.

If, in example 2, you want to add 5 to the num declared in the main function, you should write
1
2
3
void test(int *num) {
     *num += 5  
}

Where * is the dereference operator, which tells the computer to access/modify the pointed-to value.

You are correct in that you aren't copying the value of either `num' in either example; it is good for large objects. Note that passing a value by reference or pointer and not modifying can be surprising, so you should always mark your function parameters as const as possible.
Last edited on Aug 21, 2016 at 1:52am
Aug 21, 2016 at 2:22am
@mbozzi thank you for your detailed answered. I understood this explanation better than the replies on Stack Overflow. I won't forget to mark my function parameters with const.

I do have one more question if you're still around and don't mind:

What if I have a class, and I want to store a reference to a struct in that class?

1
2
3
4
5
6
7
8
class Character {
public:
   Character(const Transform &position) {
       m_pos = position;
   }
private:
 Transform m_pos;
};


In this example, would I be saving a reference of the Transform I passed?
Aug 21, 2016 at 4:07am
No, you'd be making a copy!
m_pos is a Transform, not a Transform&.

If you want to store a reference, you need to change the declaration of m_pos to be (line 7) Transform &m_pos;, but that's not all. Since references cannot be null, i.e., they always refer to a real object, they have to be initialized with a value.

Member objects and some other stuff is initialized before the code making up the constructor body is run. The constructor body is the stuff on line 4 of your example above. If you need to do something other than "default initialize" a member, you need to do so in the constructor initializer list.

The syntax looks like this:
1
2
3
4
5
6
7
8
9
class Character {
public: 
  Character(const Transform &position) 
    : m_pos(position) { 
    /* You don't need to do anything here: `m_pos' now refers to `position' */
  }
private:
  Transform &m_pos;
};


This problem is similar to what happens if you take your snippet above but mark m_pos const. You can't just assign to a constant value, but you can initialize it: this is the difference between calling an assignment operator in the constructor (m_pos = position) and actually initializing the value when it's constructed.

In other words, the difference is like that between
int const foo = 5; or int &baz = some_int; and
int const foo; foo = 5; /*oops! doesn't compile!*/ or int &baz; /*oops! doesn't compile!*/ baz=some_int;.

Edit:
Make sure the referred-to value exists the next time you use it. Once m_pos is initialized, nothing stops it from being destroyed and leaving this class with a "dangling reference".

The issue is that this class doesn't control the lifetime (doesn't "own") the value to which m_pos refers. Now it's the caller's problem to make sure that the referred-to value still exists.

At some point you should look into "smart pointers", which help enforce those ownership semantics and make things easier in general.
Last edited on Aug 21, 2016 at 4:13am
Aug 21, 2016 at 5:01am
Thanks again @mbozzi, awesome answer. I've got a better understanding now :)
Last edited on Aug 21, 2016 at 5:02am
Aug 21, 2016 at 5:58am
>> That is not true. ... *((int*)nullptr) ...

Dereferencing a null-pointer engenders undefined behaviour

undefined behaviour: Renders the entire program meaningless
http://en.cppreference.com/w/cpp/language/ub
Aug 21, 2016 at 6:54am
Surely your memory fails you, gentleguy/NaughtyAlbatross/closed_account_5a8Ym39o6.


http://www.cplusplus.com/forum/beginner/195371/#msg940769
Aug 21, 2016 at 7:39am
closed account (jN09E3v7)
0 to 123? I wonder if a 64 bit system gives a different answer to the *nullptr problem @gentleguy alerts us to. You would need to derefence it if you are checking for null as in a while loop eg while (!null) equivalent
Last edited on Aug 21, 2016 at 7:40am
Topic archived. No new replies allowed.