In Either C or C++, Should I Check Pointer Parameters Against Null/Nullptr

In either C or C++, should I check pointer parameters against NULL/nullptr?

While in general I don't see the value in detecting NULL (why NULL and not some other invalid address?) for a public API I'd probably still do it simply because many C and C++ programmers expect such behavior.

When to check for nullptr inside functions with a pointer parameter?

There is a difference between a logic error and an assertion error. If your function is only allowed to run with a valid pointer, then the user of your function is responsible and should check if it's a valid pointer before entering your function. Standard C functions document such states as "undefined behavior", because it's literally undefined how strcpy(NULL, "abc") should react - passing such values to functions is just invalid and the programmer (other code) is responsible to check for such errors.

If so, this is where assert() comes in play. assert was intended to fire only in debug configuration of your project and to be removed (so that performance is not hindered) in a release configuration of your project - hence NDEBUG macro. NASA principles of safety-critical code tells "code's assertion density should average to minimally two assertions per functions".

So if passing NULL to your function is an "invalid state" in which case the behavior of your function is "not defined", because it just doesn't make sense to pass NULL (or any other invalid pointer), I usually write:

struct Vector3 {
float x,y,z;
};

void test(struct Vector3 *va, struct Vector3 *vb) {
assert(va != NULL);
assert(vb != NULL);
// bla bla...
}

(and on gcc I would add __attribute__((__nonnull__)) and with newest gcc with -std=c2x we can [[gnu::nonnull]]). Be aware that assert() expands to "nothing" when NDEBUG is defined (ie. the expression is not evaluated), so do not put statements with side effects inside assert().

Determining null pointer

According to the C++ 14 Standard (5.10 Equality operators)

2 If at least one of the operands is a pointer, pointer conversions
(4.10) and qualification conversions (4.4) are performed on both
operands to bring them to their composite pointer type (Clause 5).
Comparing pointers is defined as follows: Two pointers compare equal
if they are both null, both point to the same function, or both
represent the same address (3.9.2), otherwise they compare unequal.

And (4.10 Pointer conversions)

1 A null pointer constant is an integer literal (2.13.2) with value
zero or a prvalue of type std::nullptr_t. A null pointer constant can
be converted to a pointer type; the result is the null pointer value
of that type and is distinguishable from every other value of object
pointer or function pointer type. Such a conversion is called a null
pointer conversion...

Thus the expression in the return statement

 return parent == 0 ? nullptr : parent->getChild();

is entirely correct because the null pointer constant 0 is converted to a null pointer value of the type of the pointer parent. But it will be more expressive to write

 return parent == nullptr ? nullptr : parent->getChild();

Checking for NULL pointer in C/C++

In my experience, tests of the form if (ptr) or if (!ptr) are preferred. They do not depend on the definition of the symbol NULL. They do not expose the opportunity for the accidental assignment. And they are clear and succinct.

Edit: As SoapBox points out in a comment, they are compatible with C++ classes such as auto_ptr that are objects that act as pointers and which provide a conversion to bool to enable exactly this idiom. For these objects, an explicit comparison to NULL would have to invoke a conversion to pointer which may have other semantic side effects or be more expensive than the simple existence check that the bool conversion implies.

I have a preference for code that says what it means without unneeded text. if (ptr != NULL) has the same meaning as if (ptr) but at the cost of redundant specificity. The next logical thing is to write if ((ptr != NULL) == TRUE) and that way lies madness. The C language is clear that a boolean tested by if, while or the like has a specific meaning of non-zero value is true and zero is false. Redundancy does not make it clearer.

Is it safe to pass nullptr to C function?

Is it safe to pass nullptr to pure C function in this case?

I think it is. From the C++11 standard (4.10 Pointer conversions):

A null pointer constant is an integral constant expression ([expr.const]) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type.

A nullptr and an intergral constant expression that evaluates to 0 are equivalent for such conversions.

Nullptr and checking if a pointer points to a valid object

In C, anything that's not 0 is true. So, you certainly can use:

if (ptrToObject) 
ptrToObject->doSomething();

to safely dereference pointers.

C++11 changes the game a bit, nullptr_t is a type of which nullptr is an instance; the representation of nullptr_t is implementation specific. So a compiler may define nullptr_t however it wants. It need only make sure it can enforce proper restriction on the casting of a nullptr_t to different types--of which boolean is allowed--and make sure it can distinguish between a nullptr_t and 0.

So nullptr will be properly and implicitly cast to the boolean false so long as the compiler follows the C++11 language specification. And the above snippet still works.

If you delete a referenced object, nothing changes.

delete ptrToObject;
assert(ptrToObject);
ptrToObject = nullptr;
assert(!ptrToObject);

Because of how long I have been writing these ifs like this, it is second nature at this point to check if the pointers valid before using by typing if (object *) and then calling it's members.

No. Please maintain a proper graph of objects (preferably using unique/smart pointers). As pointed out, there's no way to determine if a pointer that is not nullptr points to a valid object or not. The onus is on you to maintain the lifecycle anyway.. this is why the pointer wrappers exist in the first place.

In fact, because the life-cycle of shared and weak pointers are well defined, they have syntactic sugar that lets you use them the way you want to use bare pointers, where valid pointers have a value and all others are nullptr:

Shared

#include <iostream>
#include <memory>

void report(std::shared_ptr<int> ptr)
{
if (ptr) {
std::cout << "*ptr=" << *ptr << "\n";
} else {
std::cout << "ptr is not a valid pointer.\n";
}
}

int main()
{
std::shared_ptr<int> ptr;
report(ptr);

ptr = std::make_shared<int>(7);
report(ptr);
}

Weak

#include <iostream>
#include <memory>

void observe(std::weak_ptr<int> weak)
{
if (auto observe = weak.lock()) {
std::cout << "\tobserve() able to lock weak_ptr<>, value=" << *observe << "\n";
} else {
std::cout << "\tobserve() unable to lock weak_ptr<>\n";
}
}

int main()
{
std::weak_ptr<int> weak;
std::cout << "weak_ptr<> not yet initialized\n";
observe(weak);

{
auto shared = std::make_shared<int>(42);
weak = shared;
std::cout << "weak_ptr<> initialized with shared_ptr.\n";
observe(weak);
}

std::cout << "shared_ptr<> has been destructed due to scope exit.\n";
observe(weak);
}

Now, will C++ do the same for pointers? If pass in a char * like this to an if statement?

So to answer the question: with bare pointers, no. With wrapped pointers, yes.

Wrap your pointers, folks.

Ensuring compilation error while passing null pointer to a function

Is there a way that I can ensure that an error occurs on the compile time whenever nullptr is passed as a parameter.

If you specifically mean the nullptr keyword, then sort of. You can provide overloads that would be chosen in that case, and define them deleted. This works as long as you don't explicitly bypass the overloading by casting for example.

int functionA(int*, std::nullptr_t) = delete;
int functionA(std::nullptr_t, int*) = delete;

// ...
functionA(&i, &j) // OK
functionA(nullptr, &i); // error
functionA(&i, nullptr); // error
functionA(nullptr, nullptr); // error

or NULL

This will require adding overloads for integers in addition to the previous overloads:

int functionA(int*, int) = delete;
int functionA(int, int*) = delete;

// ...
functionA(NULL, &i); // error
functionA(&i, NULL); // error
functionA(NULL, NULL); // error

If you mean any pointer with null value, then that cannot be done because the values of function arguments cannot generally be known at compile time.

If your goal is to not use the pointers as iterators, then it would be safer and more convenient to pass references instead.

Good practice to check if arguments are NULL

It depends on the documented interface. If the function is documented as taking two non-null strings, you might add an assertion that they're not null and get on with life with no further checks. If the function is documented to treat null pointers as if they pointed at empty strings, or something faintly similar, then that is OK and you have to do checking and adjusting as appropriate. There's nothing wrong with checking and handling invalid parameters as your code does even if the function is documented as taking non-null pointers to valid null-terminated strings, but it bulks up your code for minimal benefit to those who can read your documentation.

The standard C library functions (such as strcmp()) simply require the arguments to be valid strings. You invoke undefined behaviour if you pass a null pointer to it. It can crash, or not, at the whim of the implementer.

For your function, it seems likely to be reasonable that users would only supply valid pointers; an assertion to keep them honest is good, and that's all that's needed.



Related Topics



Leave a reply



Submit