QC 7: Null Pointers

In the preceding posts, I talked about the different principles or building blocks of good programming practices. Some examples augmented the advocated principles and was intended to provide an overview. However, there are some common, well-known principles that are acknowledged, but often ignored. Some principles that are applicable in day-to-day coding are captured in this post and I intend to add more of the same in times to come.

Conditional Statement

If a variable is being compared with a Macro, typical tendency is write the program in such a manner that the variable is on the LHS of the operand and the Macro on the RHS. Consider the following NULL check statement for a pointer pTestPtr below

if(pTestPtr == NULL) /* Checking pTestPtr for NULL */

If a “=” operator is missed out, it can have disastrous consequences as the pointer is initialized to NULL. Moreover, code review may not immediately catch it, as human tendency is to assume the most common operators and hence, first sight will not reveal the issue. Continuous Debugging is the only way to localize the issue and fix it. If this scenario is encountered on an embedded platform running a rich system like Android or Linux, it takes some debugging to zero-in onto the issue.

To overcome such scenarios, a simple programming technique can be employed. Whenever a variable is being tested against a macro, it should be made a habit to place the Macro on the LHS and variable on RHS. This way, even if a “=” operator is missed out, the issue is caught immediately by the compiler. The aforementioned example can be re-written as below

if(NULL == pTestPtr) /* Checking pTestPtr for NULL */

Function Arguments

In any programming paradigm, algorithm is broken down into modules and implemented as functions. Each function has its own share of input and output arguments. It is extremely critical to ensure that the arguments to a function are valid and if not, an error is raised immediately.

One of the most issues faced in debugging is NULL Pointer problem. If a function has some arguments which are pointers, there is a potential bug/crash lurking around if they aren’t tested for NULL pointers. It is imperative to ensure that any pointer arguments to any function are tested for pointers. Consider an example function with 3 pointer arguments out of which 2 signify input array pointers and 1 the output one.

void func_02(int *pInpPtr0, int *pInpPtr1,int *pOutPtr0)

To ensure that these pointers are tested for NULL check, an individual check can included for each of the pointer arguments. However, Pointer NULL Check being a most common feature of any software program, the same can be implemented as a macro, such that the same conceptual template can be employed by any function in the entire software tree. Designing a pointer NULL Check macro, the body of the function would look like

#define ASSERT_IF_NULL(x)    assert(NULL != (x)) /* ASSERT IF ARGUMENT IS NULL */
void func_02(int *pInpPtr0, int *pInpPtr1,int *pOutPtr0)
{
    ASSERT_IF_NULL(pInpPtr0); // NULL Check for pInpPtr0
    ASSERT_IF_NULL(pInpPtr1); // NULL Check for pInpPtr1
    ASSERT_IF_NULL(pOutPtr0); // NULL Check for pOutPtr0
}

Implementing a NULL check improves the quality of software incredibly and helps to achieve better stability of the code. However, in any Embedded System, performance is of utmost importance and hence, any optimization in terms of conditionals is always welcome. Hence, in an end-product, where the system is *assumed* to be stable and more importantly consistent, it is a common practice to do away with NULL Checks. The key word in the previous statement is assumed, which typically FAILS!!. Lack of testing or corner cases throw up some nasty surprises, especially during customer productization cycles.

The challenge thus becomes to have a generic piece of code that can be used for both debug/stability testing as well as being optimized. This is usually achieved by having a compile-time switch to enable multiple implementations based on some compilation flags. Considering that *NULL_CHECK_OPTIMIZATION* is a flag enabled during productization, the aforementioned implementation could be rewritten as

#if !defined(NULL_CHECK_OPTIMIZED)
/* Case when NULL CHECK is NOT Optimized */
#define ASSERT_IF_NULL(x)    assert(NULL != (x))
#else
/* Case when NULL Check is optimized */
#define ASSERT_IF_NULL(x)
#endif

void func_02(int *pInpPtr0, int *pInpPtr1,int *pOutPtr0)
{
    ASSERT_IF_NULL(pInpPtr0); //NULL check for pInpPtr0
    ASSERT_IF_NULL(pInpPtr1); //NULL check for pInpPtr1
    ASSERT_IF_NULL(pOutPtr0); //NULL check for pOutPtr0
}

To debug the issue, a new build without the NULL_CHECK_OPTIMIZATION flag is tested and voila!! any pointer issues will be caught immediately. A little bit of thought and design can save a lot of headache in the long run.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s