14

After reading many posts about this, I want to clarify the next point:

A* a = new A();
A* b = a;

delete a;

A* c = a; //illegal - I know it (in c++ 11)
A* d = b; //I suppose it's legal, is it true?

So the question is about using the value of copy of deleted pointer.

I've read, that in c++ 11 reading the value of a leads to undefined behaviour - but what about reading the value of b?

Trying to read the value of the pointer (note: this is different to dereferencing it) causes implementation-defined behaviour since C++14, which may include generating a runtime fault. (In C++11 it was undefined behaviour) What happens to the pointer itself after delete?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Programmer1234
  • 207
  • 2
  • 8
  • Dereferencing a pointer to invalid memory is always *undefined behavior* - however your code does not dereference any of those pointers – UnholySheep May 25 '17 at 13:40
  • You can assign 'a' or 'b' to other pointers, but in every case it will point to the same memory location which has been freed, so it's most likely to crash if you dereference any of the pointers. In practice it's undefined behavior – AlexG May 25 '17 at 13:40
  • 1
    Why would `d=b` be legal, `b` holds the same exact value as `a`, which is illegal? – Sergey Kalinichenko May 25 '17 at 13:40
  • "A* c = a; //illegal - I know it" It's not illegal to copy a deleted pointer. By the end of your snippet, all pointers point to a deleted instance and can't be used, but there is no undefined behavior here. – François Andrieux May 25 '17 at 13:40
  • Not illegal at all. Just a lot of trouble. Both the assignments after the delete will only cause you a headache later. – Joseph Willcoxson May 25 '17 at 13:41
  • 1
    `A* c = a;` is legal - just a bit stupid. – Richard Critten May 25 '17 at 13:41
  • 1
    @dasblinkenlight many people think that `delete` modifies pointer itself somehow hense this ideas. – Slava May 25 '17 at 13:55
  • Implicit conversion to a pointer to a virtual base class may very well crash the program. – Arne Vogel May 26 '17 at 14:51

3 Answers3

35

Both:

A* c = a;
A* d = b;

are undefined in C++11 and implementation defined in C++14. This is because a and b are both "invalid pointer values" (as they point to deallocated storage space), and "using an invalid pointer value" is either undefined or implementation defined, depending on the C++ version. ("Using" includes "copying the value of").

The relevant section ([basic.stc.dynamic.deallocation]/4) in C++11 reads (emphasis added):

If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined.

with a non-normative note stating:

On some implementations, it causes a system-generated runtime

In C++14 the same section reads:

If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior.

with a non-normative note stating:

Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault

Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • Interestingly, even post C++14 (current draft) there is: `Note: the effect of using an invalid pointer value (including passing it to a deallocation function) is undefined`. It is unclear to me whether "undefined effect" means undefined or implementation defined behaviour. Although it is only a note and therefore not normative. – eerorika May 25 '17 at 14:36
  • @Programmer1234 I marked the one you linked as duplicate of this one, since the explanation is clear and all the relevant stuff is properl cited. – πάντα ῥεῖ May 25 '17 at 14:36
  • 1
    @user2079303: The new draft seems to move the details to `[basic.stc]/4`; but the effect remains implementation defined: "When the end of the duration of a region of storage is reached, the values of all pointers representing the address of any part of that region become invalid pointer values. Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer has implementation-defined behavior." – Mankarse May 25 '17 at 15:19
  • ... The Note in `[basic.stc.dynamic.safety]/4` lacks this nuance, presumably for brevity. – Mankarse May 25 '17 at 15:21
1

These 2 lines do not have any difference (meaning legality for C++):

A* c = a; //illegal - I know it (in c++ 11)
A* d = b; //I suppose it's legal, is it true?

Your mistake (and it is pretty common) to think if you call delete on a it makes it any different than b. You should remember that when you call delete on a pointer you pass argument by value, so memory, where a points to after delete is not usable anymore, but that call does not make a any different than b in your example.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • added a link at the end - that is why I think that A* c = a; is illegal – Programmer1234 May 25 '17 at 14:12
  • This does not change my answer they are both legal or both illegal, the fact that you call `delete` on `a` does not make it different. – Slava May 25 '17 at 14:25
  • It's true that there's no difference in legality between the two lines, but someone might read your answer and think both are legal (when in fact both may cause a runtime fault). `delete` is not a function, so it makes no sense to talk about "passing argument by value". – M.M May 25 '17 at 21:21
  • @M.M semantics calling delete is no different than calling a function. And SO becomes really user unfriendly site - downvotes not because answer is incorrect but because "someone might read your answer and think" – Slava May 25 '17 at 22:41
  • Calling delete is different to calling a function. – M.M May 25 '17 at 22:45
  • I can call a function that will call delete, what difference it will make? Will my pointer loose it's value in process? – Slava May 25 '17 at 22:47
  • @M.M `void mydelete( T *p ) { delete p; }` what is semantically different calling this function and calling delete directly? – Slava May 25 '17 at 22:50
  • @M.M it's value will become invalid, but I doubt value would change. Especially for pointers that point inside that memory block. – Slava May 25 '17 at 22:52
  • If it was valid before and invalid after, that means it changed – M.M May 25 '17 at 22:52
  • @M.M so `mydelete` can change parameter, passed by value? – Slava May 25 '17 at 22:54
  • Yes that's right. "pass by value" means the callee receives a copy of the value, and nothing more – M.M May 25 '17 at 22:54
  • @M.M I am afraid they need to think more about that, language would become unpredictable beast where we cannot rely on anything. You pass something by value and function changes it. `newp = oldp; funcbyvalue( newp ); ASSERT( newp == oldp );` OMG it is not invariant anymore. – Slava May 25 '17 at 22:58
  • @Slava That behaviour already exists without involving `delete`, [see example](https://wandbox.org/permlink/YLc6yhLnQ8kHFctR) – M.M May 25 '17 at 23:05
  • @M.M that is not correct example, sorry. I mean to change value through parameter. More interesting things would be with constant pointer that can magically change it's value LOL. I think it is really true - want to ruin something, give it to bureaucrats. – Slava May 25 '17 at 23:07
  • @M.M How about this: `Foo const *ptr = new Foo; delete ptr;` will `ptr` change its value? – Slava May 25 '17 at 23:09
  • Another use case would be if you are on a system that has CPU registers dedicated to memory address, and the hardware performs a check upon loading a register that the value corresponds to memory that is allocated to your process. After `delete p;` the memory might be released to OS and then you load `p` into a register and the hardware generates a fault – M.M May 25 '17 at 23:18
  • @M.M I mean `Foo *const ptr = new Foo; delete ptr;` – Slava May 25 '17 at 23:20
  • Yes, `ptr` becomes invalid in that situation too – M.M May 25 '17 at 23:26
  • @M.M will it's value change? – Slava May 25 '17 at 23:32
  • 2
    Yes, it changed from valid to invalid , and your assert may fail or cause a hardware fault in C++14 (or demons fly out your nose, in C++11) – M.M May 25 '17 at 23:40
  • @M.M another issue comes in mind https://stackoverflow.com/questions/44191920/legal-legacy-code-using-pointers-suddenly-becomes-ub?noredirect=1#comment75397854_44191920 – Slava May 26 '17 at 01:05
0

You should not use the pointer after delete. My below example with acessing a is based on implementation-defined behaviour. (thanks to for M.M and Mankarse for pointing this)

I feel that it is not the variable a (or b, c, d) that is important here, but that the value (=the memory address of a deallocated block) which in some implementations can trigger a runtime fault when used in some 'pointer context'.

This value may be an rvalue/expression, not necessarily the value stored in a variable - so I do not believe the value of a ever changes (I am using the loose 'pointer context' to distinguish from using the same value, i.e. the same set of bits, in non-pointer related expressions - which will not cause a runtime fault).

------------My original post is below.---------------

Well, you are almost there with your experiment. Just add some cout's like here:

class A {};
A* a = new A();
A* b = a;
std::cout << a << std::endl;   // <--- added here
delete a;
std::cout << a << std::endl;   // <--- added here. Note 'a' can still be used! 
A* c = a; 
A* d = b; 

Calling delete a does not do anything to the variable a. This is just a library call. The library that manages dynamic memory allocation keeps a list of allocated memory blocks and uses the value passed by variable a to mark one of the previously allocated blocks as freed.

While it is true what Mankarse cites from C++ documentation, about: "rendering invalid all pointers referring to any part of the deallocated storage" - note that the value of variable a remains untouched (you did not pass it by reference, but by value !).

So to sum up and to try to answer your question:

Variable a still exists in the scope after delete. The variable a still contains the same value, which is the address of the beginning of the memory block allocated (and now already deallocated) for an object of class A. This value of a technically can be used - you can e.g. print it like in my above example – however it is hard to find a more reasonable use for it than printing/logging the past... What you should not do is trying to de-reference this value (which you also keep in variables b, c, and d) – as this value is not a valid memory pointer any longer.

You should never rely on the object being in the deallocated storage (while it is quite probable that it will remain there for some while, as C++ does not require to clear the storage freed after use) - you have no guarantees and no safe way to check this).

Artur Opalinski
  • 1,052
  • 7
  • 12
  • `delete a;` may change the value of `a` and `b` (even though they were passed by value), as explained in Mankarse's answer, and technically they cannot be used. Also, `delete` is a language feature, not a library call. There may be a library call to a storage deallocation function involved as *part* of the operation of `delete`. – M.M May 25 '17 at 21:15
  • @M.M `delete a` cannot change value of `a` and `b` that is not technically possible and would violate way to many things. – Slava May 25 '17 at 22:42
  • @Slava It is technically possible and the standard allows it (see Mankarse's answer for standard references). One use would be that in debug builds the compiler could set `a` and `b` to known garbage values to help in detecting later invalid uses of them . Some compilers also do that for uninitialized variables in debug mode. – M.M May 25 '17 at 22:45
  • @M.M I do not see there that standard allows to change pointer value, and I do not see a way virtual machine would implement that including passing parameters, returning values from function, storing them in global variable and so on. – Slava May 25 '17 at 22:49
  • @M.M. - could you reference some documentation to back your statement that " `delete a`; may change the value of `a` and `b` (even though they were passed by value) " ? The paragraphs referenced by @Mankarse does not stipulate it; the mention of non-normative C++ 14 standard part: "(...) copying an invalid pointer value causes a system-generated runtime fault" does not require violating the pass-by-value rule either. – Artur Opalinski May 26 '17 at 04:46
  • @M.M: Referring to a debugger internal feature is not the same as referring to the language – Artur Opalinski May 26 '17 at 04:47
  • Please read very closely the standard quotes in Mankarse's answer, especially the text "rendering invalid all pointers". The pointer had a valid value before, and an invalid value after. Therefore the value changed. Do you actually think that a valid value is the same as an invalid value? What do you make of the next sentence from C++11 "the effect of using an invalid pointer value is undefined"? – M.M May 26 '17 at 05:22
  • I mention the debugger to provide rationale for why the language rules are as they are. `b` is not passed to any function in the above code so I am not sure why you are talking about `b` being passed by value. – M.M May 26 '17 at 05:26
  • @M.M : See my updated post above for a possible explanation regarding your: "The pointer had a valid value before, and an invalid value after. Therefore the value changed." What I want to convey is that C++ runtime does not have to change a value to invalidate it; it can just list it somewhere as invalid. Modifying some variable would not solve the problems of rvalues. – Artur Opalinski May 26 '17 at 05:49
  • When I say "change the value", I'm including "list it somewhere as invalid". (MMUs do maintain such lists). Maybe what you're trying to get at is "change the representation". Which the compiler can also do, because the standard doesn't specify a particular relationship between values and representations of pointers. – M.M May 26 '17 at 05:52
  • @M.M: You were significantly more specific in your previous utterances in this thread than in your last comment. :-) Hoping to meet again in another discussion. :-) – Artur Opalinski May 26 '17 at 07:08