Memory Leak: Scenario #1

Many a times we write code like this:

int *pData1 = NULL;
int *pData2 = NULL;

pData1 = new int [ n ];
pData2 = new int [ m ];

if ( pData1 && pData2 )
{
// Use pData1 and pData2

delete[ ] pData1;
delete[ ] pData2;
}

Is there anything wrong with this code? If you think there is nothing wrong with this code, well …

Let us analyze what is wrong here.

The condition in if statement makes sure we use pData1 and pData2 only when both of them are assigned valid addresses after allocating memory successfully, using new operator. When memory allocation is successful, for both pData1 and pData2, we use the pointers and when not required anymore, we release the allocated memory using delete operator.

In case memory allocation fails, for both pData1 and pData2, we never enter the if statement block. So everything looks fine here?

No, what if only one allocation fails, for either pData1 or pData2? Due to the condition in if statement we will not enter the if statement block, resulting in leaking memory which was allocated using new operator.

So what is the correct way to write the above code snippet? Instead of releasing memory inside if statement block we will release the memory out the if statement block. But doing this won’t we will be releasing memory even when it is not allocated? So we will release the memory outside if statement block but conditionally, after checking if it is allocated or not. Below is the modified code snippet.

int *pData1 = NULL;
int *pData2 = NULL;

pData1 = new int [ n ];
pData2 = new int [ m ];

if ( pData1 && pData2 )
{
// Use pData1 and pData2
}

if ( pData1 )
{
delete[ ] pData1;
}

if ( pData2 )
{
delete[ ] pData2;
}

15 thoughts on “Memory Leak: Scenario #1”

  1. I hope everyone takes your advice. It is good practice to be meticulous about memory allocation.

    Just a few comments:

    1) C++ semantics defines deleting a NULL pointer as a no-op, so you don’t really need the conditionals for the delete’s at the end. However, I agree that it is good practice to do so.

    2) It may be preferable to detect allocation failures early, and handle the error case in a conditional rather than the success case. That way, your main logic still stays at the top-level and makes for easy reading.

    if(!pData1 || !pData2)
    {
    // Log error
    return false;
    }

    // Main logic…

    However, many people prefer to have a single exit point for each function, in which case what you have is more appropriate.

    3) The new C++ standard says that new throws a std::bad_alloc exception if memory could not be allocated. So you could still have a memory leak even if you have an exception handler somewhere up the stack. With new compilers, you’ll need to have a try/catch block to handle the clean-up.

    4) One of the best ways to handle this whole situation is to use the RAII paradigm: resource acquisition is initialization. Encapsulate your pointers in an object and delete the memory in the destructor. That way, you don’t need all these conditionals, it works even if new throws exceptions, and the code is easier to read overall.

    -K

  2. Good observation Kaushik!

    Why not use std::auto_ptr to implement RAII instead of writing something on your own. Reuse at its best.

    ~Yogesh.

  3. Yyp auto_ptr should be used but then I have worked on a “big” project where the client didn’t want us to use STL and hence no auto_ptr as well.
    But then one can easily implement a Smart Pointer on its own, this reminds me to dig out my presentation about Smart Pointer and upload 🙂

  4. if circular queue is full and then we dequeue(delete) a element that is FIFO
    then will we able to add one more element to it?

  5. @Abhaysinh:
    1. Class member functions do take memory but that is not added as the size of class. Reason being that class defines a type and its size is what decides how much memory its object will need. Since member functions are shared by all the objects of a class, they are not part of any specific object. Instead they are stored seperatly, in code segment.
    2. Yes, in circular queue, when it becomes full and you delete a element, you will be able to add a new element.

  6. sir
    what is this
    double *p,*q;
    p=(double *)4000;
    q=(double *)2000;
    what is this declaration ?
    printf(“%d”,p-q);
    for this printf what will be the answer and why?

  7. sir
    int *p;
    p = (int *)300;
    similar to earlier question but
    what this pointer exactly holds in this case and how it works
    sir please tell in details

  8. @Abhay: Hope this ->http://www.navendu.net/wordpress/?p=145 will help in solving your 2nd query related to pointers. For the first one, I’ll put up the post tomorrow morning.

  9. Res. Sir
    why cant we get address of constant veriable.
    Sir I didin’t get answer of my First Pointer Query.

    const int a=5;
    int & b = a;

    Sir above case even though b is reference to const a , b have different address
    in this above case, is it ok to use referece if veriable is const.

  10. hii sir
    i am the x student of cdac 2008 aug batch, actuaaly i forgotten the variable storage like code segment,data segment etc. actually i lost my notes that i made in your class so can you suggest me from which book i revise this ?
    also sir i have one question why the default value of global variable and external variable is always zero?why not we take any other any value?

Leave a Reply

Your email address will not be published. Required fields are marked *