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
Undefined Reference to '_Gxx_Personality_Sj0'
More Elegant Way to Check for Duplicates in C++ Array
Workaround for Template Argument Deduction in Non-Deduced Context
How to Read a Growing Text File in C++
Order of Execution in Constructor Initialization List
Placement New and Assignment of Class with Const Member
Do I Really Need to Implement User-Provided Constructor for Const Objects
Finding Smallest Value in an Array Most Efficiently
"Not Declared in This Scope" Error with Templates and Inheritance
C++, Sort One Vector Based on Another One
Parameter Pack Must Be at the End of the Parameter List... When and Why
What Is the C++ Compiler Required to Do with Ill-Formed Programs According to the Standard
Operator< Comparing Multiple Fields
Where Are Temporary Object Stored