20

Inspired by this question.

Suppose in C++ code I have a valid pointer and properly delete it. According to C++ standard, the pointer will become invalid (3.7.3.2/4 - the deallocation function will render invalid all pointers referring to all parts of deallocated storage).

At least in most implementations it preserves the value and will store exactly the same address as before delete, however using the value is undefined behavior.

Does the standard guarantee that the pointer will preserve its value or is the value allowed to change?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 4
    Does the `delete` signature allow it to access the pointer, i.e. anything other than pass-by-value? – Rup Feb 15 '11 at 09:52
  • 2
    Interesting question, but purely academic curiosity, I hope. I can't imagine why you would need to know this when writing code. – Cody Gray - on strike Feb 15 '11 at 09:54
  • 1
    @Cody Gray: You're right. *Using* the pointer (for example, trying to `printf()` it) after `delete` is UB, so the user couldn't even legally read the pointer and compare to the original value. – sharptooth Feb 15 '11 at 09:58
  • 2
    @Rup: Delete is, in this case, an operator without a signature. ("Operator delete" is the function which is called by the delete operator or explicitly called.) – Fred Nurk Feb 15 '11 at 10:00
  • I am not able to understand the question. Can you provide some example? – Harish Feb 15 '11 at 10:00
  • @Harish: Something like this: I `delete` a pointer and it stores `0xDEADBEEF` address after that. – sharptooth Feb 15 '11 at 10:01
  • @Cody Gray: That depends - the information that a pointer holds it two-fold: (1) its actual address and (2) where it points. Sometimes the first one can be important - perhaps it should not - but could. Interesting question. – Ole Thomsen Buus Feb 15 '11 at 10:01
  • 2
    @FredNurk: It might be clearer to say that it's a keyword that invokes an operator (`operator delete`) to delegate its work. And that the keyword has no 'signature'. – Lightness Races in Orbit Feb 15 '11 at 10:02
  • Although not relevant in practice, this is an interesting question, BTW! – Lightness Races in Orbit Feb 15 '11 at 10:03
  • @sharptooth Stores the address where? – Harish Feb 15 '11 at 10:04
  • @Harish: In the pointer that was the operand of `delete`. – sharptooth Feb 15 '11 at 10:08
  • @Fred @Tomalak: The most correct way of referring to it is as a *delete-expression*. It's an expression; not an operator, function, delegated function, etc. (It does though, of course, have side-effects, like possible calling `operator delete` and kin.) – GManNickG Feb 15 '11 at 10:17
  • @sharptooth: I understand. Unique question. I am eager to know the answer myself. – Harish Feb 15 '11 at 10:18
  • @Cody Gray: it's not *purely* academic, at least in the sense that if the value is modified then a strictly conforming program can tell the difference. You can't access the pointer value *as a pointer value*, but you can validly compare the values of the bytes that it occupies, before and after. – Steve Jessop Feb 15 '11 at 12:45
  • @Steve Jessop: If I store the original value and then `memcmp()` it with the value after `delete` will it not be undefined behavior? – sharptooth Feb 15 '11 at 12:58
  • @sharptooth: I don't think so. `memcmp` doesn't use the pointer value, as far as I read the standard. It's not undefined behaviour to `memcmp` 4 bytes that just so happen to invalid as a float value, so why should it be undefined to `memcmp` 4 bytes that just so happen to be invalid as a pointer value? Memory can always be examined as bytes provided that it's still allocated, it doesn't matter what it does or doesn't represent. Just don't use the pointer value. The result of the `memcmp` maybe unspecified or implementation-defined, though, whichever the standard says about `delete` on lvalues. – Steve Jessop Feb 15 '11 at 13:05
  • 1
    @Tomalak: Note that the `delete` operator destructs the object before it calls `operator delete` to release the memory. – fredoverflow Feb 15 '11 at 13:41
  • @GMan: Correct. / @FredOverflow: True, it doesn't delegate *all* of its work. – Lightness Races in Orbit Feb 16 '11 at 14:24

7 Answers7

22

No, it's not guaranteed and an implementation may legitimately assign zero to an lvalue operand to delete.

Bjarne Stroustrup had hoped that implementations would choose to do this, but not many do.

http://www.stroustrup.com/bs_faq2.html#delete-zero

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
15

If, for whatever reason, you want to be sure the pointer variable is not changed by delete, write:

delete p + 0;
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
3

I believe that most implementations will keep the value, only for the sake of having no reason to change it. But regardless of whether the value is kept, it's still a useless pointer, is it not?

Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72
  • Yes, it's useless either way. But the nature of the bugs you'd encounter if you tried to use it anyway would be completely different. – Mark Ransom Aug 28 '21 at 03:16
1

A pointer is not guaranteed to be of any meaningful value in of itself other than the range in which it was allocated and one past the end of that range.

What you might be questioning is whether, say, you were doing your own leak checking so you wrote function to remove a pointer from a map after you had done a delete. That would use std::less which is guaranteed to work with pointers that do not point within a range, and would presumably work too with pointers that pointed to memory that is no longer valid.

Of course you might get your garbage collecting to do the "remove" just before deleting the memory it was pointing to.

As it is with the standard, if the value you pass to delete is not an l-value it is guaranteed to maintain the same value, but if it is an l-value it is implementation defined.

CashCow
  • 30,981
  • 5
  • 61
  • 92
1

Consider, how would you check or rely on any "yes" or "no" answer? You can't. Or, you can, but the result of that checking (except for nullpointer) is Undefined Behavior.

You can't check a non-null value after a delete, so the question is in general meaningless.

Also, the argument to delete can be an rvalue expression, so the question is meaningless.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 1
    Surely you can check by looking at the values of the bytes in memory before, remembering them, and looking again afterwards. It's true that those bytes might be a pointer value representation before and (say) a trap representations after, so in a sense there's no "after value" about which we can ask, "has it changed?". But it's meaningful to ask about the output of e.g. `char *x = 0; char one = *(char*)(&x); delete x; char two = *(char*)(&x); std::cout << (one == two);`. If it outputs `false` I think it's fair to say that something about `x` has changed, if not precisely the pointer value. – Steve Jessop Feb 15 '11 at 12:47
  • (Actually, I think I need `char *x = new char;` there, since null pointers are a special case). Also, the fact that the argument to `delete` *can* be an rvalue has no direct bearing on what happens when it's an lvalue. Of course, it might suggest to implementers that doing different things in the two cases would be unnecessary effort. – Steve Jessop Feb 15 '11 at 12:52
  • @Steve: it's usually no good to serialize pointer values (e.g. storing them in files), but one might conceivably have a pointer value in a POD struct and compare it bitwise with earlier copy. i think that that is pretty marginal case. so, in general, meaningless. – Cheers and hth. - Alf Feb 15 '11 at 13:33
  • "In general" meaning, "except in the cases where it is meaningful in the sense that it affects the behaviour of a strictly conforming program"? Granted, not a strictly conforming program that you or I have any motivation to write. To be honest, I think your hope that your answer will help is forlorn ;-) – Steve Jessop Feb 15 '11 at 13:45
  • @Cheersandhth.-Alf: how can argument to delete can be an rvalue expression? Will you please give an example? – Destructor Sep 15 '15 at 15:18
  • 1
    @PravasiMeet: The simplest realistic example is probably `delete foo()`, where `foo` is a function that serves up the pointer value. Bjarne Stroustrup, the language creator, also provides some examples in [his FAQ item about this](http://www.stroustrup.com/bs_faq2.html#delete-zero). – Cheers and hth. - Alf Sep 15 '15 at 15:25
  • @anonymous downvoter: explain what you, most likely in complete ignorance, thought was wrong here. it's very anti-social to **mislead others** by downvoting perfectly good answers such as this. – Cheers and hth. - Alf Sep 15 '15 at 15:46
1

This question is important!

I have seen that Visual Studio 2017 has changed a pointer's value after delete. It caused a problem because I was using a memory tracing tool that collected pointers after each operator new and was checking them after delete. Pseudo code:

Data* New(const size_t count)
{
    Data* const ptr(new Data[count]);
    #ifdef TEST_MODE
    DebugMemory.Collect(ptr);
    #endif
    return ptr;
}

void Delete(Data* const ptr)
{
    delete[] ptr;
    #ifdef TEST_MODE
    DebugMemory.Test(ptr);
    #endif
}

This code worked good in Visual Studio 2008, but was failing in Visual Studio 2017, so I have changed the order of operations in the second function.

However, the question is good, and the problem exists. Experienced engineers should be aware of that.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

The signature of the global operator delete, as required by the standard 3.7.3.2/2:

Each deallocation function shall return void and its first parameter shall be void*.

This means that delete cannot modify the pointer you pass to it, and it will always retain its value.

Erik
  • 88,732
  • 13
  • 198
  • 189
  • 7
    The function "operator delete" is not the delete operator. The signature of that function has nothing to do with whether the operator can modify a value. (That function cannot itself modify any value, of course. It can't even take the pointer by reference or a pointer to the pointer, as it doesn't have the type information from the original pointer.) – Fred Nurk Feb 15 '11 at 10:01