开发者

Understanding references vs. pointers. Why does this work?

开发者 https://www.devze.com 2023-01-29 07:13 出处:网络
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.

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 tha开发者_如何学JAVAt 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.


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.


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.


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.


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

True

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

int v = 4;
r = v;

compiles because r is a reference to an int.

However, when you delete whatever a reference points to and then try to use it, it is undefined behaviour!


Your first question is a little too vague to answer. What is an alias to the value at an address?

The second is definitely false. The memory where p was pointing was freed, and can be reused. It just happens that it hadn't changed yet. It is undefined to use r, and r = v dereferenced r and copied v's value there (which was also undefined -- could have crashed).

When something is undefined, that doesn't mean that you'll get an error. It means that any behavior you get is considered to specification. Crashing or working are both acceptable "undefined" behaviors.


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

False: It has nothing to do with address (that's an implementation detail).

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. It is undefined behavior to use a reference to a variable that is no longer alive. Different types of variable die at different times.

  • automatic storage duration variables die at the end of scope.
  • static storage duration variables die in reverse order of creation after main exits
  • dynamic storage duration variables die when delete is called on the memory they reside within.


The first one is true:

  • A reference is indeed an alias to a value. The specific implementation of the reference does not change that fact, and should not be relied upon.

The second is false:

  • If you delete a pointer to the same address, the only definitive source on what happens if you use the previously created reference is the language spec. I believe that the case in C++ is "undefined behavior". What happens in your example is that (I 'm hypothesizing here), the reference is implemented as a pointer and therefore it still points to the block of memory having the value 3 when interpreted as an int. That block has been released to the heap for possible future use, so there's no guarantee that what it contains is actually 3 or even a legal value for the referenced type (although of course every possible value is legal for an int -- it could have been a class type though). In practice, since the value was not overwritten with anything else, you still get a "meaningful" result when using the reference. However, this happens by sheer "coincidence".

Update: I was wrong in answering the first question, have reworked the answer.

0

精彩评论

暂无评论...
验证码 换一张
取 消