Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding references vs. pointers. Why does this work?

It has become apparent through a series of SO questions today that I have only a poor understanding of the true nature of pointers, references, and values.

Consider the following code:

int* p = new int(3);
int& r = *p;

cout << " p = " << p << "\t*p = " << *p << endl;
cout << "&r = " << &r << "\t r = " << r << endl;

delete p;

cout << "&r = " << &r << "\t r = " << r << endl;

int v = 4;
r = v;

cout << "&r = " << &r << "\t r = " << r << endl;

The output for this is

 p = 0x1001000b0    *p = 3
&r = 0x1001000b0     r = 3
&r = 0x1001000b0     r = 3
&r = 0x1001000b0     r = 4

The thing I don't understand is why the second time I print the value of the reference that I don't get an error. The pointer corresponding to the value of the reference has already been deleted. From my previous question, I had almost convinced myself that any statement such as r = x makes a copy of x in the place of the value that r refers to. However, if this was the case then the p and &r would be different addresses, right? If I've already called delete on 0x100100b0, then how can I keep using it?

True or false: A reference is the same thing as an alias to the value at an address.

True or false: If you delete a pointer to the same address as a referenced value resides, (as I do above), then no undefined behavior will occur, and no one will ever overwrite that address as long as the reference exists.

like image 421
JnBrymn Avatar asked Dec 08 '10 18:12

JnBrymn


3 Answers

A reference is an alias for an object. If that object's lifetime has ended, the reference to that object ceases to be valid.

In your example, using referecen r after the delete p operation results in undefined behavior - the fact that it 'appears' to work is just a coincidence - using r after that point is just as invalid as using *p after that point (and will likey result in the same behavior.

For example, modifying your program to do the same thigns with *p as with r looks like the following:

#include <iostream>
using namespace std;

int main()
{
    int* p = new int(3);
    int& r = *p;

    cout << " p = " << p << "\t*p = " << *p << endl;
    cout << "&r = " << &r << "\t r = " << r << endl;

    delete p;

    cout << " p = " << p << "\t*p = " << *p << endl;
    cout << "&r = " << &r << "\t r = " << r << endl;

    int v = 4;

    *p = v+1;
    cout << " p = " << p << "\t*p = " << *p << endl;

    r = v;
    cout << "&r = " << &r << "\t r = " << r << endl;
}

Output:

 p = 0x3f1730   *p = 3
&r = 0x3f1730    r = 3
 p = 0x3f1730   *p = 4134736
&r = 0x3f1730    r = 4134736
 p = 0x3f1730   *p = 5
&r = 0x3f1730    r = 4

You'll see that there's similar behavior for using *p after the object has been deleted (and it's just as invalid).

In all instances, accessing the object after it's been deleted is undefined behavior, whether that access occurs through an invalid pointer or an invalid reference.

like image 33
Michael Burr Avatar answered Nov 03 '22 05:11

Michael Burr


Even though you don't get an error, the results are still undefined. Undefined behavior means anything can happen, including your program appearing to continue to work correctly.

A reference is the same thing as an alias to the value at an address.

This is effectively true. It would be more correct to say that a reference is an alias for an object (not a value).

If you delete a pointer to the same address as a referenced value resides, (as I do above), then no undefined behavior will occur,

This is also true. There is no defined behavior until you attempt to use the reference. When you attempt to use the reference (for example, via &r), you get undefined behavior.

If you never attempt to use the reference after you destroy the object, there is no undefined behavior.

no one will ever overwrite that address as long as the reference exists.

No, this is not correct. As soon as the object is destroyed, any references or pointers to it are invalid and unusable. If you attempt to use a pointer or reference to a destroyed object, the results are undefined.

like image 176
James McNellis Avatar answered Nov 03 '22 06:11

James McNellis


True or false: A reference is the same thing as an alias to the value at an address.

Errr... true? Your terminology is what makes it unclear, but i think i got what you are asking.

True or false: If you delete a pointer to the same address as a referenced value resides, (as I do above), then no undefined behavior will occur, and no one will ever overwrite that address as long as the reference exists.

False. You are getting away with reading correct value after you unallocated memory only because your C++ implementation is not paranoid about memory management and you accessing it right after its "validness" expired.

You see, at compile time there is no way for compiler to predict you are gonna pull this dirty trick and at run-time memory security micromanagement is costly.

So, why this is bad practice and what can go wrong? 1) if in-between of your "delete p" and 3rd cout you do major memory-heavy operations with lots of "new" and "delete" calls - there is a fat chance an actual memory point "r" is referencing to will be compromised (its value changed or totally unavailable due to memory freeing to operating system - which will cause general protection fault and your app crash)

2) if you compile and run your app in some paranoid (or resource scarce) environment you will get a crash because system tracks what memory belongs to your app even at scale as low as "int" value.

If you want more details you can look for "c++ heap" and "c++ memory management" topics.

like image 38
Gleb Varenov Avatar answered Nov 03 '22 04:11

Gleb Varenov