Is there any reason to check for a NULL pointer before deleting?
Asked Answered
B

10

57

I often see legacy code checking for NULL before deleting a pointer, similar to,

if (NULL != pSomeObject) 
{
    delete pSomeObject;
    pSomeObject = NULL;
}

Is there any reason to checking for a NULL pointer before deleting it? What is the reason for setting the pointer to NULL afterwards?

Brazilein answered 5/3, 2009 at 15:47 Comment(1)
Dunno why they're checking for NULL before deleting as it's perfectly safe to delete a NULL pointer, but the reason they are setting it to NULL afterwords is so that if the object accidentally gets deleted a second time, your program won't explode (deleting a dangling pointer is bad).Neotype
E
77

It's perfectly "safe" to delete a null pointer; it effectively amounts to a no-op.

The reason you might want to check for null before you delete is that trying to delete a null pointer could indicate a bug in your program.

Edit

NOTE: if you overload the delete operator, it may no longer be "safe" to delete NULL

Euboea answered 5/3, 2009 at 15:50 Comment(11)
operator delete must do null check, I guess it should be a c++ standard, am I right?Breadfruit
@Dbger: I'm not sure what you mean, so let me clarify what I meant by my post. If you pass a NULL pointer to the delete mechanism, yes, under the covers, a null check is made, and if the pointer is null, the "function" simply returns without actually doing anything. But the reason you might want to check for a null pointer before you delete is that if you are trying to delete a pointer that has already been set to null, that means that your assumptions about the pointer are incorrect. If your assumptions are incorrect, odds are you have a bug.Euboea
yea, I got it, it makes sense. What I mean is when the compiler vendor or yourself implement an operator delete, you must make sure null-check the pointer inside, this should be a c++ standard.Breadfruit
@Dbger: I'm 90% certain the c++ standard says exactly that. Gonna go check it now.Euboea
@Dbger: Here we go... Using ISO/IEC 14882:2003 section 5.3.5 paragraph 2, second sentence: "In either alternative, if the value of the operand of delete is the null pointer the operation has no effect."Euboea
@Dbger: did you get it from that google code page? Me too. I'm too cheap to spring for thirty bucks at the ISO site. Crap like that is exactly what's wrong with the world these days, IMO.Euboea
@Randolpho:I got it from here: open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf, I am not sure if this could be called "standard", but there are a lot of stuff inside:)Breadfruit
-1 Please clarify what you mean by "The reason you might want to check for null before you delete is that trying to delete a null pointer could indicate a bug in your program." I don't see how the check for null, as shown in the code in the question, would help detect/avoid/workaround such a bug, so how is it helpful?Heshvan
@DonHatch I already answered that question 4 years ago. When you delete a pointer, you intend to free memory, yes? So if you delete a NULL pointer, you are freeing nothing. If your program is freeing nothing, but you thought it was freeing specific memory, it's highly likely that you have a bug in your program. Checking for NULL before deleting and logging the fact that you tried to delete a NULL pointer could help you track down the bug. It doesn't work around the bug, it helps you discover that there is a bug in the first place.Euboea
@Euboea thank you, that makes sense. The crucial part being "and logging the fact that you tried to delete a NULL pointer" which isn't in the code sample and I don't believe you mentioned it earlier in your answer or subsquent comments (although maybe it's there and I'm missing it, stranger things have happened). It would be helpful if you added this clarification to your answer; I really didn't know what you meant otherwise.Heshvan
Please modify the answer to indicate that this is only true if the overloaded delete operator (if any) actually performs the null check. Until C++14, there is no guarantee that operator delete won't be called, and if it does not expect a null pointer, this could lead to nasty memory corruption.Ellaelladine
B
41

The C++ standard guarantees that it is legal to use a null pointer in a delete-expression (§8.5.2.5/2). However, it is unspecified whether this will call a deallocation function (operator delete or operator delete[]; §8.5.2.5/7, note).

If a default deallocation function (i.e. provided by the standard library) is called with a null pointer, then the call has no effect (§6.6.4.4.2/3).

But it is unspecified what happens if the deallocation function is not provided by the standard library — i.e. what happens when we overload operator delete (or operator delete[]).

A competent programmer would handle null pointers accordingly inside the deallocation function, rather than before the call, as shown in OP’s code.Likewise, setting the pointer to nullptr/NULL after the deletion only serves very limited purpose. Some people like to do this in the spirit of defensive programming: it will make program behaviour slightly more predictable in the case of a bug: accessing the pointer after deletion will result in a null pointer access rather than a access to a random memory location. Although both operations are undefined behaviour, the behaviour of a null pointer access is a lot more predictable in practice (it most often results in a direct crash rather than memory corruption). Since memory corruptions are especially hard to debug, resetting deleted pointers aids debugging.

— Of course this is treating the symptom rather than the cause (i.e. the bug). You should treat resetting pointers as code smell. Clean, modern C++ code will make memory ownership clear and statically checked (by using smart pointers or equivalent mechanisms), and thus provably avoid this situation.

Bonus: An explanation of overloaded operator delete:

operator delete is (despite its name) a function that may be overloaded like any other function. This function gets called internally for every call of operator delete with matching arguments. The same is true for operator new.

Overloading operator new (and then also operator delete) makes sense in some situations when you want to control precisely how memory is allocated. Doing this isn't even very hard, but a few precautions must be made to ensure correct behaviour. Scott Meyers describes this in great detail Effective C++.

For now, let's just say that we want to overload the global version of operator new for debugging. Before we do this, one short notice about what happens in the following code:

klass* pobj = new klass;
// … use pobj.
delete pobj;

What actually happens here? Well the above can be roughly translated to the following code:

// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();

// … use pobj.

// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);

Notice step 2 where we call new with a slightly odd syntax. This is a call to so-called placement new which takes an address and constructs an object at that address. This operator can be overloaded as well. In this case, it just serves to call the constructor of the class klass.

Now, without further ado here's the code for an overloaded version of the operators:

void* operator new(size_t size) {
    // See Effective C++, Item 8 for an explanation.
    if (size == 0)
        size = 1;

    cerr << "Allocating " << size << " bytes of memory:";

    while (true) {
        void* ret = custom_malloc(size);

        if (ret != 0) {
            cerr << " @ " << ret << endl;
            return ret;
        }

        // Retrieve and call new handler, if available.
        new_handler handler = set_new_handler(0);
        set_new_handler(handler);

        if (handler == 0)
            throw bad_alloc();
        else
            (*handler)();
    }
}

void operator delete(void* p) {
    cerr << "Freeing pointer @ " << p << "." << endl;
    custom_free(p);
}

This code just uses a custom implementation of malloc/free internally, as do most implementations. It also creates a debugging output. Consider the following code:

int main() {
    int* pi = new int(42);
    cout << *pi << endl;
    delete pi;
}

It yielded the following output:

Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.

Now, this code does something fundamentally different than the standard implementation of operator delete: It didn't test for null pointers! The compiler doesn't check this so the above code compiles but it may give nasty errors at run-time when you try to delete null pointers.

However, as I said before, this behaviour is actually unexpected and a library writer should take care to check for null pointers in the operator delete. This version is much improved:

void operator delete(void* p) {
    if (p == 0) return;
    cerr << "Freeing pointer @ " << p << "." << endl;
    free(p);
}

In conclusion, although a sloppy implementation of operator delete may require explicit null checks in the client code, this is non-standard behaviour and should only be tolerated in legacy support (if at all).

Bayern answered 5/3, 2009 at 15:59 Comment(7)
I did not understand the first point .Can you elaborate more on overloadingBrazilein
rajKumar: see update. I hope this helps. Understanding these overloads isn't easy. I can only advise reading Scott Meyers' Effective C++ (also in general; it's THE de facto C++ how-to manual).Bayern
Setting deleted pointers to NULL has another benefit: guaranteeing that any subsequent attempt to dereference the pointer will crash immediately, instead of doing who-knows-what.Edana
You should know that free(NULL) is also a no-op so your example isn't really an example of the problem. Beyond that, hit the mark on the head.Prank
Checking for null before delete because of a broken overloaded operator is wrong - fix the operator!Photoelectron
@Photoelectron I absolutely agree. I just wanted to demonstrate what the language technically allows.Bayern
@AndrewMedico Dereferencing a null pointer might increase your chances of getting alerted to the problem on OSes that specifically check for access to *0, but either way it invokes undefined behaviour. It's blatantly wrong to state that it "will" reliably crash, or to rely on the apparent fact that all the platforms you use react that way. The responsible way to handle that would be to have assertions that it's not null before ever using it, and rigorously test the program in debug mode to make sure you're never at the mercy of whether or not the OS decides to react in a predictable way to it.Shopkeeper
A
10

Deleting null is a no-op. There's no reason to check for null before calling delete.

You might want to check for null for other reasons if the pointer being null carries some additional information you care about.

Actuary answered 5/3, 2009 at 15:50 Comment(2)
-1 for saying "there's no reason" just because you can't think of one. performance is a possible reason.Heshvan
+1 to counter that ridiculous -1 (I wish I could downvote comments). There is indeed no reason to check for NULL, and performance is a reason not to do so ... checking for NULL before deleting is redundant, and its a redundancy that the compiler cannot eliminate. And this answer usefully includes the observation that the test is not redundant for if (p != NULL) {dosomethingwith(p); delete p; }, which is a common pattern.Grader
P
9

Delete checks for NULL internally. Your test is redundent

Poor answered 5/3, 2009 at 15:59 Comment(2)
Delete.cpp calls Free.c, which checks to see if it is NULL, then it returns. QED. void __cdecl _free_base (void * pBlock) { PHEADER pHeader; if (pBlock == NULL) return;Poor
-1 yes the test for NULL is logically redundant, but that doesn't answer the question of why it might be usefulHeshvan
T
4

According to C++03 5.3.5/2, it's safe to delete a null pointer. This following is quoted from the standard:

In either alternative, if the value of the operand of delete is the null pointer the operation has no effect.

Typhoid answered 21/4, 2012 at 14:20 Comment(1)
-1 yes it's safe, but this doesn't answer the question of why it might be useful to do the test anyway.Heshvan
E
3

If pSomeObject is NULL, delete won't do anything. So no, you don't have to check for NULL.

We consider it good practice to assign NULL to the pointer after deleting it if it's at all possible that some knucklehead can attempt to use the pointer. Using a NULL pointer is slightly better than using a pointer to who knows what (the NULL pointer will cause a crash, the pointer to deleted memory may not)

Eastman answered 5/3, 2009 at 15:51 Comment(2)
Not only use the pointer, but the knucklehead might try to delete it again. Double delete is not allowed.Hemidemisemiquaver
Setting deleted pointers to NULL may also hide serious problems, if the surrounding code is very defensive: checking all pointers against NULL before dereferencing it.Daves
A
1

There is no reason to check for NULL prior to delete. Assigning NULL after delete might be necessary if somewhere in the code checks are made whether some object is already allocated by performing a NULL check. An example would be some sort of cached data that is allocated on demand. Whenever you clear out the cache-object you assign NULL to the pointer so the code that allocates the object knows that it needs to perform an allocation.

Awhirl answered 5/3, 2009 at 15:58 Comment(2)
-1 for saying "there's no reason" just because you can't think of one. performance is a possible reason.Heshvan
+1 because of this againShopkeeper
E
0

I believe the previous developer coded it "redundantly" to save some milliseconds: It's a good thing to have the pointer be set to NULL upon being deleted, so you could use a line like the following right after deleting the object:

if(pSomeObject1!=NULL) pSomeObject1=NULL;

But then delete is doing that exact comparison anyway (doing nothing if it's NULL). Why do this twice? You can always assign pSomeObject to NULL after calling delete, regardless of its current value - but this would be slightly redundant if it had that value already.

So my bet is the author of those lines tried to ensure pSomeObject1 would always be NULL after being deleted, without incurring the cost of a potentially unnecessary test and assignation.

Envy answered 5/3, 2009 at 20:46 Comment(4)
How can it save time if operator deleting is going to do it anyway? Surely it is using 50% more time in most cases where it isn't null because of double checking it?Photoelectron
I can guess the intent was to speed up the case when the pointer is indeed NULL. Imagine it doing no comparison: delete's going to compare versus NULL anyway, get out immediately, and then pointer's assigned (unnecessarily) to NULL again. Since function calls are costlier than comparing against a constant - by doing the comparison first (which delete would do anyway) you'd save some milliseconds from invoking the delete code call plus the other milliseconds of the (unnecessary by then) NULL assignation.Envy
+1 on this since it is the only answer so far to point out a potentially valid reason for the test, assuming delete is implemented correctly.Heshvan
@JimBalter Calling a function is not free - pushing parameters in, remote call, push params out, then push return value in, return, push ret value out. Unless the compiler can inline it away (I concede nowadays most do with "delete") then just calling "free" would imply doing that dreaded comparison plus a function call, plus assigning NULL to a variable that already has NULL in its value - too much wasted milliseconds.Envy
G
-2

It depends on what you are doing. Some older implementations of free, for example, will not be happy if they are passed a NULL pointer. Some libraries still have this problem. For example, XFree in the Xlib library says:

DESCRIPTION

The XFree function is a general-purpose Xlib routine that frees the specified data. You must use it to free any objects that were allocated by Xlib, unless an alternate function is explicitly specified for the object. A NULL pointer cannot be passed to this function.

So consider freeing NULL pointers as a bug and you'll be safe.

Gourmont answered 5/3, 2009 at 16:4 Comment(1)
-1 This question is about delete, not free or XFree. free has checked for NULL since at least UNIX V7, and is required to do so by the language standard, just as delete is.Grader
B
-5

As for my observations, deleting a null pointer using delete is safe in unix based machines ike PARISC and itanium. But is quite unsafe for Linux systems as the process would crash then.

Boyse answered 7/8, 2009 at 1:21 Comment(2)
It's not true that it's unsafe on Linux; the standard guarantees it's safe and I've done it countless times.Heshvan
If your processes crashed, your compiler was broken, and I hope you got a new one by now.Shopkeeper

© 2022 - 2024 — McMap. All rights reserved.