views:

568

answers:

6

I have inherited a large c++ code base and I have a task to avoid any null pointer exceptions that can happen in the code base. Are there are static analysis tools available, I am thinking lint, that you have used successfully.

What other things do you look out for?

+1  A: 

These might be of interest:

What open source C++ static analysis tools are available?

C++ static code analysis tool on Windows

Are C++ static code analyis tools worth it?

I would also look at using a dynamic runtime tool like Valgrind (free)

Mitch Wheat
+1  A: 

If you are mainly maintaining the code base, One of the lowest level-of-effort and highest return things you can do is start refactoring your bare pointers to reference counted pointers.

I'd also look at something like Purify which will instrument your code to detect memory corruption.

Jeff Leonard
+1  A: 

A side question, is the purpose of avoiding these because they don't want the customer to see a crash? In many cases null pointers are unexpected conditions that should be handled immediately, but all too often they get passed thru the system like a hot potato.

I once worked on a code base where the habit was upon entry into a function, to first check for any null pointers and if so, return. The problem with this is while the tool didn't crash, it ultimately generated bad data silently. And trying to debug these problems was difficult because there might have been illegally null pointers being passed around for a long time thru many functions before the results became intolerable or ultimately had to manifest itself.

Ideally, you want proper assertions at least during your development period , so consider a macro to hide or redefine the assert for production builds

no-op
+2  A: 

You can start by eliminating sources of NULL:

Change

if (error) {
    return NULL;
}

Into

if (error) {
    return DefaultObject; // Ex: an empty vector
}

If returning default objects does not apply and your code base already uses exceptions, do

if (error) {
    throw BadThingHappenedException;
}

Then, add handling at appropriate places.

If you are working with legacy code, you could make some wrapper functions/classes:

ResultType *new_function() {
    ResultType *result = legacy_function();
    if (result) {
        return result;
    } else {
        throw BadThingHappenedException;
    }
}

New functionalities should start using new functions and have proper exception handling.

I know some programmers just don't get exceptions, including smart people like Joel. But, what ends up happening by returning NULL is that this NULL gets pass around like crazy since everybody would just think it's not their business to handle it and return silently. Some functions may return error code, which is fine, but the caller often ends up returning yet-another-NULL in response to errors. Then, you see a lot of NULL-checking in every single functions, no matter how trivial the function is. And, all it takes is just ONE place that doesn't check for NULL to crash the program. Exceptions force you to carefully think about the error and decide exactly where and how it should be handled.

It seems like you're just looking for easy solutions like static analysis tools (which you should always use). Changing pointers to references is also a great solution too. However, C++ has the beauty of RAII, which eliminates the need for having "try {} finally {}" everywhere, so I think it worths your serious consideration.

David Lin
The first is coding for slop which I don't think is generally a good idea (Although 3 days after release day if it fixes your last bug it can be a lifesaver!). Fail as fast as you can--throw an exception as in the later examples.
Bill K
I should mention that the first is called NullObject pattern. Like any patterns, think thrice before using.
David Lin
+1  A: 

First, as a technical point, C++ does not have NULL pointer exceptions. Dereferencing a NULL pointer has undefined behavior, and on most systems causes the program to abruptly terminate ("crash").

As for tools, I too recommend the question:

Are C++ static code analyis tools worth it?

Regarding NULL pointer dereferences in particular, consider that a NULL pointer dereference has three main elements:

  1. The introduction of a NULL pointer.
  2. The flow of that pointer elsewhere in the program.
  3. The dereference of that pointer.

The hard part for a static analysis tool is of course step 2, and tools are differentiated by how complicated a path they can accurately (i.e., without too many false positives) track. It might be useful to see some specific examples of bugs you want to catch in order to better advise about what sort of tool will be most effective.

Disclaimer: I work for Coverity.

Scott McPeak
+1  A: 

Hi,

If you don't want to change any code, you have to use some tools (see other answers). But for a special part of the Problem (where you put a pointer in a function to use it) there is a nice little Makro-Definition you could use to find some little Buggers: (no time overhead in release-mode and adds a visible condition to the code)

    #ifdef  NDEBUG

    #define NotNull(X) X

    #else // in Debug-Mode

    template<typename T> class NotNull;

    template<typename T> // template specialization only for pointer-types
    class NotNull<T*> {
    public:
        NotNull(T* object)
        : _object(object) {
            assert(object);
        }

        operator T*() const {
            return _object;
        }

        T* operator->() const {
            return _object;
        }
    private:
        T *_object;
    };

    #define NotNull(X) NotNull<X>

    #endif // in Debug-Mode

You just change every function from this:

void increase(int* counter)  
{ .. }

to this

void increase(NotNull(int*) counter)
{ .. }

p.s : first found HERE and can be tweaked even further

Dane