Practical Guide to using of Pointers

Introduction

This article talks about some general tips while using pointers in an Application.

Background

While working with different kinds of errors, generally detecting an error is easier than fixing it. When it comes to pointer, the error is usually the result of a pointer pointing somwehere else it should not. And using such pointer is nothing but working on memory which is corrupted.

While working with pointers, we need to keep two points in mind

Either avoid introducing pointer-error in the first place.

Or fix the pointer-error as soon as we code them as possible.

Here are some general tips on pointer while using them in our program.

1. Isolate pointer operations in Routines or Classes.

Suppose if we are using linked list in several places of an application, then instead of using the list directly in each place it's used, it is better to have access routines like CurrentNode(), PrevNode(),NextNode(), DeleteNode(), InsterNode() etc & use these access routines everywhere the list is used. This in turn helps in re-usability.

2. Avoid acquiring pointers to non-heap memory.

For Example:

The code

Code: Cpp

void function(){int a;int *ptr;

a=0; *ptr = &a;// do something here}

Here, you may forget that ptr is pointing to an object whose life is over when the function returns, which will lead to all kinds of dangerous errors.

3. Declare and Define pointers at the same time.

First of all, assiging a Variable it's initial value close to where it is declared is one of the coding guideline we usually need to follow. And this coding guideline is more valuable when it comes to pointer variables.

For Example:

Bad Practice:

Library *libPtr;

//Lots of other code here

libPtr = new Library;

Here there are chances that libPtr being used in between the code (between it's declaration and it's initialization) before even it gets allocated and initialised properly.

Good Practice:

Library *libPtr;
libPtr = new Library;

4. Delete Pointers at the same scoping level as they were allocated.

Always keep Allocation and De-allocation of pointers symmetric.

For Example,

Bad Practice:

A Routine allocating memory to a pointer and expecting it's client code to de-allocate that pointer. This creates in-consistency which in-turn may lead to errors.

Good Practice:

if we allocate and initialize a pointer in a class's constructor, it should to be de-allocated/deleted in the class's destructor.

5. Always test pointers before using them.

Before using pointers in our application, make sure the memory location it points to is valid.

For Example:

If we expect memory location for our application to begin with start_data and end with end_data, in such case, check the pointer whether it is pointing in between start_data and end_data location. We can do this by having access routines rather than directly manipulating them.

6. Also test the pointers for NULLness before using them in application.

We can write a generalised Assert for the application and use it everywhere to test the pointers before using them, rather than directly checking everytime.

7. Check the Value of the variable pointed by pointer before using it.

Sometimes it is good to check the value of the pointer it points to before working on it.

For Example:

Suppose you expect an Interger pointer to point to some integer value that takes value like 0-1000 range. Then after de-referencing pointer and before using the value, check whether the value falls in the correct range or has some different value, before working on it.

As said in last tip, this also can be done using some access routines.

This not only helps for better redability, it adds even to improve the performance of code, by giving the benefit of not doing the discountValue calculation inside loop for every iteration.

9. Initialize pointer to NULL.

If you are not immediately initializing a pointer to some valid value, always initialize it to NULL.

Good Practice:

Employee *empPtr;
empPtr = NULL;

This protects our code from leaving a pointer un-initialized and later doing as below:

if (empPtr)
{
delete empPtr;
}

10. Shred your garbage.

Pointer Errors are hard to debug because the location in the memory, at which the pointer points to, becomes invalid, is non-deterministic. Sometimes the memory contents look valid, even after the pointer is freed.

So, we can force errors related to using deallocated pointers to be more consistent by overwriting freed meomry blocks with junk-data before deleting the pointer.

Also, common error with pointer is "dangling pointer", use of pointer that has been deleted or freed.

Good Practice:

Employee::~Employee()
{
delete (myPtr_mp);
myPtr_mp = NULL;
};

By setting pointer to NULL after freeing it, we don't change the fact of reading data pointed by the dangling pointer, but we do ensure that not writing data to such pointer, as writing to such pointer will yeild an error or result in segmentation fault.

Inconsistent usage of new[] and delete[] lead to memory leaks. The most common mistake is using new[] to allocate and using just delete to deallocate. The effect is that we construct an array of objects, then destruct only the first element of it.

14. Draw a Picture.

It usually helps to draw a picture while working on linked list.

For Example:
If we are adding a newNode to the existing list. Then we can draw a picture something like below. This would help coding easier.